#include "browser.h"
#include "page.h"
#include "hiliter.h"
#include "style.h"

#include <QScrollBar>
#include <QPalette>

#include <QStandardPaths>
#include <KLocalizedString>

#include <QFileDialog>
#include <QPrinter>
#include <QPrintDialog>
#include <QPrintPreviewDialog>

#include <QContextMenuEvent>
#include <QMenu>


namespace Dict {

Browser::Browser(QWidget *parent)
	: QTextBrowser(parent), histSize(100), plaintext(false), hiliter(0)
{
	setOpenLinks(false);
	page = new Page();
	style = new Style();

	entry.type = 0;
	connect(this, SIGNAL(highlighted(const QUrl &)),
		SLOT(pointCursor(const QUrl &)));
	connect(this, SIGNAL(cursorPositionChanged()), SLOT(selection()));
	connect(page, SIGNAL(changed(bool)), SLOT(setPage(bool)));
}

Browser::~Browser()
{
	delete page;
	delete style;
}

void Browser::setPage(bool append)
{
	if (!append)
		setPage(page->title(), page->htmltext());
	else
		appendPage(page->title(), page->htmltext());
}

void Browser::moveTo(int steps)
{
	QList<HistoryEntry> &history = (steps > 0)
		? forwardHistory : backwardHistory;

	if (history.isEmpty())
		return;

	QList<HistoryEntry> &other = (steps > 0)
		? backwardHistory : forwardHistory;

	savePosition();
	if (steps < 0)
		steps = -steps;
	while (steps--) {
		other.prepend(entry);
		entry = history.takeFirst();
	}
	displayPage(entry.page);
	restorePosition();

	emit backwardAvailable(!backwardHistory.isEmpty());
	emit forwardAvailable(!forwardHistory.isEmpty());
	emit historyChanged(entry.title);
}

void Browser::savePosition()
{
	entry.hpos = horizontalScrollBar()->value();
	entry.vpos = verticalScrollBar()->value();
}

void Browser::restorePosition()
{
	horizontalScrollBar()->setValue(entry.hpos);
	verticalScrollBar()->setValue(entry.vpos);
}

void Browser::clearHistory()
{
	backwardHistory.clear();
	forwardHistory.clear();
	emit backwardAvailable(false);
	emit forwardAvailable(false);
}

QStringList Browser::historyList(const QList<HistoryEntry> &history, int len) const
{
	QStringList list;
	len = qMin(history.size(), len);
	for (int i = 0; i < len; ++i)
		list << history[i].title;
	return list;
}

void Browser::setPage(const QString &title, const QString &text)
{
	savePosition();
	displayPage(text);

	if ((entry.title != title || entry.type != page->type())
	&& !entry.title.isEmpty()) {
		backwardHistory.prepend(entry);
		forwardHistory.clear();
	}
	if (backwardHistory.size() > histSize)
		backwardHistory.removeLast();

	entry.title = title;
	entry.page = text;
	entry.type = page->type();

	emit backwardAvailable(!backwardHistory.isEmpty());
	emit forwardAvailable(!forwardHistory.isEmpty());
	emit historyChanged(title);
}

void Browser::appendPage(const QString &, const QString &text)
{
	savePosition();
	if (plaintext) {
		moveCursor(QTextCursor::End);
		insertPlainText(text);
	} else append(text);
	restorePosition();
	entry.page += text;
}

void Browser::displayPage(const QString &text)
{
	if (plaintext) setPlainText(text);
	else setHtml(text);
}

void Browser::plainText(bool toggle)
{
	plaintext = toggle;
	displayPage(entry.page);
}

void Browser::reload()
{
	savePosition();
	displayPage(entry.page);
	restorePosition();
}

QString resource(const QString &resfile);

QString Browser::htmlPage() const
{
	QString html = resource("page.html")
		.arg(entry.title)
		.arg(document()->defaultStyleSheet())
		.arg(entry.page);
	return html;
}

void Browser::clearSelection()
{
	QTextCursor cursor = textCursor();
	cursor.clearSelection();
	setTextCursor(cursor);
}

void Browser::busyCursor(bool busy)
{
	viewport()->setCursor((busy) ? Qt::BusyCursor : Qt::ArrowCursor);
	cursor = viewport()->cursor();
}

void Browser::pointCursor(const QUrl &url)
{
	if (url.isEmpty())
		viewport()->setCursor(cursor);
}

void Browser::focusOutEvent(QFocusEvent *event)
{
	QTextEdit::focusOutEvent(event);
}

void Browser::contextMenuEvent(QContextMenuEvent *event)
{
	QMenu *menu;

	if (actions().isEmpty())
		menu = createStandardContextMenu();
	else {
		menu = new QMenu();
		QAction *menubar = actions().first();
		if (!menubar->isChecked()) {
			menu->addAction(menubar);
			menu->addSeparator();
		}
		foreach (QAction *action, actions().mid(1))
			menu->addAction(action);
		if (actions().size() > 3)
			menu->insertSeparator(actions().at(3));
	}

	menu->exec(event->globalPos());
	delete menu;
}

void Browser::selection()
{
	if (!textCursor().hasSelection())
		return;

	QPalette palette;
	QColor text = palette.color(QPalette::HighlightedText);
	QColor back = palette.color(QPalette::Highlight);

	if (textBackgroundColor().value()
		&& textBackgroundColor().value() < textColor().value()) {
		palette.setColor(QPalette::HighlightedText, back);
		palette.setColor(QPalette::Highlight, text);
	} else {
		palette.setColor(QPalette::HighlightedText, text);
		palette.setColor(QPalette::Highlight, back);
	}
	setPalette(palette);
}

void Browser::savePage()
{
	static QString docPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
	QString path = QString("%1/%2.html").arg(docPath, entry.title);
	QString fileName = QFileDialog::getSaveFileName(this, i18n("Save Document"), path);
	if (fileName.isEmpty())
		return;
	docPath = fileName.left(fileName.lastIndexOf('/'));

	QFile file(fileName);
	if (file.open(QFile::WriteOnly | QFile::Text)) {
		QTextStream out(&file);
		out.setCodec("UTF-8");
		out << htmlPage();
		emit highlighted(QUrl(i18n("Document saved")));
	} else {
		page->setTitle(entry.title);
		page->error(i18n("Cannot write file <b>%1</b>: %2").arg(fileName, file.errorString()), Page::FileSystem);
	}
}

void Browser::printPage()
{
	QPrinter printer;
	if (textCursor().hasSelection())
		printer.setPrintRange(QPrinter::Selection);
	QPrintDialog dialog(&printer, this);
	dialog.setWindowTitle(i18n("Print Document"));
	if (dialog.exec() == QDialog::Accepted)
		print(&printer);
}

void Browser::previewPage()
{
	QPrinter printer;
	if (textCursor().hasSelection())
		printer.setPrintRange(QPrinter::Selection);
	QPrintPreviewDialog dialog(&printer, this);
	connect(&dialog, SIGNAL(paintRequested(QPrinter *)),
		SLOT(print(QPrinter *)));
	dialog.exec();
}

Hiliter *Browser::textHiliter()
{
	if (!hiliter)
		hiliter = new Hiliter(this);
	return hiliter;
}

void Browser::setHilite(bool on)
{
	if (on) {
		textHiliter();
		hiliter->setHiliteFormat(style->format(Style::Headword), Hiliter::Query);
		hiliter->setQuery(entry.title);
		connect(this, SIGNAL(historyChanged(const QString &)),
			hiliter, SLOT(setQuery(const QString &)));
	} else if (hiliter) {
		hiliter->setQuery("");
		disconnect(hiliter);
	}
}

void Browser::setup()
{
	style->read();
	document()->setDefaultFont(style->font());
	document()->setDefaultStyleSheet(style->stylesheet());
	reload();
	page->setup();
}

void Browser::reformat()
{
	page->textFormatter()->readConf();
	reload();
}

}
