/*
 *
 *      keyserverimportdialog.cpp
 *
 *      Copyright 2008 gpg4usb-team <gpg4usb@cpunk.de>
 *
 *      This file is part of gpg4usb.
 *
 *      Gpg4usb 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.
 *
 *      Gpg4usb 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 gpg4usb.  If not, see <http://www.gnu.org/licenses/>
 */

#include "keyserverimportdialog.h"

#include <utility>

KeyServerImportDialog::KeyServerImportDialog(GpgME::GpgContext *ctx, KeyList *keyList, QWidget *parent)
        : QDialog(parent) {
    mCtx = ctx;
    mKeyList = keyList;
    // Buttons
    closeButton = createButton(tr("&Close"), SLOT(close()));
    importButton = createButton(tr("&Import"), SLOT(slotImport()));
    searchButton = createButton(tr("&Search"), SLOT(slotSearch()));

    // Line edit for search string
    searchLabel = new QLabel(tr("Search string:"));
    searchLineEdit = new QLineEdit();

    // combobox for keyserverlist
    keyServerLabel = new QLabel(tr("Keyserver:"));
    keyServerComboBox = createComboBox();

    // table containing the keys found
    createKeysTable();
    message = new QLabel;
    icon = new QLabel;

    // Layout for messagebox
    auto *messageLayout = new QHBoxLayout;
    messageLayout->addWidget(icon);
    messageLayout->addWidget(message);
    messageLayout->addStretch();

    // Layout for import and close button
    auto *buttonsLayout = new QHBoxLayout;
    buttonsLayout->addStretch();
    buttonsLayout->addWidget(importButton);
    buttonsLayout->addWidget(closeButton);

    auto *mainLayout = new QGridLayout;
    mainLayout->addWidget(searchLabel, 1, 0);
    mainLayout->addWidget(searchLineEdit, 1, 1);
    mainLayout->addWidget(searchButton, 1, 2);
    mainLayout->addWidget(keyServerLabel, 2, 0);
    mainLayout->addWidget(keyServerComboBox, 2, 1);
    mainLayout->addWidget(keysTable, 3, 0, 1, 3);
    mainLayout->addLayout(messageLayout, 4, 0, 1, 3);
    mainLayout->addLayout(buttonsLayout, 5, 0, 1, 3);

    this->setLayout(mainLayout);
    this->setWindowTitle(tr("Import Keys from Keyserver"));
    this->resize(700, 300);
    this->setModal(true);
}

QPushButton *KeyServerImportDialog::createButton(const QString &text, const char *member) {
    auto *button = new QPushButton(text);
    connect(button, SIGNAL(clicked()), this, member);
    return button;
}

QComboBox *KeyServerImportDialog::createComboBox() {
    auto *comboBox = new QComboBox;
    comboBox->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);

    // Read keylist from ini-file and fill it into combobox
    QSettings settings;
    comboBox->addItems(settings.value("keyserver/keyServerList").toStringList());

    // set default keyserver in combobox
    QString keyserver = settings.value("keyserver/defaultKeyServer").toString();
    comboBox->setCurrentIndex(comboBox->findText(keyserver));

    return comboBox;
}

void KeyServerImportDialog::createKeysTable() {
    keysTable = new QTableWidget();
    keysTable->setColumnCount(4);

    // always a whole row is marked
    keysTable->setSelectionBehavior(QAbstractItemView::SelectRows);
    keysTable->setEditTriggers(QAbstractItemView::NoEditTriggers);

    // Make just one row selectable
    keysTable->setSelectionMode(QAbstractItemView::SingleSelection);

    QStringList labels;
    labels << tr("UID") << tr("Creation date") << tr("KeyID") << tr("Tag");
    keysTable->horizontalHeader()->setSectionResizeMode(0, QHeaderView::ResizeToContents);
    keysTable->setHorizontalHeaderLabels(labels);
    keysTable->verticalHeader()->hide();

    connect(keysTable, SIGNAL(cellActivated(int, int)),
            this, SLOT(slotImport()));
}

void KeyServerImportDialog::setMessage(const QString &text, bool error) {
    message->setText(text);
    if (error) {
        QIcon undoicon = QIcon::fromTheme("dialog-error");
        QPixmap pixmap = undoicon.pixmap(QSize(32, 32), QIcon::Normal, QIcon::On);
        icon->setPixmap(pixmap);
    } else {
        QIcon undoicon = QIcon::fromTheme("dialog-information");
        QPixmap pixmap = undoicon.pixmap(QSize(32, 32), QIcon::Normal, QIcon::On);
        icon->setPixmap(pixmap);
    }
}

void KeyServerImportDialog::slotSearch() {
    QUrl urlFromRemote = keyServerComboBox->currentText() + ":11371/pks/lookup?search=" + searchLineEdit->text() +
                         "&op=index&options=mr";
    qnam = new QNetworkAccessManager(this);
    QNetworkReply *reply = qnam->get(QNetworkRequest(urlFromRemote));
    connect(reply, SIGNAL(finished()),
            this, SLOT(slotSearchFinished()));
}

