#include "page.h"

#include <KLocalizedString>
#include <QStandardPaths>
#include <QFile>
#include <QRegExp>


namespace Dict {

QString escape(const QString &text);

Page::Page() : correct(false)
{}

Page::Page(const Response &response) : Page()
{
	set(response);
}

void Page::setup(void)
{
	search.read();
	browse.read();
}

void Page::set(const Response &response, bool append)
{
	switch (response.type) {
	case Response::Words: {
		DefineResponse &define = (DefineResponse &)response;
		html = (browse.reformat && !query.startsWith("00"))
			? formatter.format(define)
			: formatter.words(define.list);
		if (browse.headings) heading(define.description);
		break; }
	case Response::Matches: {
		MatchResponse &match = (MatchResponse &)response;
		if (correct) {
			if (!append) corrections(true);
			suggestions(match.database, match.list);
			append = true;
		} else {
			matches(match.list);
			if (browse.headings) heading(match.database);
		}
		break; }
	case Response::NoMatch:
		return;
		break;
	case Response::DatabaseList:
		databases(((ListResponse &)response).list);
		break;
	case Response::StrategyList:
		strategies(((ListResponse &)response).list);
		break;
	case Response::DatabaseInfo:
		html = formatter.words(response.text);
		break;
	case Response::ServerInfo:
		server(response.text);
		break;
	case Response::StatusMessage:
		status(response.message);
		break;
	default:
		html = response.text;
	}
	typeCode = response.type;
	emit changed(append);
}

void Page::nomatches(const QString &text, bool define)
{
	query = text;
	html = "<div class=\"message\">" + ((define)
		? i18n("No definitions found for <span class=\"hword\">%1</span>.", escape(text))
		: i18n("No matches found for <span class=\"hword\">%1</span>.", escape(text)))
		+ "</div>";
	typeCode = Response::NoMatch;
	emit changed();
}

void Page::corrections(bool found)
{
	html = "<p>" + ((found) ? i18n("Suggestions:") : i18n("No suggestions."));
	emit changed(true);
}

void Page::heading(const QString &text)
{
	html.prepend("<div class=\"title\">" + escape(text) + "</div>");
}

void Page::server(const QString &text)
{
	html = "<div class=\"info\">";
	html += escape(text);
	html += "</div>";
}

void Page::title(const QString &title)
{
	html += "<div class=\"title\">" + escape(title) + "</div>";
}

void Page::status(const Connection *dict)
{
	html.clear();
	title(i18n("Server"));
	html += "<table>";
	status(i18n("Host"), dict->host());
	status(i18n("Port"), QString::number(dict->port()));
	status(i18n("Identification"), dict->ident());
	status(i18n("Capabilities"), dict->capabilities().join(", "));
	html += "</table>";

	title(i18n("Client"));
	html += "<table>";
	status(i18n("User Agent"), dict->client());
	if (!dict->user().isEmpty())
		status(i18n("User"), dict->user());
	html += "</table>";
}

static const QString format = "<tr><td class=\"name\">%1:<td>%2";

void Page::status(const QString &name, const QString &desc)
{
	html += format.arg(name, escape(desc));
}

void Page::status(const QString &message)
{
	title(i18n("Connection"));
	QRegExp rx("d/m/c = (\\d+)/(\\d+)/(\\d+)");
	if (rx.indexIn(message) > 0) {
		QStringList list = rx.capturedTexts();
		QStringList desc;
		desc << i18n("Defines") << i18n("Matches") << i18n("Comparisons");
		html += "<table>";
		for (int i = 0; i < 3; ++i)
			html += format.arg(desc[i], list[i+1]);
		html += "</table>";
	} else
		html += "<div>" + escape(message) + "</div>";
}

inline int dictmin(int max, int len)
{
	return (max && max < len) ? max : len;
}

void Page::matches(const QStringList &list)
{
	html = "<div class=\"match\">";
	int count = dictmin(search.maxMatches, list.size());
	for (int i = 0; i < count; ++i)
		html += QString("<a href=\"%1\">%1</a>\n").arg(escape(list[i]));
	html += "</div>";
}

void Page::suggestions(const QString &database, const QStringList &list)
{
	html = "<div class=\"list\">";
	if (browse.headings) html += database + ": ";
	html += "<span class=\"suggest\">";
	int count = dictmin(search.maxSuggestions, list.size());
	for (int i = 0; i < count; ++i)
		html += QString("<a href=\"%1\">%1</a>, ").arg(escape(list[i]));
	html.chop(2);
	html += "</span></div>";
}

void Page::databases(const List &list)
{
	html = "<h4>"+ i18n("Dictionary Databases") + "</h4>";
	html += "<table>";
	foreach (Pair pair, list)
		html += QString("<tr><td class=\"name\"><a href=\"about:%1\">%1</a><td>%2")
				.arg(escape(pair.first), escape(pair.second));
	html += "</table>";
}

void Page::strategies(const List &list)
{
	html = "<h4>" + i18n("Matching Strategies") + "</h4>";
	html += "<table>";
	foreach (Pair pair, list)
		html += QString("<tr><td class=\"name\">%1<td>%2")
				.arg(escape(pair.first), escape(pair.second));
	html += "</table>";
}

QString resourcePath(const QString &resfile)
{
	if (resfile.startsWith(":")) return resfile;
	QString path = QStandardPaths::locate(QStandardPaths::DataLocation, resfile);
	if (path.isEmpty()) path = ":" + resfile;
	return path;
}

QString resource(const QString &resfile)
{
	QFile file(resourcePath(resfile));
	bool open = file.open(QFile::ReadOnly);
	Q_ASSERT(open); Q_UNUSED(open);
	return QString(file.readAll());
}

void Page::about(const QString &text, const QString &version)
{
	setTitle(text);
	html = resource("intro.html").arg(version);
	typeCode = 0;
	emit changed();
}

void Page::help(const QString &text)
{
	setTitle(text);
	html = resource("help.html");
	typeCode = 0;
	emit changed();
}

void Page::error(const QString &text, int code)
{
	QString title, desc;
	QString network = i18n("If you are unable to connect to any host, check your computer's network connection.");
	QString firewall = i18n("If your computer or network is protected by a firewall, check that you can access the network.");
	QString unavail = i18n("The server could be temporarily unavailable or too busy. Try again in a few moments.");
	QString typing = i18n("Check the address for typing errors.");
	QString web = i18n("If there is a web server on the same address, it may provide information on how to obtain access.");

	switch (code) {
	case Connection::HostNotFound:
		title = i18n("Unable to find the host.");
		desc = "<li>" + i18n("The host with that name may not exist.");
		desc += " " + typing + "<li>" + network + "<li>" + firewall;
		break;
	case Connection::ConnectionRefused:
		title = i18n("Unable to connect to the server.");
		desc = "<li>" + i18n("The host may not run a dict server.");
		desc += " " + typing + "<li>" + unavail;
		break;
	case Connection::ConnectTimeout:
		title = i18n("Unable to connect to the server.");
		desc = "<li>" + unavail;
		desc += "<li>" + network + "<li>" + firewall;
		break;
	case Connection::ReadTimeout:
		title = i18n("Unable to read from the server");
		desc = "<li>" + unavail;
		desc += "<li>" + i18n("If you receive frequent timeouts, the connection is too slow. Try a different dict server.");
		break;
	case MalformedURL:
		desc = "<li>" + typing;
		break;
	case UnsupportedScheme:
		title = i18n("The protocol is not supported.");
		desc = "<li>" + i18n("Supported protocols are <b>dict</b> and <b>http</b>.");
		break;
	case FileSystem:
		title = i18n("Unable to save the document.");
		desc = "<li>" + i18n("Check that you have the permission to write the file.");
		break;
	case Response::ServerUnavailable:
		desc = "<li>" + i18n("The server is temporarily unavailable or too busy. Try again in a few moments.");
		break;
	case Response::ServerShutdown:
		desc = "<li>" + i18n("The server is shutting down and it is unable to serve any more requests.");
		break;
	case Response::AccessDenied:
		title = i18n("Your computer is not allowed to connect.");
		desc = "<li>" + web;
		break;
	case Response::AuthDenied:
		desc = "<li>" + i18n("See <a href=\"about:server\">Server Info</a> page for information about the server.");
		desc += "<li>" + web;
		break;
	case UnknownDatabase:
	case Response::InvalidDatabase:
		desc = "<li>" + i18n("See <a href=\"about:databases\">Database Info</a> page for a list of available databases.");
		break;
	case Response::InvalidStrategy:
		desc = "<li>" + i18n("See <a href=\"about:strategies\">Strategy Info</a> page for a list of available strategies.");
		break;
	case Response::UnrecognizedCommand:
	case Response::IllegalParameter:
	case Response::UnimplementedCommand:
	case Response::UnimplementedParameter:
		desc = "<li>" + i18n("The server has encountered an error and it is unable to process your request.");
		break;
	default:
		qWarning() << "dict page: unhandled error" << code;
	}
	html = resource("error.html").arg(text, title, desc);
	typeCode = code;
	emit changed();
}

void Page::warning(const QString &text, Warning type)
{
	QString title, desc;

	switch (type) {
	case LongSelection:
		title = i18n("Long Selection");
		desc = i18n("Selected text is longer than configured limit.");
		break;
	case UrlSelection:
		title = i18n("Url Selected");
		desc = i18n("Selected text is a Url.");
		break;
	case UrlRequested:
		title = i18n("Url Requested");
		desc = i18n("Requested text is a Url.");
		break;
	}
	html = resource("warning.html").arg(title, text, desc);
	typeCode = type;
	emit changed();
}

}
