#include "tracker.h"
#include "page.h"


namespace Dict {

Tracker::Tracker(Connection *d, Page *w, Page *m)
	: dict(d), wordPage(w), matchPage(m)
{
	matchlist = false;
	dictinit = Stopped;
	setDefaultDatabase(0);
	clear();
}

void Tracker::define(const QString &text)
{
	init();
	query = text;
	reset();
	state.defining = true;
	dbcount = database.size();
	emit loading(true);

	if (dbcount)
		foreach (QString dbase, database)
			dict->define(query, dbase);
	else {
		dbcount = 1;
		dict->define(query, defaultDatabase);
	}
	emit progress(0, dbcount);
}

void Tracker::match(const QString &text, const QString &strategy)
{
	init();
	query = text;
	state.matching = true;
	dbcount = database.size();
	QString matchstrat = (!strategy.isEmpty()) ? strategy : this->strategy;
	emit loading(true);

	if (dbcount)
		foreach (QString dbase, database)
			dict->match(query, dbase, matchstrat);
	else {
		dbcount = 1;
		dict->match(query, defaultDatabase, matchstrat);
	}
	emit progress(0, dbcount);
}

void Tracker::info(const QString &text, const QString &info)
{
	init();
	query = text;
	emit loading(true);

	if (info == "status")
		dict->status();
	else
		dict->info(info);
	emit progress(0, 1);
}

void Tracker::cancel()
{
	dict->cancel();
	if (dictinit == Started)
		dictinit = Stopped;
	clear();
}

void Tracker::clear()
{
	reset();
	state.clear();
	emit loading(false);
}

void Tracker::State::clear()
{
	defining = matching = correcting = appending = automatching = false;
}

void Tracker::setDatabase(const QStringList &list)
{
	database = list;
}

void Tracker::setStrategy(const QString &text)
{
	strategy = (!text.isEmpty()) ? text : Connection::DefaultStrategy;
}

void Tracker::setDefaultDatabase(int dbase)
{
	defaultDatabase = (dbase) ? Connection::AllDatabases : Connection::DefaultDatabase;
}

void Tracker::setup()
{
	search.read();
}

void Tracker::init()
{
	if (dictinit & Started)
		return;
	dictinit = Started;
	dict->info("databases");
	dict->info("strategies");
}

bool Tracker::init(Init state)
{
	if (!(dictinit & state)) {
		dictinit |= state;
		return true;
	} else
		return false;
}

void Tracker::done()
{
	if (search.autoMatch && matchPage
		&& !state.automatching && state.defining
		&& (search.filter || !matchlist))
		automatches();
	else
		clear();
	matchlist = false;
}

void Tracker::suggestions()
{
	reset();
	state.correcting = true;
	match(query, Connection::DefaultStrategy);
}

void Tracker::automatches()
{
	reset();
	state.automatch();
	match(query, (search.autoStrategy) ? strategy : Connection::DefaultStrategy);
}

void Tracker::State::automatch()
{
	automatching = true; appending = false; correcting = false;
}

void Tracker::response(const Response &response)
{
	Page *page = wordPage;

	switch (response.type) {
	case Response::Words:
		emit words();
		break;
	case Response::Matches:
		page->correcting(state.correcting);
		if (state.correcting)
			emit corrections();
		else if (matchPage) {
			page = matchPage;
			emit matches(!state.automatching);
		}
		break;
	case Response::NoMatch:
		++misses;
		emit progress(count(), dbcount);
		if (misses < dbcount) {
			if (count() >= dbcount)
				done();
			return;
		} else if (state.automatching) {
		// do nothing
		} else if (!state.correcting) {
			page->nomatches(query, state.defining);
			if (search.corrections) {
				suggestions();
				return;
			}
		} else
			page->corrections(false);
		break;
	case Response::DatabaseList:
		emit databases(((ListResponse &)response).list);
		if (init(Databases))
			return;
		break;
	case Response::StrategyList:
		emit strategies(((ListResponse &)response).list);
		if (init(Strategies))
			return;
		break;
	case Response::ServerInfo:
	case Response::DatabaseInfo:
		break;
	case Response::StatusMessage:
		page->status(dict);
		break;
	case Response::AuthOK:
		restart();
		break;
	default:
		qWarning() << "dict tracker: unhandled type" << response.type;
	}

	page->setTitle(query);
	page->set(response, state.appending);
	state.appending = true;

	if (response.type == Response::Words
	|| response.type == Response::Matches) {
		if (response.done()) ++hits;
		emit progress(count(), dbcount);
		if (count() >= dbcount)
			done();
	} else {
		if (response.type != Response::NoMatch)
			emit progress(1, 1);
		done();
	}
}

}
