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

#include "preferences.h"

#include "config.h"
#include "mainwindow.h"
#include "password.h"
#include "progressbar.h"
#include "usystemtray.h"
#include "utils.h"
#include "uwidgets.h"
#include "actions/bootentry.h"

#include <QColorDialog>

// public

// FIXME: an extra "ghost" window without content appears for a fraction of second
// (also happens in kwrite settings window)
Preferences::Preferences(QWidget *parent, const int pageIndex) :
	UDialog(parent, i18n("Preferences"), false) {

	setSizeGripEnabled(true);

	auto progressBar = MainWindow::self()->progressBar();
	m_oldProgressBarVisible = progressBar->isVisible();

	m_pageListWidget = new QListWidget();
	m_pageListWidget->setAlternatingRowColors(true);
	
	#ifdef Q_OS_WIN32
	m_pageListWidget->setIconSize(Utils::getSmallIconSize()); // for right padding only
	#else
	m_pageListWidget->setIconSize(Utils::getLargeIconSize());
	#endif // Q_OS_WIN32
	m_fallbackIcon = UWidgets::emptyIcon(m_pageListWidget->iconSize());
	
	m_pageListWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);

// FIXME: left padding
	m_pageListWidget->setStyleSheet("QListWidget:item { padding: 10px; }");

	connect(m_pageListWidget, &QListWidget::currentRowChanged, [this](int currentRow) {
		setCurrentPage(currentRow);
	});

	addPage(createGeneralPage(), i18n("General"), "configure");
	addPage(createSystemTrayPage(), i18n("System Tray"), "user-desktop");
	
	m_passwordPreferences = new PasswordPreferences(this);
	addPage(m_passwordPreferences, i18n("Password"), "dialog-password");

	auto *rebootPage = createRebootPage();
	#ifdef Q_OS_LINUX
	addPage(rebootPage, RebootAction::self()->originalText(), "system-reboot");
	#else
	Q_UNUSED(rebootPage)
	#endif // Q_OS_LINUX

// TODO: "Tweaks/Advanced" page

// TODO: actions/triggers config.
	//tabs->addTab(createActionsWidget(), i18n("Actions"));
	//tabs->addTab(createTriggersWidget(), i18n("Triggers"));

	m_pageContainerLayout = UWidgets::newVBoxLayout({ }, 0_px);

// TODO: auto *scrollArea = new QScrollArea();

	auto *pageListLayout = UWidgets::newHBoxLayout({ m_pageListWidget }, 0_px);
	pageListLayout->addLayout(m_pageContainerLayout);

	mainLayout()->addLayout(pageListLayout);
 
	m_pageListWidget->setMaximumWidth(m_pageListWidget->sizeHintForColumn(0) + 10_px);
	m_pageListWidget->setFocus();

	if (pageIndex == -1) {
		// show recently used page
		m_pageListWidget->setCurrentRow(
			Config::readInt("Preferences", "Current Page Index", 0, 0, m_pageListWidget->count() - 1)
		);
	}
	else {
		m_pageListWidget->setCurrentRow(pageIndex);
	}

	connect(this, &QDialog::finished, [this](const int code) { onFinish(code); });
}

// private

void Preferences::addHeader(QVBoxLayout *layout, const QString &text) {
	if (! layout->isEmpty())
		layout->addSpacing(30_px);

	layout->addLayout(Utils::newHeader(text, nullptr, 0));
	layout->addSpacing(10_px);
}

void Preferences::addPage(QWidget *widget, const QString &text, const QString &iconName) {
	widget->hide();

	m_pageListData += widget;

	auto *item = new QListWidgetItem(text, m_pageListWidget);
	item->setIcon(QIcon::fromTheme(iconName, m_fallbackIcon));
}

