/**************************************************************************
* Otter Browser: Web browser controlled by the user, not vice-versa.
* Copyright (C) 2017 - 2018 Michal Dutkiewicz aka Emdek <michal@emdek.pl>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
**************************************************************************/

#include "TextEditWidget.h"
#include "Action.h"
#include "MainWindow.h"
#include "Menu.h"
#include "../core/NotesManager.h"
#include "../core/SpellCheckManager.h"
#ifdef OTTER_ENABLE_SPELLCHECK
#include "../../3rdparty/sonnet/src/ui/highlighter.h"
#endif

#include <QtGui/QClipboard>
#include <QtGui/QGuiApplication>

namespace Otter
{

TextEditWidget::TextEditWidget(const QString &text, QWidget *parent) : QPlainTextEdit(text, parent), ActionExecutor(),
	m_highlighter(nullptr),
	m_hadSelection(false),
	m_wasEmpty(text.isEmpty())
{
	initialize();
}

TextEditWidget::TextEditWidget(QWidget *parent) : QPlainTextEdit(parent),
	m_highlighter(nullptr),
	m_hadSelection(false),
	m_wasEmpty(true)
{
	initialize();
}

void TextEditWidget::initialize()
{
#ifdef OTTER_ENABLE_SPELLCHECK
	m_highlighter = new Sonnet::Highlighter(this);
	m_highlighter->setCurrentLanguage(SpellCheckManager::getDefaultDictionary());
#endif

	connect(this, &TextEditWidget::selectionChanged, this, &TextEditWidget::handleSelectionChanged);
	connect(this, &TextEditWidget::textChanged, this, &TextEditWidget::handleTextChanged);
	connect(QGuiApplication::clipboard(), &QClipboard::dataChanged, this, &TextEditWidget::notifyPasteActionStateChanged);
}

void TextEditWidget::focusInEvent(QFocusEvent *event)
{
	MainWindow *mainWindow(MainWindow::findMainWindow(this));

	if (mainWindow)
	{
		mainWindow->setActiveEditorExecutor(ActionExecutor::Object(this, this));
	}

	QPlainTextEdit::focusInEvent(event);
}

void TextEditWidget::contextMenuEvent(QContextMenuEvent *event)
{
	ActionExecutor::Object executor(this, this);
	QMenu menu(this);
	menu.addAction(new Action(ActionsManager::UndoAction, {}, executor, &menu));
	menu.addAction(new Action(ActionsManager::RedoAction, {}, executor, &menu));
	menu.addSeparator();
	menu.addAction(new Action(ActionsManager::CutAction, {}, executor, &menu));
	menu.addAction(new Action(ActionsManager::CopyAction, {}, executor, &menu));
	menu.addAction(new Action(ActionsManager::PasteAction, {}, executor, &menu));
	menu.addAction(new Action(ActionsManager::DeleteAction, {}, executor, &menu));
	menu.addSeparator();
	menu.addAction(new Action(ActionsManager::CopyToNoteAction, {}, executor, &menu));
	menu.addSeparator();
	menu.addAction(new Action(ActionsManager::ClearAllAction, {}, executor, &menu));
	menu.addAction(new Action(ActionsManager::SelectAllAction, {}, executor, &menu));
	menu.addSeparator();
	menu.addAction(new Action(ActionsManager::CheckSpellingAction, {}, executor, &menu));

	Menu *dictionariesMenu(new Menu(Menu::DictionariesMenuRole, &menu));
	dictionariesMenu->setExecutor(executor);

	menu.addMenu(dictionariesMenu);
	menu.exec(event->globalPos());
}

void TextEditWidget::triggerAction(int identifier, const QVariantMap &parameters)
{
	switch (identifier)
	{
		case ActionsManager::UndoAction:
			if (!isReadOnly())
			{
				undo();
			}

			break;
		case ActionsManager::RedoAction:
			if (!isReadOnly())
			{
				redo();
			}

			break;
		case ActionsManager::CutAction:
			if (!isReadOnly())
			{
				cut();
			}

			break;
		case ActionsManager::CopyAction:
			copy();

			break;
		case ActionsManager::CopyToNoteAction:
			{
				const QString text(hasSelection() ? textCursor().selectedText() : toPlainText());

				if (!text.isEmpty())
				{
					NotesManager::addNote(BookmarksModel::UrlBookmark, {{BookmarksModel::DescriptionRole, text}});
				}
			}

			break;
		case ActionsManager::PasteAction:
			if (!isReadOnly())
			{
				if (parameters.contains(QLatin1String("note")))
				{
					const BookmarksItem *bookmark(NotesManager::getModel()->getBookmark(parameters[QLatin1String("note")].toULongLong()));

					if (bookmark)
					{
						insertPlainText(bookmark->getDescription());
					}
				}
				else if (parameters.contains(QLatin1String("text")))
				{
					insertPlainText(parameters[QLatin1String("text")].toString());
				}
				else
				{
					paste();
				}
			}

			break;
		case ActionsManager::DeleteAction:
			if (!isReadOnly())
			{
				textCursor().removeSelectedText();
			}

			break;
		case ActionsManager::SelectAllAction:
			selectAll();

			break;
		case ActionsManager::UnselectAction:
			textCursor().clearSelection();

			break;
		case ActionsManager::ClearAllAction:
			if (!isReadOnly())
			{
				selectAll();
				textCursor().removeSelectedText();
			}

			break;
		case ActionsManager::CheckSpellingAction:
#ifdef OTTER_ENABLE_SPELLCHECK
			if (parameters.contains(QLatin1String("dictionary")))
			{
				const QString dictionary(parameters[QLatin1String("dictionary")].toString());

				SettingsManager::setOption(SettingsManager::Browser_SpellCheckDictionaryOption, dictionary);

				m_highlighter->setCurrentLanguage(dictionary);
				m_highlighter->setActive(true);
				m_highlighter->slotRehighlight();
			}
			else
			{
				m_highlighter->setActive(parameters.value(QLatin1String("isChecked"), !m_highlighter->isActive()).toBool());
			}
#endif
			break;
		default:
			break;
	}
}

void TextEditWidget::handleSelectionChanged()
{
	if (hasSelection() != m_hadSelection)
	{
		m_hadSelection = hasSelection();

		emit arbitraryActionsStateChanged({ActionsManager::CutAction, ActionsManager::CopyAction, ActionsManager::CopyToNoteAction, ActionsManager::PasteAction, ActionsManager::DeleteAction, ActionsManager::UnselectAction});
	}
}

void TextEditWidget::handleTextChanged()
{
	if (document() && document()->isEmpty() != m_wasEmpty)
	{
		m_wasEmpty = document()->isEmpty();

		emit arbitraryActionsStateChanged({ActionsManager::UndoAction, ActionsManager::RedoAction, ActionsManager::SelectAllAction, ActionsManager::ClearAllAction});
	}
}

void TextEditWidget::notifyPasteActionStateChanged()
{
	emit arbitraryActionsStateChanged({ActionsManager::PasteAction});
}

ActionsManager::ActionDefinition::State TextEditWidget::getActionState(int identifier, const QVariantMap &parameters) const
{
	const ActionsManager::ActionDefinition definition(ActionsManager::getActionDefinition(identifier));
	ActionsManager::ActionDefinition::State state(definition.getDefaultState());
	state.isEnabled = false;

	if (definition.scope == ActionsManager::ActionDefinition::EditorScope)
	{
		switch (definition.identifier)
		{
			case ActionsManager::UndoAction:
				state.isEnabled = (!isReadOnly() && document() && document()->isUndoAvailable());

				break;
			case ActionsManager::RedoAction:
				state.isEnabled = (!isReadOnly() && document() && document()->isRedoAvailable());

				break;
			case ActionsManager::CutAction:
				state.isEnabled = (!isReadOnly() && hasSelection());

				break;
			case ActionsManager::CopyAction:
				state.isEnabled = hasSelection();

				break;
			case ActionsManager::CopyToNoteAction:
				state.isEnabled = hasSelection();

				break;
			case ActionsManager::PasteAction:
				state.isEnabled = (!isReadOnly() && (parameters.contains(QLatin1String("note")) || parameters.contains(QLatin1String("text")) || canPaste()));

				break;
			case ActionsManager::DeleteAction:
				state.isEnabled = (!isReadOnly() && hasSelection());

				break;
			case ActionsManager::SelectAllAction:
				state.isEnabled = (document() && !document()->isEmpty());

				break;
			case ActionsManager::UnselectAction:
				state.isEnabled = hasSelection();

				break;
			case ActionsManager::ClearAllAction:
				state.isEnabled = (!isReadOnly() && document() && !document()->isEmpty());

				break;
			case ActionsManager::CheckSpellingAction:
#ifdef OTTER_ENABLE_SPELLCHECK
				{
					state.isEnabled = true;

					if (parameters.contains(QLatin1String("dictionary")))
					{
						const QString dictionary(parameters[QLatin1String("dictionary")].toString());
						const QVector<SpellCheckManager::DictionaryInformation> dictionaries(SpellCheckManager::getDictionaries());

						state.text = dictionary;
						state.isChecked = (dictionary == (SettingsManager::getOption(SettingsManager::Browser_SpellCheckDictionaryOption).isNull() ? SpellCheckManager::getDefaultDictionary() : SettingsManager::getOption(SettingsManager::Browser_SpellCheckDictionaryOption).toString()));

						for (int i = 0; i < dictionaries.count(); ++i)
						{
							if (dictionaries.at(i).name == dictionary)
							{
								state.text = dictionaries.at(i).title;

								break;
							}
						}
					}
					else
					{
						state.isChecked = m_highlighter->isActive();
					}
				}
#else
				state.isEnabled = false;
#endif
				break;
			default:
				break;
		}
	}

	return state;
}

bool TextEditWidget::hasSelection() const
{
	return textCursor().hasSelection();
}

}
