/*
 * options.cpp - plugin widget
 *
 * Copyright (C) 2013  Ivan Romanov <drizt@land.ru>
 *
 * 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 2
 * 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

#include <QInputDialog>
#include <QStandardItemModel>
#include <QStandardItem>
#include <QItemSelectionModel>
#include <QProgressDialog>
#include <QApplication>
#include <QFileDialog>
#include <QMessageBox>
#include <QLabel>
#include <QProgressBar>
#include <QAction>
#include <QMenu>
#include <QClipboard>
#include <QApplication>
#include "options.h"
#include "ui_options.h"
#include "model.h"
#include "gpgprocess.h"
#include "addkeydlg.h"
#include "optionaccessinghost.h"

Options::Options(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Options)
{
    ui->setupUi(this);

    Model *model = new Model(this);
    ui->keys->setModel(model);
    updateKeys();


    // Import key
    QAction *action;
    QMenu *menu = new QMenu(this);

    action = menu->addAction(trUtf8("from file"));
    connect(action, SIGNAL(triggered()), SLOT(importKeyFromFile()));

    action = menu->addAction(trUtf8("from clipboard"));
    connect(action, SIGNAL(triggered()), SLOT(importKeyFromClipboard()));

    ui->btnImport->setMenu(menu);

    // Export key

    menu = new QMenu(this);
    action = menu->addAction(trUtf8("to file"));
    connect(action, SIGNAL(triggered()), SLOT(exportKeyToFile()));
    ui->btnExport->addAction(action);

    action = menu->addAction(trUtf8("to clipboard"));
    connect(action, SIGNAL(triggered()), SLOT(exportKeyToClipboard()));

    ui->btnExport->setMenu(menu);
}

Options::~Options()
{
    delete ui;
}

void Options::update()
{
}

void Options::loadSettings()
{
    ui->chkAutoImport->setChecked(_optionHost->getPluginOption("auto-import", true).toBool());
    ui->chkHideKeyMessage->setChecked(_optionHost->getPluginOption("hide-key-message", true).toBool());
}

void Options::saveSettings()
{
    _optionHost->setPluginOption("auto-import", ui->chkAutoImport->isChecked());
    _optionHost->setPluginOption("hide-key-message", ui->chkHideKeyMessage->isChecked());
}

void Options::addKey()
{
    AddKeyDlg dlg(this);
    if (dlg.exec() == QDialog::Rejected) {
        return;
    }

    QString key;
    QString type, stype, length, name, comment, email, expiration, pass;
    switch (dlg.type()) {
    case 0: type = stype = "RSA"; break;
    case 1: type = "DSA"; stype = "ELG-E"; break;
    case 2: type = "DSA"; break;
    case 3: type = "RSA"; break;
    }

    length = QString::number(dlg.length());
    name = dlg.name();
    comment = dlg.comment();
    email = dlg.email();
    expiration = dlg.expiration().isValid() ? dlg.expiration().toString(Qt::ISODate) : "0";
    pass = dlg.pass();

    key += QString("Key-Type: %1\n").arg(type);
    key += QString("Key-Length: %2\n").arg(length);
    if (!stype.isEmpty()) {
        key += QString("Subkey-Type: %1\n").arg(stype);
        key += QString("Subkey-Length: %2\n").arg(length);
    }

    if (!name.isEmpty()) {
        key += QString("Name-Real: %1\n").arg(name);
    }

    if (!comment.isEmpty()) {
        key += QString("Name-Comment: %1\n").arg(comment);
    }

    if (!email.isEmpty()) {
        key += QString("Name-Email: %1\n").arg(email);
    }

    key += QString("Expire-Date: %1\n").arg(expiration);

    if (!pass.isEmpty()) {
        key += QString("Passphrase: %1\n").arg(pass);
    }

    key += "%commit\n";

    QProgressDialog waitingDlg("", trUtf8("Cancel"), 0, 0, this);

    QLabel progressTextLabel(trUtf8(
"<b>Please wait!</b><br/>"
"We need to generate a lot of random bytes. It is a good idea to perform "
"some other action (type on the keyboard, move the mouse, utilize the "
"disks) during the prime generation; this gives the random number "
"generator a better chance to gain enough entropy."), &waitingDlg);
    progressTextLabel.setAlignment(Qt::AlignHCenter);
    progressTextLabel.setWordWrap(true);

    waitingDlg.setLabel(&progressTextLabel);

    QProgressBar progressBar(&waitingDlg);
    progressBar.setAlignment(Qt::AlignHCenter);
    progressBar.setMinimum(0);
    progressBar.setMaximum(0);

    waitingDlg.setBar(&progressBar);

    waitingDlg.setWindowModality(Qt::WindowModal);
    waitingDlg.setWindowTitle(trUtf8("Key pair generating"));
    waitingDlg.show();

    GpgProcess gpg;
    QStringList arguments;
    arguments << "--batch"
              << "--gen-key";

    gpg.start(arguments);
    gpg.waitForStarted();
    gpg.write(key.toUtf8());
    gpg.closeWriteChannel();
    while (gpg.state() == QProcess::Running) {
        gpg.waitForFinished(1);
        if (waitingDlg.wasCanceled()) {
            gpg.terminate();
            break;
        }
        qApp->processEvents();
    }

    updateKeys();
}

void Options::removeKey()
{
    QItemSelectionModel *selModel = ui->keys->selectionModel();

    if (!selModel->hasSelection()) {
        return;
    }

    QModelIndexList indexes = selModel->selectedIndexes();
    QModelIndexList pkeys; // Key IDs
    foreach (QModelIndex index, indexes) {
        // Every selection contains all columns. Need to work only with first
        if (index.column() > 0) {
            continue;
        }

        // Choose only primary keys
        QModelIndex pIndex = index;
        if (index.parent().isValid()) {
            pIndex = index.parent();
        }

        if (pkeys.indexOf(pIndex) < 0) {
            pkeys << pIndex;
        }
    }

    if (!pkeys.isEmpty()) {
        if (QMessageBox::question(this, tr("Delete"), tr("Do you want to delete the selected keys?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) {
            return;
        }
    }

    // Remove primary keys
    foreach (QModelIndex key, pkeys) {
        GpgProcess gpg;
        QStringList arguments;
        arguments << "--yes"
                  << "--batch"
                  << "--delete-secret-and-public-key"
                  << "0x" + key.sibling(key.row(), Model::Fingerprint).data().toString();

        gpg.start(arguments);
        gpg.waitForFinished();
    }

    updateKeys();
}

void Options::importKeyFromFile()
{
    QFileDialog dlg(this);
    dlg.setFileMode(QFileDialog::ExistingFiles);
    QStringList nameFilters;
    nameFilters << trUtf8("ASCII (*.asc)")
                << trUtf8("All files (*)");
    dlg.setNameFilters(nameFilters);
    if (dlg.exec() == QDialog::Rejected) {
        return;
    }

    QStringList allFiles = dlg.selectedFiles();
    foreach (QString filename, allFiles) {
        GpgProcess gpg;
        QStringList arguments;
        arguments << "--batch"
                  << "--import"
                  << filename;
        gpg.start(arguments);
        gpg.waitForFinished();
    }

    updateKeys();
}

void Options::exportKeyToFile()
{
    QItemSelectionModel *selModel = ui->keys->selectionModel();

    if (!selModel->hasSelection()) {
        return;
    }

    QModelIndexList indexes = selModel->selectedIndexes();
    QModelIndexList pkeys; // Key IDs
    foreach (QModelIndex index, indexes) {
        // Every selection contains all columns. Need to work only with first
        if (index.column() > 0) {
            continue;
        }

        // Choose only primary keys
        QModelIndex pIndex = index;
        if (index.parent().isValid()) {
            pIndex = index.parent();
        }

        if (pkeys.indexOf(pIndex) < 0) {
            pkeys << pIndex;
        }
    }

    // Remove primary keys
    foreach (QModelIndex key, pkeys) {
        QString filename = key.sibling(key.row(), 1).data().toString() + " " + key.sibling(key.row(), 2).data().toString() + ".asc";
        QFileDialog dlg(this);
        dlg.setAcceptMode(QFileDialog::AcceptSave);
        dlg.setFileMode(QFileDialog::AnyFile);
        QStringList nameFilters;
        nameFilters << trUtf8("ASCII (*.asc)");
        dlg.setNameFilters(nameFilters);
        dlg.selectFile(filename);
        if (dlg.exec() == QDialog::Rejected) {
            break;
        }

        filename = dlg.selectedFiles().first();
        if (filename.right(4) != ".asc") {
            filename += ".asc";
        }

        GpgProcess gpg;
        QStringList arguments;
        QString fingerprint = "0x" + key.sibling(key.row(), 8).data().toString();
        arguments << "--output"
                  << filename
                  << "--armor"
                  << "--export"
                  << fingerprint;

        gpg.start(arguments);
        gpg.waitForFinished();
    }
}

void Options::importKeyFromClipboard()
{
    QClipboard *clipboard = QApplication::clipboard();
    QString key = clipboard->text().trimmed();

    if (!key.startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----") ||
        !key.endsWith("-----END PGP PUBLIC KEY BLOCK-----")) {
        return;
    }

    GpgProcess gpg;
    QStringList arguments;
    arguments << "--batch"
              << "--import";
    gpg.start(arguments);
    gpg.waitForStarted();
    gpg.write(key.toUtf8());
    gpg.closeWriteChannel();
    gpg.waitForFinished();

    updateKeys();
}

void Options::exportKeyToClipboard()
{
    QItemSelectionModel *selModel = ui->keys->selectionModel();

    if (!selModel->hasSelection()) {
        return;
    }

    QModelIndexList indexes = selModel->selectedIndexes();
    QModelIndexList pkeys; // Key IDs
    foreach (QModelIndex index, indexes) {
        // Every selection contains all columns. Need to work only with first
        if (index.column() > 0) {
            continue;
        }

        // Choose only primary keys
        QModelIndex pIndex = index;
        if (index.parent().isValid()) {
            pIndex = index.parent();
        }

        if (pkeys.indexOf(pIndex) < 0) {
            pkeys << pIndex;
        }
    }

    // Remove primary keys
    QString strKey = "";
    foreach (QModelIndex key, pkeys) {
        GpgProcess gpg;
        QStringList arguments;
        QString fingerprint = "0x" + key.sibling(key.row(), 8).data().toString();
        arguments << "--armor"
                  << "--export"
                  << fingerprint;

        gpg.start(arguments);
        gpg.waitForFinished();

        strKey += QString::fromUtf8(gpg.readAllStandardOutput());
    }

    QClipboard *clipboard = QApplication::clipboard();
    clipboard->setText(strKey.toUtf8().trimmed());
}

void Options::showInfo()
{
    GpgProcess gpg;
    QString info;
    QMessageBox::Icon icon;
    if (gpg.info(info)) {
        icon = QMessageBox::Information;
    }
    else {
        icon = QMessageBox::Critical;
    }
    QMessageBox box(icon, trUtf8("GnuPG info"), info, QMessageBox::Ok, this);
    box.exec();
}

void Options::updateKeys()
{
    qobject_cast<Model*>(ui->keys->model())->listKeys();

    int columns = ui->keys->model()->columnCount();
    for (int i = 0; i < columns; i++) {
        ui->keys->resizeColumnToContents(i);
    }
}