void Preferences::apply() {
	Config::confirmAction.write(m_confirmAction);
	Config::lockScreenBeforeHibernate.write(m_lockScreenBeforeHibernate);

	Config::minimizeToSystemTrayIcon.setBool(!m_noMinimizeToSystemTrayIcon->isChecked());
	Config::minimizeToSystemTrayIcon.write();

	Config::progressBarEnabled.write(m_progressBarEnabled);
	Config::countdownEnabled.write(m_countdownEnabled);

	Config::systemTrayIconActiveColor.setColor(systemTrayIconActiveColor);
	Config::systemTrayIconActiveColor.write();

	Config::systemTrayIconNormalColor.setColor(systemTrayIconNormalColor);
	Config::systemTrayIconNormalColor.write();

	Config::systemTrayIconCustomColor.write(m_systemTrayIconCustomColor);
	Config::systemTrayIconEnabled.write(m_systemTrayIconEnabled);

	Config::systemTrayIconUseTheme.write(m_useThemeIconInSystemTray);

	m_passwordPreferences->apply();

	#ifdef Q_OS_LINUX
	Config::writeString("KShutdown Action lock", "Custom Command", m_lockCommandEdit->command());
	#endif // Q_OS_LINUX

	Config::cancelDefault.write(m_cancelDefault);

	Config::bootEntries.setString(m_bootEntries->toPlainText());
	Config::bootEntries.write();

	#ifdef Q_OS_LINUX
	Config::bootSetEntryCommand.setString(m_setBootEntryCommandEdit->command());
	Config::bootSetEntryCommand.write();
	#endif // Q_OS_LINUX

	Config::bootEntriesVisibleInMenu.write(m_bootEntriesVisibleInMenu);
	Config::bootEntriesVisibleInWindow.write(m_bootEntriesVisibleInWindow);

	//!!!!linux only
	BootEntry::initFromConfig(); // 1.
	RebootAction::self()->updateBootEntryView(); // 2.

	Config::sync();
}

void Preferences::showDialog(const int pageIndex) {
	auto *mainWindow = MainWindow::self();

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

	auto dialog = std::make_unique<Preferences>(mainWindow, pageIndex);
	if (dialog->exec() == Accepted) {
		dialog->apply();

		mainWindow->setProgressWidgetsVisible();

		auto *systemTray = mainWindow->systemTray();
		systemTray->setVisible(Config::systemTrayIconEnabled.getBool());
		systemTray->updateIcon(); // update colors
	}
}

/*
QWidget *Preferences::createActionsWidget() {
	QWidget *w = createContainerWidget();

	return w;
}
*/

QVBoxLayout *Preferences::createGeneralPage() {
	auto *l = newPageLayout();

	addHeader(l, i18n("Confirmation"));

	m_confirmAction = Config::confirmAction.newCheckBox(i18n("Confirm Action"));
	l->addWidget(m_confirmAction);

	m_cancelDefault = Config::cancelDefault.newCheckBox(i18n("Select \"Cancel\" button by default"));
	QString attr = QApplication::isRightToLeft() ? "margin-right" : "margin-left";
	m_cancelDefault->setStyleSheet("QCheckBox { " + attr + ": 20px; }");
	l->addWidget(m_cancelDefault);

	addHeader(l, i18n("User Interface"));

	m_progressBarEnabled = Config::progressBarEnabled.newCheckBox(i18n("Show progress bar on top/bottom of the screen"));
// TODO: connect(m_progressBarEnabled, &QCheckBox::toggled, [this](bool v) { onProgressBarEnabled(v); });

	auto progressBar = MainWindow::self()->progressBar();
	auto *progressBarSettingsButton = new QPushButton(i18n("Settings"));
	progressBarSettingsButton->setToolTip(i18n("Progress Bar"));

	auto *progressBarMenu = new QMenu();
	connect(progressBarMenu, &QMenu::aboutToShow, [progressBar, progressBarMenu] {
		progressBar->createSettingsMenu(progressBarMenu, false);
	});
	progressBarSettingsButton->setMenu(progressBarMenu);

	auto *progressBarLayout = UWidgets::newHBoxLayout({ m_progressBarEnabled, progressBarSettingsButton }, 20_px);
	progressBarLayout->addStretch();
	l->addLayout(progressBarLayout);

// TODO: Countdown settings (color, size, etc.)
	m_countdownEnabled = Config::countdownEnabled.newCheckBox(i18n("Show countdown in the main window"));
	l->addWidget(m_countdownEnabled);

	m_lockScreenBeforeHibernate = Config::lockScreenBeforeHibernate.newCheckBox(i18n("Lock Screen Before Hibernate/Sleep"));

	#ifdef Q_OS_LINUX
	addHeader(l, i18n("Lock Screen"));
	#endif // Q_OS_LINUX

	l->addWidget(m_lockScreenBeforeHibernate);
#if defined(Q_OS_WIN32) || defined(Q_OS_HAIKU)
	m_lockScreenBeforeHibernate->hide();
#endif // Q_OS_WIN32

	#ifdef Q_OS_LINUX
	m_lockCommandEdit = new UCommandEdit();
	m_lockCommandEdit->setCommand(Config::readString("KShutdown Action lock", "Custom Command", ""));
	m_lockCommandEdit->setCompleter({ "xscreensaver-command -lock" });
	m_lockCommandEdit->setLabel(i18n("Custom Lock Screen Command:"));

	l->addSpacing(10_px);
	l->addWidget(m_lockCommandEdit);
	#endif // Q_OS_LINUX

	l->addStretch();

	return l;
}

