/* Copyright (C) 2018 by Intevation GmbH <info@intevation.de>
 *
 * This file is free software under the GNU GPL (v>=2)
 * and comes with ABSOLUTELY NO WARRANTY!
 * See LICENSE.txt for details.
 */

#include "gpgolconfigpage.h"
#include "w32-gettext.h"
#include "w32-util.h"

#include <QDebug>
#include <QGroupBox>
#include <QLabel>
#include <QCheckBox>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QToolTip>
#include <QPushButton>

#include <Libkleo/KeySelectionCombo>
#include <Libkleo/DefaultKeyFilter>


class SecretKeyFilter: public Kleo::DefaultKeyFilter
{
public:
    SecretKeyFilter(): Kleo::DefaultKeyFilter()
    {
        setHasSecret(Kleo::DefaultKeyFilter::Set);
        setRevoked(Kleo::DefaultKeyFilter::NotSet);
        setDisabled(Kleo::DefaultKeyFilter::NotSet);
        setExpired(Kleo::DefaultKeyFilter::NotSet);
        setCanEncrypt(Kleo::DefaultKeyFilter::Set);

    }
};
static auto s_secretKeyFilter = std::shared_ptr<Kleo::KeyFilter> (new SecretKeyFilter);

/*
class ExplainingChkBox: public QWidget
{
    Q_OBJECT
public:
    explicit ExplainingChkBox(const QString &text, const QString &explanation):
        mChkBox(new QCheckBox(text)),
        mExplanation(explanation)
    {
        auto hBox = new QHBoxLayout(this);

        hBox->addWidget(mChkBox);

        auto infoBtn = new QPushButton;
        infoBtn->setIcon(QIcon::fromTheme("help-contextual"));
        hBox->addWidget(infoBtn);
        hBox->addStretch(1);

        connect(infoBtn, &QPushButton::clicked, this, [this, infoBtn] () {
                QToolTip::showText(infoBtn->mapToGlobal(QPoint()), mExplanation, infoBtn);
            });
    }

    void setChecked(bool value)
    {
        mChkBox->setChecked(value);
    }
private:
    QCheckBox *mChkBox;
    QString mExplanation;
};
*/

GpgOLConfigPage::GpgOLConfigPage(QWidget *parent):
    QWidget(parent)
{
    setupGUI();

    load();
}

/* Helper to build an "About" style layout.
static QLayout *buildAboutLayout(const QString &version)
{
    auto hLay = new QHBoxLayout;
    auto vLay = new QVBoxLayout;
    hLay->addLayout(vLay);
    hLay->addStretch(1);

    auto iconLbl = new QLabel;
    iconLbl->setPixmap(QIcon(":/gpgol-logo.png").pixmap(128, 80));
    auto versionLbl = new QLabel(QStringLiteral("    ") + QString::fromUtf8(_("Version ")) + version);
    vLay->addWidget(iconLbl);
    vLay->addWidget(versionLbl);

    return hLay;
}
*/

