/* SPDX-License-Identifier: GPL-3.0-or-later
 * SPDX-FileCopyrightText: 2024-2025 Soren Stoutner <soren@stoutner.com>
 *
 * This file is part of Privacy Browser PC <https://www.stoutner.com/privacy-browser-pc/>.
 *
 * 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 <https://www.gnu.org/licenses/>.
 */

// Application headers.
#include "Settings.h"
#include "SettingsDialog.h"
#include "helpers/SearchEngineHelper.h"
#include "helpers/UserAgentHelper.h"
#include "ui_SettingsGeneral.h"
#include "ui_SettingsPrivacy.h"
#include "ui_SettingsSpellCheck.h"

// Qt framework headers.
#include <QDir>
#include <QFileDialog>

SettingsDialog::SettingsDialog(QWidget *parentWidgetPointer, KCoreConfigSkeleton *coreConfigSkeletonPointer) :
                               KConfigDialog(parentWidgetPointer, QLatin1String("settings"), coreConfigSkeletonPointer)
{
    // Set the minimum size of the dialog.
    setMinimumSize(600, 600);

    // Instantiate the settings UI.
    Ui::PrivacySettings privacySettingsUi;
    Ui::GeneralSettings generalSettingsUi;
    Ui::SpellCheckSettings spellCheckSettingsUi;

    // Create the settings widgets.
    QWidget *privacySettingsWidgetPointer = new QWidget;
    QWidget *generalSettingsWidgetPointer = new QWidget;
    QWidget *spellCheckSettingsWidgetPointer = new QWidget;

    // Setup the UI to display the settings widgets.
    privacySettingsUi.setupUi(privacySettingsWidgetPointer);
    generalSettingsUi.setupUi(generalSettingsWidgetPointer);
    spellCheckSettingsUi.setupUi(spellCheckSettingsWidgetPointer);

    // Get handles for the widgets.
    QCheckBox *javaScriptCheckBoxPointer = privacySettingsUi.kcfg_javaScriptEnabled;
    QCheckBox *localStorageCheckBoxPointer = privacySettingsUi.kcfg_localStorageEnabled;
    QCheckBox *domStorageCheckBoxPointer = privacySettingsUi.kcfg_domStorageEnabled;
    QComboBox *userAgentComboBoxPointer = privacySettingsUi.kcfg_userAgent;
    userAgentLabelPointer = privacySettingsUi.userAgentLabel;
    QComboBox *searchEngineComboBoxPointer = generalSettingsUi.kcfg_searchEngine;
    searchEngineLabelPointer = generalSettingsUi.searchEngineLabel;
    downloadDirectoryComboBoxPointer = generalSettingsUi.kcfg_downloadDirectory;
    QPushButton *browseButtonPointer = generalSettingsUi.browseButton;
    QListWidget *spellCheckListWidgetPointer = spellCheckSettingsUi.spellCheckListWidget;

    // Create a save spell check languages lambda.
    auto updateCheckBoxes = [javaScriptCheckBoxPointer, localStorageCheckBoxPointer, domStorageCheckBoxPointer] ()
    {
        // Only enable the DOM storage check box if both JavaScript and local storage are checked.
        domStorageCheckBoxPointer->setEnabled(javaScriptCheckBoxPointer->isChecked() && localStorageCheckBoxPointer->isChecked());
    };

    // Update the status of the DOM storage check box when either JavaScript or local storage are changed.
    connect(javaScriptCheckBoxPointer, &QCheckBox::checkStateChanged, this, updateCheckBoxes);
    connect(localStorageCheckBoxPointer, &QCheckBox::checkStateChanged, this, updateCheckBoxes);

    // Populate the combo box labels.
    updateUserAgentLabel(userAgentComboBoxPointer->currentText());
    updateSearchEngineLabel(searchEngineComboBoxPointer->currentText());

    // Update the labels when the combo boxes change.
    connect(userAgentComboBoxPointer, SIGNAL(currentTextChanged(const QString)), this, SLOT(updateUserAgentLabel(const QString)));
    connect(searchEngineComboBoxPointer, SIGNAL(currentTextChanged(const QString)), this, SLOT(updateSearchEngineLabel(const QString)));

    // Connect the download directory directory browse button.
    connect(browseButtonPointer, SIGNAL(clicked()), this, SLOT(showDownloadDirectoryBrowseDialog()));

    // Create a dictionaries QDir from the `QTWEBENGINE_DICTIONARIES_PATH` environment variable, defaulting to `/usr/share/hunspell-bdic` if no environment variable is set.
    QDir dictionariesDir = QDir(qEnvironmentVariable("QTWEBENGINE_DICTIONARIES_PATH", QLatin1String("/usr/share/hunspell-bdic")));

    // Get a dictionaries string list.
    QStringList dictionariesStringList = dictionariesDir.entryList(QStringList(QLatin1String("*.bdic")), QDir::Files | QDir::NoSymLinks);

    // Remove the `.bdic` file extensions from the dictionaries list.
    dictionariesStringList.replaceInStrings(QLatin1String(".bdic"), QLatin1String(""));

    // Get a list of the enabled spell check languages.
    QStringList enabledSpellCheckLanguagesList = Settings::spellCheckLanguages();

    // Add each dictionary to the spell check list widget.
    for (QString dictionaryString : dictionariesStringList)
    {
        // Create a new list widget item pointer.
        QListWidgetItem *listWidgetItemPointer = new QListWidgetItem();

        // Create a dictionary check box widget with the name of the dictionary string.
        QCheckBox *dictionaryCheckBoxWidget = new QCheckBox(dictionaryString);

        // Check the language if it is currently enabled.
        if (enabledSpellCheckLanguagesList.contains(dictionaryString))
            dictionaryCheckBoxWidget->setCheckState(Qt::Checked);
        else
            dictionaryCheckBoxWidget->setCheckState(Qt::Unchecked);

        // Add the list widget item to the spell check list widget.
        spellCheckListWidgetPointer->addItem(listWidgetItemPointer);

        // Set the list widget item check box widget.
        spellCheckListWidgetPointer->setItemWidget(listWidgetItemPointer, dictionaryCheckBoxWidget);
    }

    // Create a settings icon string.
    QString settingsIconString;

    // Get a settings icon that matches the theme.
    if (QIcon::hasThemeIcon(QLatin1String("breeze-settings")))
    {
        // KDE uses breeze-settings.
        settingsIconString = QLatin1String("breeze-settings");
    }
    else
    {
        // Gnome uses preferences-desktop.
        settingsIconString = QLatin1String("preferences-desktop");
    }

    // Add the settings widgets as config dialog pages.
    addPage(privacySettingsWidgetPointer, i18nc("Settings tab title", "Privacy"), QLatin1String("privacybrowser"));
    addPage(generalSettingsWidgetPointer, i18nc("Settings tab title", "General"), settingsIconString);
    addPage(spellCheckSettingsWidgetPointer, i18nc("Settings tab title", "Spell Check"), QLatin1String("tools-check-spelling"));

    // Get handles for the buttons.
    QPushButton *applyButtonPointer = button(QDialogButtonBox::Apply);
    QPushButton *okButtonPointer = button(QDialogButtonBox::Ok);

    // Prevent interaction with the parent window while the dialog is open.
    setWindowModality(Qt::WindowModal);

    // Create a save spell check languages lambda.
    auto saveSpellCheckLanguages = [spellCheckListWidgetPointer, coreConfigSkeletonPointer, this] ()
    {
        // Create a list of enabled languages.
        QStringList newSpellCheckLanguages = QStringList();

        // Get a count of all the languages.
        int allLanguagesCount = spellCheckListWidgetPointer->count();

        // Get a list of all the checked languages.
        for (int i = 0; i < allLanguagesCount; ++i) {
            // Get the language item.
            QListWidgetItem *languageItemPointer = spellCheckListWidgetPointer->item(i);

            // Get the language check box.
            QCheckBox *languageCheckBoxPointer = qobject_cast<QCheckBox*>(spellCheckListWidgetPointer->itemWidget(languageItemPointer));

            // Add the item to the enabled languages if it is checked.
            if (languageCheckBoxPointer->checkState() == Qt::Checked)
            {
                // Get the text.
                QString languageString = languageCheckBoxPointer->text();

                // Remove all instances of `&`, which may have been added automatically when creating the check box text.
                languageString.remove(QLatin1Char('&'));

                // Add the language string to the list.
                newSpellCheckLanguages.append(languageString);
            }
        }

        // Update the spell check languages.
        if (Settings::spellCheckLanguages() != newSpellCheckLanguages)
        {
            // Update the spell check languages.
            Settings::setSpellCheckLanguages(newSpellCheckLanguages);

            // Write the settings to disk.
            coreConfigSkeletonPointer->save();

            // Emit the spell check languages updated signal.
            Q_EMIT spellCheckLanguagesUpdated();
        }
    };

    // Process clicks on the buttons.
    connect(applyButtonPointer, &QPushButton::clicked, this, saveSpellCheckLanguages);
    connect(okButtonPointer, &QPushButton::clicked, this, saveSpellCheckLanguages);
}