QVBoxLayout *Preferences::createRebootPage() {
	auto *l = newPageLayout();

	QString menuText = RebootAction::self()->originalText();

	m_bootEntries = Utils::newTextEdit(Config::bootEntries.getString());

	l->addWidget(Utils::newLabel(i18n("Boot Entries:"), m_bootEntries));

	l->addWidget(m_bootEntries);

	l->addWidget(InfoWidget::info(
		i18n("Enter a list of entries that will be visible in the \"%0\" menu.")
			.arg(menuText) + "\n" +
		i18n("Examples:") + "\n" +
		"\n" +
		i18n("%0 - a Windows system").arg("Windows 10 (on /dev/xx)") + "\n" +
		i18n("%0 - a Linux system").arg("Ubuntu") + "\n" +
		i18n("%0 - a second GRUB entry").arg("1") + "\n" +
		i18n("%0 - a memory test").arg("'Memory test (memtest86+)'") + "\n" +
		i18n("%0 - a separator").arg(BootEntry::SEPARATOR) + "\n" +
// TODO: id?
// TODO: parse entire GRUB menuentry line
// TODO: "Insert from grub.cfg" button (?)
		+ "\n" +
		i18n("Hint: Run \"%0\" to list available entries.").arg("sudo grep menuentry /boot/grub/grub.cfg")
	));

	#ifdef Q_OS_LINUX
	for (QString &i : BootEntry::getProblemList()) {
		l->addWidget(InfoWidget::warning(
			i.replace("\n", "<br>")
		));
	}
	#endif // Q_OS_LINUX

	l->addSpacing(10_px);

	m_bootEntriesVisibleInMenu = Config::bootEntriesVisibleInMenu.newCheckBox(i18n("Show \"%0\" Menu").arg(menuText));
	connect(m_bootEntriesVisibleInMenu, &QCheckBox::toggled, [this](bool) { showRestartMessage(); });
	l->addWidget(m_bootEntriesVisibleInMenu);

	m_bootEntriesVisibleInWindow = Config::bootEntriesVisibleInWindow.newCheckBox(i18n("Show in Main Window"));
	l->addWidget(m_bootEntriesVisibleInWindow);

	#ifdef Q_OS_LINUX
	QString grubExample = "grub-reboot \"%entry\"";

	m_setBootEntryCommandEdit = new UCommandEdit();
	m_setBootEntryCommandEdit->setCommand(Config::bootSetEntryCommand.getString());
	m_setBootEntryCommandEdit->setCompleter({ grubExample, "pkexec " + grubExample });
	m_setBootEntryCommandEdit->setExample(grubExample);
	m_setBootEntryCommandEdit->setLabel(i18n("Custom Set Boot Entry Command:"));

	l->addSpacing(10_px);
	l->addWidget(m_setBootEntryCommandEdit);
	#endif // Q_OS_LINUX

	return l;
}