void GpgOLConfigPage::setupGUI()
{
    auto baseLay = new QVBoxLayout(this);

    mSMIMEGrp = new QGroupBox(_("Enable the S/MIME support"));
    mSMIMEGrp->setCheckable(true);
    mSMIMEGrp->setAlignment(Qt::AlignLeft);

    auto smimeLay = new QVBoxLayout(mSMIMEGrp);

    mPreferSMIMEChk = new QCheckBox(_("&Prefer S/MIME"));
    mPreferSMIMEChk->setToolTip(_("Prefer S/MIME over OpenPGP if both are possible."));
    smimeLay->addWidget(mPreferSMIMEChk);

    mSearchSMIMEChk = new QCheckBox(_("Search and import &X509 certificates in the configured directory services"));
    mSearchSMIMEChk->setToolTip(_("Searches for X509 certificates automatically and imports them. This option searches in all configured services."));
    mSearchSMIMEWarning = new QLabel(_("<b>Warning:</b> The configured services will receive information about whom you send Emails!"));

    smimeLay->addWidget(mSearchSMIMEChk);
    smimeLay->addWidget(mSearchSMIMEWarning);

    connect(mSearchSMIMEChk, &QCheckBox::toggled, [this] (bool on) {
            mSearchSMIMEWarning->setVisible(on);
        });


    baseLay->addWidget(mSMIMEGrp);

    // The general group
    auto generalGrp = new QGroupBox(_("General"));
    auto generalLay = new QVBoxLayout(generalGrp);
    generalGrp->setAlignment(Qt::AlignLeft);
    mAlwaysSigChk = new QCheckBox(_("&Sign new messages by default"));
    mAlwaysSigChk->setToolTip(_("Toggles the sign option for all new mails."));
    mAlwaysEncChk = new QCheckBox(_("&Encrypt new messages by default"));
    mAlwaysEncChk->setToolTip(_("Toggles the encrypt option for all new mails."));

    mReplyCryptChk = new QCheckBox(_("S&elect crypto settings automatically "
                                     "for reply and forward"));
    mReplyCryptChk->setToolTip(_("Toggles sign, encrypt options if the original mail was signed or encrypted."));

    mInlinePGPChk = new QCheckBox(_("&Send OpenPGP mails without attachments as PGP/Inline"));
    mInlinePGPChk->setToolTip(_("Instead of using the PGP/MIME format, "
                                "which properly handles attachments and encoding, "
                                "the deprecated PGP/Inline is used.\n"
                                "This can be useful for compatibility but should generally not "
                                "be used."));

    mAlwaysShowApprovalChk = new QCheckBox(QString::fromUtf8(_("&Always show security approval dialog")));
    mAlwaysShowApprovalChk->setToolTip(_("Always show the security approval and certificate selection dialog. "
                                          "This slows down the encryption / signing process, especially with large keyrings."));

    generalLay->addWidget(mAlwaysSigChk);
    generalLay->addWidget(mAlwaysEncChk);
    generalLay->addWidget(mReplyCryptChk);
    generalLay->addWidget(mInlinePGPChk);
    generalLay->addWidget(mAlwaysShowApprovalChk);

    // The draft encryption part
    mDraftEncChk = new QCheckBox(QString::fromUtf8(_("Encrypt &drafts of secure mails to this key:")));
    mDraftEncChk->setToolTip(_("Encrypt drafts and autosaved mails if the secure button is toggled."));
    mDraftKey = new Kleo::KeySelectionCombo (false);
    mDraftKey->setKeyFilter(s_secretKeyFilter);

    auto draftLay = new QHBoxLayout;
    draftLay->addWidget(mDraftEncChk);
    draftLay->addWidget(mDraftKey);
    generalLay->addLayout(draftLay);


    baseLay->addWidget(generalGrp);

    // The automation checkboxes
    mAutomationGrp = new QGroupBox(_("Automation"));
    mAutomationGrp->setToolTip(_("Enable or disable any automated key handling."));
    auto autoLayout = new QVBoxLayout(mAutomationGrp);
    mAutomationGrp->setCheckable(true);


    mAutoImportChk = new QCheckBox(_("&Import any keys included in mails"));
    mAutoImportChk->setToolTip(_("Import OpenPGP keys from mail attachments or from mail headers."));
    autoLayout->addWidget(mAutoImportChk);

    mAutoResolveChk = new QCheckBox(_("&Resolve recipient keys automatically"));
    autoLayout->addWidget(mAutoResolveChk);

    mAutoSecureChk = new QCheckBox(_("Automatically secure &messages"));
    mAutoSecureChk->setToolTip(_("Automatically toggles secure if keys with at least level 1 trust were found for all recipients."));
    mAutoEncryptUntrustedChk = new QCheckBox(_("Also &with untrusted keys"));
    mAutoEncryptUntrustedChk->setToolTip(_("Also automatically toggles secure if keys with level 0 trust were found."));

    auto subLay = new QHBoxLayout;
    subLay->addSpacing(20);
    subLay->addWidget(mAutoSecureChk);
    subLay->addWidget(mAutoEncryptUntrustedChk);
    autoLayout->addLayout(subLay);


    mAutoTrustChk = new QCheckBox(QStringLiteral("%1 (%2)").arg(_("Include OpenPGP &trust based on communication history")).arg(_("experimental")));
    mAutoTrustChk->setToolTip(_("This changes the trust model to \"tofu+pgp\" which tracks the history of key usage. "
                                "Automated trust can <b>never</b> exceed level 2."));
    /* Dsiabled for now */
    mAutoTrustChk->setVisible(false);

    autoLayout->addWidget(mAutoTrustChk);

    baseLay->addWidget(mAutomationGrp);

    // baseLay->addLayout(buildAboutLayout(mVersion));

    baseLay->addStretch(1);

    connect(mAutoResolveChk, &QCheckBox::toggled, [this] (bool on) {
            mAutoSecureChk->setEnabled(on);
            mAutoEncryptUntrustedChk->setEnabled(mAutoSecureChk->isChecked());
            mSearchSMIMEChk->setEnabled(mSMIMEGrp->isChecked() && on);
        });
    connect(mAutoSecureChk, &QCheckBox::toggled, [this] (bool on) {
            mAutoEncryptUntrustedChk->setEnabled(on);
        });
    connect(mSMIMEGrp, &QGroupBox::toggled, [this] (bool on) {
            mSearchSMIMEChk->setEnabled(mAutoResolveChk->isChecked() && on);
        });
    connect (mDraftEncChk, &QCheckBox::toggled, [this] (bool on) {
            mDraftKey->setEnabled(on);
        });
}

