// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2012 Konrad Twardowski

#include "bookmarks.h"

#include "config.h"
#include "mainwindow.h"
#include "password.h"
#include "plugins.h"
#include "utils.h"

#include <QFormLayout>
#include <QLineEdit>
#include <QPushButton>

// public:

BookmarkAction::BookmarkAction(
	const QString &text,
	const QString &actionID, const QString &triggerID,
	const QString &actionOption, const QString &triggerOption
)
	: QAction(nullptr), // no owner, because clear() will delete action

	m_actionID(actionID),
	m_actionOption(actionOption),
	m_triggerID(triggerID),
	m_triggerOption(triggerOption)
{
	connect(this, &QAction::triggered, [this] { onAction(); });
	
	auto *action = PluginManager::action(actionID);
	auto *trigger = PluginManager::trigger(triggerID);
	
	if (action != nullptr)
		setIcon(action->icon());
	setIconVisibleInMenu(true);

// TODO: better trigger text
	QString actionText = BookmarksMenu::makeText(action, trigger, actionOption, triggerOption);
	m_userText = !text.isEmpty() && (text != actionText);
	m_originalText = m_userText ? text : actionText;

	setText(m_originalText);
	setToolTip(actionText);
}

// private slots:

void BookmarkAction::onAction() {
	auto *mainWindow = MainWindow::self();
	mainWindow->setSelectedAction(m_actionID);
	mainWindow->setSelectedTrigger(m_triggerID);
	
	auto *action = mainWindow->getSelectedAction();
	auto *trigger = mainWindow->getSelectedTrigger();
	
	if ((action->id() != m_actionID) || (trigger->id() != m_triggerID))
		return;

	action->setStringOption(m_actionOption);
	trigger->setStringOption(m_triggerOption);
	
	if (!m_confirmAction)
		mainWindow->setActive(true);
}

// public:

BookmarksMenu::BookmarksMenu(QWidget *parent)
	: QMenu(i18n("&Bookmarks"), parent)
{
	QString group = "Bookmarks";
	int count = Config::readInt(group, "Count", 0, 0, 1024);
	for (int i = 0; i < count; i++) {
		QString index = QString::number(i);
		auto *bookmarkAction = new BookmarkAction(
			Config::readString(group, "Text " + index, ""),
			Config::readString(group, "Action " + index, ""),
			Config::readString(group, "Trigger " + index, ""),
			Config::readString(group, "Action Option " + index, ""),
			Config::readString(group, "Trigger Option " + index, "")
		);
		bookmarkAction->m_confirmAction = Config::readBool(group, "Confirm Action " + index, true);
		m_list.append(bookmarkAction);
	}
	sortList();

	m_addAction = new QAction(/*this*/);
	m_addAction->setIcon(QIcon::fromTheme("bookmark-new"));
	m_addAction->setShortcut(QKeySequence("Ctrl+D"));
	connect(m_addAction, &QAction::triggered, [this] { onAddBookmark(); });

	connect(this, &QMenu::aboutToShow, [this] { onUpdateMenu(); });

	Utils::showToolTips(this);

	addAction(m_addAction); // for keyboard shortcut
}

QString BookmarksMenu::makeText(Action *action, Trigger *trigger, const QString &actionOption, const QString &triggerOption) {
	QStringList result;

	if (action != nullptr) {
		result += action->originalText();

		QString option = actionOption.isEmpty() ? action->getStringOption() : actionOption;

		if (!option.isEmpty())
			result += Utils::trim(option, 30);
	}
	else {
		result += "?";
	}

	if (trigger != nullptr) {
		result += trigger->text();

		QString option = triggerOption.isEmpty() ? trigger->getStringOption() : triggerOption;

		if (!option.isEmpty())
			result += Utils::trim(option, 30);
	}
	else {
		result += "?";
	}

	return result.join(Utils::TITLE_SEPARATOR);
}

// private:

void BookmarksMenu::sortList() {
	// sort alphabetically, by original action text
	std::sort(
		m_list.begin(), m_list.end(),
		[](const BookmarkAction *a1, const BookmarkAction *a2) {
			return a1->originalText().localeAwareCompare(a2->originalText()) < 0;
		}
	);
}