void SettingsDialog::showDownloadDirectoryBrowseDialog()
{
    // Get the current download directory.
    QString currentDownloadDirectory = downloadDirectoryComboBoxPointer->currentText();

    // Resolve the system download directory if specified.
    if (currentDownloadDirectory == QStringLiteral("System Download Directory"))
        currentDownloadDirectory = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);

    // Get the new download directory.
    QString newDownloadDirectory = QFileDialog::getExistingDirectory(this, i18nc("Select download directory dialog caption", "Select Download Directory"), currentDownloadDirectory);

    // Populate the download directory combo box according to the new download location.
    if (newDownloadDirectory == QStandardPaths::writableLocation(QStandardPaths::DownloadLocation))  // The default download location was selected.
    {
        // Populate the download location with the default text.
        downloadDirectoryComboBoxPointer->setCurrentText(QLatin1String("System Download Directory"));
    }
    else if (newDownloadDirectory != QStringLiteral(""))  // A different directory was selected.
    {
        // Populate the download location.
        downloadDirectoryComboBoxPointer->setCurrentText(newDownloadDirectory);
    }
}

void SettingsDialog::updateSearchEngineLabel(const QString &searchEngineString) const
{
    // Update the search engine label.
    searchEngineLabelPointer->setText(SearchEngineHelper::getSearchUrl(searchEngineString));
}

void SettingsDialog::updateUserAgentLabel(const QString &userAgentDatabaseName) const
{
    // Update the user agent label.
    userAgentLabelPointer->setText(UserAgentHelper::getUserAgentFromDatabaseName(userAgentDatabaseName));
}