QVBoxLayout *Preferences::createSystemTrayPage() {
	auto *l = newPageLayout();

	#ifdef Q_OS_LINUX
// TODO: show info if reported as not supported
	l->addWidget(InfoWidget::warning(
		i18n("May not work correctly with some Desktop Environments")
	));
	l->addSpacing(10_px);
	#endif // Q_OS_LINUX

	m_systemTrayIconEnabled = Config::systemTrayIconEnabled.newCheckBox(i18n("Enable System Tray Icon"));

	m_noMinimizeToSystemTrayIcon = new QCheckBox(i18n("Quit instead of minimizing to System Tray Icon"));
	m_noMinimizeToSystemTrayIcon->setChecked(!Config::minimizeToSystemTrayIcon);

	m_noMinimizeToSystemTrayIcon->setEnabled(m_systemTrayIconEnabled->isChecked());
	connect(m_systemTrayIconEnabled, SIGNAL(toggled(bool)), m_noMinimizeToSystemTrayIcon, SLOT(setEnabled(bool)));

	m_useThemeIconInSystemTray = Config::systemTrayIconUseTheme.newCheckBox(i18n("Use System Icon Theme"));

	m_systemTrayIconCustomColor = Config::systemTrayIconCustomColor.newCheckBox(i18n("Use Custom Colors:"));
	connect(m_systemTrayIconCustomColor, &QCheckBox::toggled, [this]([[maybe_unused]] const bool selected) {
		updateSystemTrayIconPreview();
	});

// TODO: keep value in systemTrayIconActiveColor; and revert it on Cancel (?)
	systemTrayIconActiveColor = Config::systemTrayIconActiveColor.getColor();
	systemTrayIconNormalColor = Config::systemTrayIconNormalColor.getColor();

	m_systemTrayIconNormalColorButton = new QPushButton(i18n("Normal Color..."));
	connect(m_systemTrayIconNormalColorButton, &QPushButton::clicked, [this]() {
		QColor newColor = QColorDialog::getColor(systemTrayIconNormalColor, this);
		if (newColor.isValid()) {
			systemTrayIconNormalColor = newColor;
			updateSystemTrayIconPreview();
		}
	});

	m_systemTrayIconActiveColorButton = new QPushButton(i18n("Active Color..."));
	connect(m_systemTrayIconActiveColorButton, &QPushButton::clicked, [this]() {
		QColor newColor = QColorDialog::getColor(systemTrayIconActiveColor, this);
		if (newColor.isValid()) {
			systemTrayIconActiveColor = newColor;
			updateSystemTrayIconPreview();
		}
	});

	auto *colorLayout = UWidgets::newHBoxLayout(
		{ m_systemTrayIconNormalColorButton, m_systemTrayIconActiveColorButton },
		10_px
	);
	colorLayout->addStretch();

	#ifdef KS_KF5
// TODO: redesign this option
	m_useThemeIconInSystemTray->hide();
	m_systemTrayIconCustomColor->hide();
	m_systemTrayIconNormalColorButton->hide();
	m_systemTrayIconActiveColorButton->hide();
	#endif

	#ifdef Q_OS_WIN32
	m_useThemeIconInSystemTray->hide();
	#endif // Q_OS_WIN32

	l->addWidget(m_systemTrayIconEnabled);
	l->addWidget(m_noMinimizeToSystemTrayIcon);

	#ifdef KS_PURE_QT
	addHeader(l, i18n("Color"));
	#endif // KS_PURE_QT

	l->addWidget(m_useThemeIconInSystemTray);
	
	if (m_useThemeIconInSystemTray->isVisible())
		l->addSpacing(10_px);
	
	l->addWidget(m_systemTrayIconCustomColor);
	l->addLayout(colorLayout);
	l->addStretch();

	updateSystemTrayIconPreview();

	return l;
}

/*
QWidget *Preferences::createTriggersWidget() {
	QWidget *w = createContainerWidget();

	return w;
}
*/

QVBoxLayout *Preferences::newPageLayout() {
	auto *w = new QWidget();

	return UWidgets::newVBoxLayout(w, { }, UWidgets::STYLE_SPACING, PAGE_MARGIN);
}

void Preferences::setCurrentPage(const int index) {
	QWidget *old = m_currentPageWidget;
	m_currentPageWidget = m_pageListData[index];

	if (m_currentPageWidget == old)
		return;

	if (old != nullptr) {
		old->hide();

		m_pageContainerLayout->replaceWidget(old, m_currentPageWidget);
	}
	else {
		m_pageContainerLayout->addWidget(m_currentPageWidget);
	}

	m_currentPageWidget->show();
}

void Preferences::showRestartMessage() {
	infoWidget()->setText(
		i18n("Restart application to apply changes"),
		InfoWidget::Type::Warning
	);
}

void Preferences::updateSystemTrayIconPreview() {
	QIcon baseIcon = QApplication::windowIcon();
	QSize iconSize(24_px, 24_px);

	m_systemTrayIconNormalColorButton->setEnabled(m_systemTrayIconCustomColor->isChecked());
	m_systemTrayIconNormalColorButton->setIconSize(iconSize);
	m_systemTrayIconNormalColorButton->setIcon(USystemTray::makeIcon(
		baseIcon, iconSize.height(),
		true, false,
		systemTrayIconNormalColor, systemTrayIconActiveColor
	));

	m_systemTrayIconActiveColorButton->setEnabled(m_systemTrayIconCustomColor->isChecked());
	m_systemTrayIconActiveColorButton->setIconSize(iconSize);
	m_systemTrayIconActiveColorButton->setIcon(USystemTray::makeIcon(
		baseIcon, iconSize.height(),
		true, true,
		systemTrayIconNormalColor, systemTrayIconActiveColor
	));
}

// event handlers:

void Preferences::onFinish([[maybe_unused]] int result) {
/*
	ProgressBar *progressBar = MainWindow::self()->progressBar();
	progressBar->setDemo(false);
	progressBar->setVisible(m_oldProgressBarVisible);
*/

	// save recently used page
	Config::writeInt("Preferences", "Current Page Index", m_pageListWidget->currentRow());
}

void Preferences::onProgressBarEnabled(bool enabled) const {
	auto progressBar = MainWindow::self()->progressBar();
	progressBar->setDemo(enabled);
	progressBar->setVisible(enabled || m_oldProgressBarVisible);
}