static std::pair<bool, bool> loadBool(const char *name, bool defaultVal)
{
    bool forced = false;
    bool val = strToBool(W32::readRegStr(nullptr, GPGOL_REG_PATH, name), defaultVal, forced);
    return std::make_pair(val, forced);
}

/* Bump this if you remove a config value */
#define CONFIG_VERSION "1"

static const QMap<QString, std::pair <bool, bool> > defaultMap {
    { QStringLiteral("enableSmime"), std::make_pair(false, false) },
    { QStringLiteral("encryptDefault"), std::make_pair(false, false) },
    { QStringLiteral("signDefault"), std::make_pair(false, false) },
    { QStringLiteral("inlinePGP"), std::make_pair(false, false) },
    { QStringLiteral("replyCrypt"), std::make_pair(true, false) },
    { QStringLiteral("preferSmime"), std::make_pair(false, false) },
    { QStringLiteral("debugGPGME"), std::make_pair(false, false) },
    { QStringLiteral("automation"), std::make_pair(true, false) },
    { QStringLiteral("autoresolve"), std::make_pair(true, false) },
    { QStringLiteral("autosecure"), std::make_pair(true, false) },
    { QStringLiteral("autotrust"), std::make_pair(false, false) },
    { QStringLiteral("automation"), std::make_pair(true, false) },
    { QStringLiteral("syncEnc"), std::make_pair(false, false) },
    { QStringLiteral("searchSmimeServers"), std::make_pair(false, false) },
    { QStringLiteral("autoimport"), std::make_pair(false, false) },
    { QStringLiteral("autoencryptUntrusted"), std::make_pair(false, false) },
    { QStringLiteral("draftEnc"), std::make_pair(false, false) },
    { QStringLiteral("alwaysShowApproval"), std::make_pair(false, false) },
};

void GpgOLConfigPage::updateGUI(const QMap<QString, std::pair <bool, bool> > &values)
{
    bool smimeEnabled = values["enableSmime"].first;
    mSMIMEGrp->setChecked(smimeEnabled);
    mSMIMEGrp->setEnabled(!values["enableSmime"].second);
    mPreferSMIMEChk->setChecked(values["preferSmime"].first);
    mPreferSMIMEChk->setEnabled(!values["preferSmime"].second);
    mSearchSMIMEChk->setChecked(values["searchSmimeServers"].first);
    mSearchSMIMEChk->setEnabled(!values["searchSmimeServers"].second);
    mSearchSMIMEWarning->setVisible(mSearchSMIMEChk->isChecked());

    mAlwaysEncChk->setChecked(values["encryptDefault"].first);
    mAlwaysEncChk->setEnabled(!values["encryptDefault"].second);
    mAlwaysSigChk->setChecked(values["signDefault"].first);
    mAlwaysSigChk->setEnabled(!values["signDefault"].second);
    mInlinePGPChk->setChecked(values["inlinePGP"].first);
    mInlinePGPChk->setEnabled(!values["inlinePGP"].second);
    mReplyCryptChk->setChecked(values["replyCrypt"].first);
    mReplyCryptChk->setEnabled(!values["replyCrypt"].second);
    mDraftEncChk->setChecked(values["draftEnc"].first);
    mDraftEncChk->setEnabled(!values["draftEnc"].second);
    mAlwaysShowApprovalChk->setChecked(values["alwaysShowApproval"].first);
    mAlwaysShowApprovalChk->setEnabled(!values["alwaysShowApproval"].second);

    mAutomationGrp->setChecked(values["automation"].first);
    mAutomationGrp->setEnabled(!values["automation"].second);
    mAutoSecureChk->setChecked(values["autosecure"].first);
    mAutoSecureChk->setEnabled(!values["autosecure"].second);
    mAutoTrustChk->setChecked(values["autotrust"].first);
    mAutoTrustChk->setEnabled(!values["autotrust"].second);
    mAutoResolveChk->setChecked(values["autoresolve"].first);
    mAutoResolveChk->setEnabled(!values["autoresolve"].second);
    mAutoImportChk->setChecked(values["autoimport"].first);
    mAutoImportChk->setEnabled(!values["autoimport"].second);
    mAutoEncryptUntrustedChk->setChecked(values["autoencryptUntrusted"].first);
    mAutoEncryptUntrustedChk->setEnabled(!values["autoencryptUntrusted"].second);

    mAutoSecureChk->setEnabled(mAutoResolveChk->isChecked() && mAutomationGrp->isChecked() && !values["autosecure"].second);
    mAutoEncryptUntrustedChk->setEnabled(mAutoSecureChk->isChecked() && mAutomationGrp->isChecked() && !values["autoencryptUntrusted"].second);
    mSearchSMIMEChk->setEnabled(mAutoResolveChk->isChecked() && smimeEnabled && !values["enableSmime"].second);
}