void KeyServerImportDialog::slotSearchFinished() {
    auto *reply = qobject_cast<QNetworkReply *>(sender());

    keysTable->clearContents();
    keysTable->setRowCount(0);
    QString firstLine = QString(reply->readLine(1024));

    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (reply->error()) {
        setMessage(tr("Couldn't contact keyserver!"), true);
        //setMessage(reply->error());
        qDebug() << reply->error();
    }
    if (firstLine.contains("Error")) {
        QString text = QString(reply->readLine(1024));
        if (text.contains("Too many responses")) {
            setMessage(tr("Too many responses from keyserver!"), true);
        } else if (text.contains("No keys found")) {
            // if string looks like hex string, search again with 0x prepended
            QRegExp rx("[0-9A-Fa-f]*");
            QString query = searchLineEdit->text();
            if (rx.exactMatch(query)) {
                setMessage(tr("No keys found, input may be kexId, retrying search with 0x."), true);
                searchLineEdit->setText(query.prepend("0x"));
                this->slotSearch();
            } else {
                setMessage(tr("No keys found containing the search string!"), true);
            }
        } else if (text.contains("Insufficiently specific words")) {
            setMessage(tr("Insufficiently specific search string!"), true);
        } else {
            setMessage(text, true);
        }
    } else {
        int row = 0;
        char buff[1024];
        bool strikeout = false;
        while (reply->readLine(buff, sizeof(buff)) != -1) {
            QString decoded = QString::fromUtf8(QByteArray::fromPercentEncoding(buff));
            QStringList line = decoded.split(":");

            //TODO: have a look at two following pub lines
            if (line[0] == "pub") {
                strikeout = false;

                QString flags = line[line.size() - 1];

                keysTable->setRowCount(row + 1);

                // flags can be "d" for disabled, "r" for revoked
                // or "e" for expired
                if (flags.contains("r") or flags.contains("d") or flags.contains("e")) {
                    strikeout = true;
                    if (flags.contains("e")) {
                        keysTable->setItem(row, 3, new QTableWidgetItem(QString("expired")));
                    }
                    if (flags.contains("r")) {
                        keysTable->setItem(row, 3, new QTableWidgetItem(QString(tr("revoked"))));
                    }
                    if (flags.contains("d")) {
                        keysTable->setItem(row, 3, new QTableWidgetItem(QString(tr("disabled"))));
                    }
                }

                QStringList line2 = QString(reply->readLine()).split(":");

                auto *uid = new QTableWidgetItem();
                if (line2.size() > 1) {
                    uid->setText(line2[1]);
                    keysTable->setItem(row, 0, uid);
                }
                auto *creationdate = new QTableWidgetItem(
                        QDateTime::fromTime_t(line[4].toInt()).toString("dd. MMM. yyyy"));
                keysTable->setItem(row, 1, creationdate);
                auto *keyid = new QTableWidgetItem(line[1]);
                keysTable->setItem(row, 2, keyid);
                if (strikeout) {
                    QFont strike = uid->font();
                    strike.setStrikeOut(true);
                    uid->setFont(strike);
                    creationdate->setFont(strike);
                    keyid->setFont(strike);
                }
                row++;
            } else {
                if (line[0] == "uid") {
                    QStringList l;
                    int height = keysTable->rowHeight(row - 1);
                    keysTable->setRowHeight(row - 1, height + 16);
                    QString tmp = keysTable->item(row - 1, 0)->text();
                    tmp.append(QString("\n") + line[1]);
                    auto *tmp1 = new QTableWidgetItem(tmp);
                    keysTable->setItem(row - 1, 0, tmp1);
                    if (strikeout) {
                        QFont strike = tmp1->font();
                        strike.setStrikeOut(true);
                        tmp1->setFont(strike);
                    }
                }
            }
            setMessage(tr("%1 keys found. Doubleclick a key to import it.").arg(row), false);
        }
        keysTable->resizeColumnsToContents();
    }
    reply->deleteLater();
}

void KeyServerImportDialog::slotImport() {
    if (keysTable->currentRow() > -1) {
        QString keyid = keysTable->item(keysTable->currentRow(), 2)->text();
        slotImport(QStringList(keyid), keyServerComboBox->currentText());
    }
}

void KeyServerImportDialog::slotImport(QStringList keyIds) {
    QSettings settings;
    QString keyserver = settings.value("keyserver/defaultKeyServer").toString();;
    slotImport(std::move(keyIds), QUrl(keyserver));
}


void KeyServerImportDialog::slotImport(QStringList keyIds, const QUrl &keyServerUrl) {
            foreach(QString keyId, keyIds) {
            QUrl reqUrl(
                    keyServerUrl.scheme() + "://" + keyServerUrl.host() + ":11371/pks/lookup?op=get&search=0x" + keyId +
                    "&options=mr");
            //qDebug() << "req to " << reqUrl;
            qnam = new QNetworkAccessManager(this);
            QNetworkReply *reply = qnam->get(QNetworkRequest(reqUrl));
            connect(reply, SIGNAL(finished()),
                    this, SLOT(slotImportFinished()));
        }
}

void KeyServerImportDialog::slotImportFinished() {
    auto *reply = qobject_cast<QNetworkReply *>(sender());

    QByteArray key = reply->readAll();

    QVariant redirectionTarget = reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
    if (reply->error()) {
        setMessage(tr("Error while contacting keyserver!"), true);
        return;
    }
    this->importKeys(key.constData());
    setMessage(tr("Key imported"), false);

    // Add keyserver to list in config-file, if it isn't contained
    QSettings settings;
    QStringList keyServerList = settings.value("keyserver/keyServerList").toStringList();
    if (!keyServerList.contains(keyServerComboBox->currentText())) {
        keyServerList.append(keyServerComboBox->currentText());
        settings.setValue("keyserver/keyServerList", keyServerList);
    }
    reply->deleteLater();
    reply = nullptr;
}

void KeyServerImportDialog::importKeys(QByteArray inBuffer) {
    GpgImportInformation result = mCtx->importKey(std::move(inBuffer));
    new KeyImportDetailDialog(mCtx, result, this);
}
