/* 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 "resolver.h"

#include "util/overlay.h"

#include <QApplication>
#include <QCommandLineParser>
#include <QDebug>

#include <Libkleo/Enum>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyResolver>
#include <Libkleo/KeyGroupConfig>

#include <gpgme++/key.h>

#include <iostream>

class Resolver::Private
{
public:
    Private(Resolver *qq): q(qq) {
    }

    void printResolvedKeys(const Kleo::KeyResolver *kr) {
        const auto result = kr->result();
        for (const auto &key: result.signingKeys) {
            if (!key.isNull()) {
                const std::string protocol = key.protocol() == GpgME::CMS ? "smime" : "openpgp";
                std::cout << "sig:" << protocol << ":"
                    << key.primaryFingerprint() << std::endl;
            }
        }
        for (auto it = std::cbegin(result.encryptionKeys); it != std::cend(result.encryptionKeys); ++it) {
            const auto &mbox = it.key();
            const auto &keys = it.value();
            for (const auto &key: keys) {
                if (!key.isNull()) {
                    const std::string protocol = key.protocol() == GpgME::CMS ? "smime" : "openpgp";
                    std::cout << "enc:" << protocol << ":"
                        << key.primaryFingerprint() << ":" << mbox.toUtf8().constData()
                        << std::endl;
                }
            }
        }
    }

    void newOverlay(WId wid, const QString &text)
    {
        m_overlay = std::shared_ptr<Overlay>(new Overlay(wid, text));
    }

    void newResolver(const QCommandLineParser &parser) {
        const auto proto = parser.value(QStringLiteral("protocol")).toLower();
        GpgME::Protocol protocol;
        if (proto == QStringLiteral("cms")) {
            protocol = GpgME::CMS;
        } else if (proto == QStringLiteral("pgp")) {
            protocol = GpgME::OpenPGP;
        } else {
            protocol = GpgME::UnknownProtocol;
        }

        const auto preferredVal = parser.value(QStringLiteral("preferred-protocol")).toLower();
        GpgME::Protocol preferred = GpgME::UnknownProtocol;
        if (preferredVal == QStringLiteral("cms")) {
            preferred = GpgME::CMS;
        } else if (preferredVal == QStringLiteral("pgp")) {
            preferred = GpgME::OpenPGP;
        }

        QMap <GpgME::Protocol, QMap <QString, QStringList> > overrides;

        for (const QString &oride: parser.values("override")) {
            const QStringList split = oride.split(QLatin1Char(':'));
            GpgME::Protocol fmt = GpgME::UnknownProtocol;
            if (split.size() < 2 || split.size() > 3) {
                qDebug() << "Invalid override format" << oride;
                std::cout << "cancel" << std::endl;
                qApp->quit();
            }

            if (split.size() == 3) {
                const QString fmtStr = split[2].toLower();
                if (fmtStr == "openpgp") {
                    fmt = GpgME::OpenPGP;
                } else if (fmtStr == "smime") {
                    fmt = GpgME::CMS;
                } else if (fmtStr == "auto") {
                    fmt = GpgME::UnknownProtocol;
                } else {
                    qDebug() << "Invalid override protocol string" << fmtStr;
                    std::cout << "cancel" << std::endl;
                    qApp->quit();
                }
            }
            const QStringList fingerprints = split[1].split(QLatin1Char(','));

            auto map = overrides.value(fmt);
            map.insert(split[0], fingerprints);
            overrides.insert(fmt, map);
            qDebug () << "Passing overrides" << fingerprints << split[0];
        }

        const auto recps = parser.positionalArguments();
        const bool encrypt = !recps.isEmpty() || parser.isSet(QStringLiteral("encrypt"));
        const bool sign = parser.isSet(QStringLiteral("sign"));
        const bool allowMixed = parser.isSet("allowMixed");
        auto *kr = new Kleo::KeyResolver(encrypt, sign, protocol, allowMixed);

        kr->setRecipients(recps);
        kr->setSender(parser.value("sender"));
        kr->setOverrideKeys(overrides);
        kr->setPreferredProtocol(preferred);

        connect (kr, &Kleo::KeyResolver::keysResolved, q, [this, kr] (bool success, bool sendUnencrypted) {
            if (!success) {
                std::cout << "cancel" << std::endl;
            } else if (sendUnencrypted) {
                std::cout << "unencrypted" << std::endl;
            } else {
                printResolvedKeys(kr);
            }
            delete kr;
            qApp->quit();
        });
        kr->setDialogWindowFlags(Qt::Window |
                                 Qt::CustomizeWindowHint |
                                 Qt::WindowTitleHint |
                                 Qt::WindowCloseButtonHint);
        kr->start(parser.isSet(QStringLiteral("alwaysShow")), m_overlay.get());
    }
    Resolver *q;
    std::shared_ptr<Overlay> m_overlay;
};

Resolver::Resolver(int &argc, char *argv[]) :
    QApplication(argc, argv),
    d(new Private(this))
{

}

QString Resolver::newInstance(const QCommandLineParser &parser)
{
    const auto hwnd = parser.value(QStringLiteral("hwnd"));
    if (!hwnd.isEmpty()) {
        bool ok;
        WId id = (WId) hwnd.toInt(&ok);
        if (!ok) {
            std::cerr << "invalid hwnd value" << std::endl;
            exit(EXIT_FAILURE);
        }
        d->newOverlay(id, parser.value(QStringLiteral("overlayText")));
    }

    // Enable groups early
    auto cache = Kleo::KeyCache::mutableInstance();
    cache->setGroupConfig(std::make_shared<Kleo::KeyGroupConfig>(QStringLiteral("kleopatragroupsrc")));
    cache->setGroupsEnabled(true);

    d->newResolver(parser);

    return QString();
}