void BookmarksMenu::syncConfig() {
	QString group = "Bookmarks";

	Config::removeAllKeys(group);

	Config::writeInt(group, "Count", m_list.count());

	int i = 0;
	for (const auto *bookmark : m_list) {
		QString index = QString::number(i);
		Config::writeString(group, "Text " + index,           bookmark->m_userText ? bookmark->originalText() : "");
		Config::writeString(group, "Action " + index,         bookmark->m_actionID);
		Config::writeString(group, "Action Option " + index,  bookmark->m_actionOption);
		Config::writeString(group, "Trigger " + index,        bookmark->m_triggerID);
		Config::writeString(group, "Trigger Option " + index, bookmark->m_triggerOption);
		Config::writeBool  (group, "Confirm Action " + index, bookmark->m_confirmAction);
		i++;
	}

	Config::sync();
}

// event handlers:

void BookmarksMenu::onAddBookmark() {
	auto *mainWindow = MainWindow::self();
	auto *action = mainWindow->getSelectedAction();
	auto *trigger = mainWindow->getSelectedTrigger();

	if (
		! action->canBookmark() ||
		! trigger->canBookmark() ||
		! PasswordDialog::authorizeSettings(mainWindow)
	) {
		return;
	}

	auto dialog = std::make_unique<UDialog>(mainWindow, i18n("Add Bookmark"), false);
	dialog->setSizeGripEnabled(true);
	dialog->acceptButton()->setText(i18n("Add"));

	auto *nameField = new QLineEdit(makeText(action, trigger, QString(), QString()));
	nameField->setClearButtonEnabled(true);

	auto *confirmActionField = new QCheckBox(i18n("Confirm Action"));
	confirmActionField->setChecked(true);

	auto *layout = Utils::newFormLayout();
	layout->addRow(i18n("Name:"), nameField);
	layout->addRow(confirmActionField);
	dialog->mainLayout()->addLayout(layout);
	Utils::setMargin(dialog->mainLayout(), 20_px);

	nameField->setFocus();
	nameField->selectAll();

	if (dialog->exec() == UDialog::Accepted) {
		auto *bookmark = new BookmarkAction(
			nameField->text().trimmed(),
			action->id(),
			trigger->id(),
			action->getStringOption(),
			trigger->getStringOption()
		);
		bookmark->m_confirmAction = confirmActionField->isChecked();

		m_list.append(bookmark);
		sortList();

		syncConfig();
	}
}

void BookmarksMenu::onRemoveBookmark(BookmarkAction *bookmark) {
	auto *mainWindow = MainWindow::self();

	if (! PasswordDialog::authorizeSettings(mainWindow))
		return;

	UMessageBuilder message(UMessageBuilder::Type::Question);
	message.okText(i18n("Remove"));
	message.showKey("KShutdown Remove Bookmark");
	message.plainText(i18n("Are you sure?"));

	if (message.exec(mainWindow)) {
		m_list.removeOne(bookmark);
		syncConfig();
	}
}

void BookmarksMenu::onUpdateMenu() {
	auto *mainWindow = MainWindow::self();
	auto *action = mainWindow->getSelectedAction();
	auto *trigger = mainWindow->getSelectedTrigger();

	m_addAction->setEnabled(action->canBookmark() && trigger->canBookmark());

	QString text = makeText(action, trigger, QString(), QString());
	m_addAction->setText(i18n("Add: %0").arg(text));

	clear();

	addAction(m_addAction);

	addSeparator();

	auto *removeMenu = new QMenu(i18n("Remove Bookmark"), this);
	removeMenu->setIcon(QIcon::fromTheme("edit-delete"));
	addMenu(removeMenu);

	addSeparator();

	for (auto *bookmark : m_list) {
		addAction(bookmark);

		auto *removeAction = new QAction(bookmark->icon(), bookmark->originalText());
		connect(removeAction, &QAction::triggered, [this, bookmark]() {
			onRemoveBookmark(bookmark);
		});
		removeMenu->addAction(removeAction);
	}

	removeMenu->setEnabled(!m_list.isEmpty());
}