void GpgOLConfigPage::load()
{
    QMap<QString, std::pair <bool, bool> > confValues;

    for (const auto &key: defaultMap.keys()) {
        confValues[key] = loadBool(key.toLocal8Bit().constData(), defaultMap[key].first);
    }
    updateGUI(confValues);

    const std::string version = W32::readRegStr(nullptr, GPGOL_REG_PATH, "config-version");
    if (version != CONFIG_VERSION) {
        qDebug() << "Config update. Cleaning old values";
    }

    std::string draftKeyFpr = W32::readRegStr(nullptr, GPGOL_REG_PATH, "draftKey");
    if (!draftKeyFpr.empty()) {
        if (draftKeyFpr.size() && draftKeyFpr[draftKeyFpr.size() - 1] == '!')
        {
            draftKeyFpr.pop_back();
            mDraftKey->setEnabled(false);
        }
        mDraftKey->setDefaultKey(QString::fromStdString(draftKeyFpr));
    }
}

void GpgOLConfigPage::defaults()
{
    updateGUI(defaultMap);
}

static void saveBool(const char *name, bool value)
{
    const char *val = value ? "1" : "0";

    if (!W32::writeRegStr(nullptr, GPGOL_REG_PATH, name, val)) {
        qWarning() << "Failed to write registry value for" << name;
    }
}

void GpgOLConfigPage::save() const
{
    saveBool("enableSmime", mSMIMEGrp->isChecked());
    saveBool("preferSmime", mPreferSMIMEChk->isChecked());
    saveBool("searchSmimeServers", mSearchSMIMEChk->isChecked());

    saveBool("encryptDefault", mAlwaysEncChk->isChecked());
    saveBool("signDefault", mAlwaysSigChk->isChecked());
    saveBool("inlinePGP", mInlinePGPChk->isChecked());
    saveBool("replyCrypt", mReplyCryptChk->isChecked());
    saveBool("draftEnc", mDraftEncChk->isChecked());
    saveBool("alwaysShowApproval", mAlwaysShowApprovalChk->isChecked());

    saveBool("automation", mAutomationGrp->isChecked());
    saveBool("autosecure", mAutoSecureChk->isChecked());
    saveBool("autotrust", mAutoTrustChk->isChecked());
    saveBool("autoresolve", mAutoResolveChk->isChecked());
    saveBool("autoencryptUntrusted", mAutoEncryptUntrustedChk->isChecked());
    saveBool("autoimport", mAutoImportChk->isChecked());

    W32::writeRegStr(nullptr, GPGOL_REG_PATH, "config-version", CONFIG_VERSION);

    const auto key = mDraftKey->currentKey();
    if (!key.isNull()) {
        W32::writeRegStr(nullptr, GPGOL_REG_PATH, "draftKey",
                key.primaryFingerprint());
    }
}

#include "gpgolconfigpage.moc"
