/*
 * gnupg.cpp - plugin main class
 *
 * 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 <QDomElement>
#include <QMessageBox>
#include <QFile>
#include <QCursor>
#include <QMenu>
#include "options.h"
#include "gnupg.h"
#include "gpgprocess.h"
#include "psiaccountcontrollinghost.h"
#include "optionaccessinghost.h"
#include "iconfactoryaccessinghost.h"
#include "model.h"
#include "activetabaccessinghost.h"
#include "accountinfoaccessinghost.h"
#include "stanzasendinghost.h"

#ifndef HAVE_QT5
Q_EXPORT_PLUGIN(GnuPG);
#endif

GnuPG::GnuPG()
	: _enabled(false)
	, _optionsForm(0)
	, _accountHost(0)
	, _optionHost(0)
	, _iconFactory(0)
	, _menu(0)
	, _stanzaSending(0)
	, _activeTab(0)
	, _accountInfo(0)
{
}


GnuPG::~GnuPG()
{
}

QWidget *GnuPG::options()
{
	if (!_enabled) {
		return 0;
	}

	_optionsForm = new Options();
	_optionsForm->setOptionAccessingHost(_optionHost);
	_optionsForm->loadSettings();
	return qobject_cast<QWidget*>(_optionsForm);
}

bool GnuPG::enable()
{
	QFile file(":/icons/key.png");
	if ( file.open(QIODevice::ReadOnly) ) {
		QByteArray image = file.readAll();
		_iconFactory->addIcon("gnupg/icon",image);
		file.close();
		_enabled = true;
	}
	else {
		_enabled = false;
	}
	return _enabled;
}

bool GnuPG::disable()
{
	_enabled = false;
	return true;
}

void GnuPG::applyOptions()
{
	_optionsForm->saveSettings();
}

void GnuPG::restoreOptions()
{
}

QPixmap GnuPG::icon() const
{
	return QPixmap(":/icons/gnupg.png");
}

QString GnuPG::pluginInfo()
{
	return tr("Author: ") +	 "Ivan Romanov\n"
		   + tr("e-mail: ") + "drizt@land.ru\n\n"
		   + tr("GnuPG Key Manager can create, remove, export and import GnuPG keys. "
				"It can do only the base operations but I hope it will be enough for your needs.");
}

bool GnuPG::incomingStanza(int account, const QDomElement& stanza)
{
	if (!_enabled) {
		return false;
	}

	if (!_optionHost->getPluginOption("auto-import", true).toBool()) {
		return false;
	}

	if (stanza.tagName() != "message" && stanza.attribute("type") != "chat") {
		return false;
	}

	QString body = stanza.firstChildElement("body").text();

	int start = body.indexOf("-----BEGIN PGP PUBLIC KEY BLOCK-----");
	if (start == -1) {
		return false;
	}

	int end = body.indexOf("-----END PGP PUBLIC KEY BLOCK-----", start);
	if (end == -1) {
		return false;
	}

	QString key = body.mid(start, end - start);

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

	QString from = stanza.attribute("from");
	// Cut trash from gpg command output
	QString res = QString::fromUtf8(gpg.readAllStandardError());
	res = _stanzaSending->escape(res.mid(0, res.indexOf('\n')));
	_accountHost->appendSysMsg(account, from, res);

	// Don't hide message if an error occurred
	if (gpg.exitCode()) {
		return false;
	}

	if (!_optionHost->getPluginOption("hide-key-message", true).toBool()) {
		return false;
	}
	else {
		return true;
	}
}

QList<QVariantHash> GnuPG::getButtonParam()
{
	QList<QVariantHash> l;

	QVariantHash hash;
	hash["tooltip"] = QVariant(tr("Send GnuPG Public Key"));
	hash["icon"] = QVariant(QString("gnupg/icon"));
	hash["reciver"] = qVariantFromValue(qobject_cast<QObject *>(this));
	hash["slot"] = QVariant(SLOT(actionActivated()));
	l << hash;
	return l;
}

void GnuPG::actionActivated()
{
	if (_menu) {
		delete _menu;
	}

	_menu = new QMenu();

	Model *model = new Model(_menu);
	model->listKeys();

	for (int i = 0; i < model->rowCount(); i++) {
		if (model->item(i, Model::Type)->text() != "sec") {
			continue;
		}

		QString str;
		// User name
		if (!model->item(i, Model::Name)->text().isEmpty()) {
			str += model->item(i, Model::Name)->text();
		}

		// Comment
		if (!model->item(i, Model::Comment)->text().isEmpty()) {
			if (!str.isEmpty()) {
				str += " ";
			}
			str += QString("(%1)").arg(model->item(i, Model::Comment)->text());
		}

		// Email
		if (!model->item(i, Model::Email)->text().isEmpty()) {
			if (!str.isEmpty()) {
				str += " ";
			}
			str += QString("<%1>").arg(model->item(i, Model::Email)->text());
		}

		// Short ID
		if (!str.isEmpty()) {
			str += " ";
		}
		str += model->item(i, Model::ShortId)->text();

		QAction *action = _menu->addAction(str);
		action->setData(model->item(i, Model::Fingerprint)->text());
		connect(action, SIGNAL(triggered()), SLOT(sendPublicKey()));
	}

	_menu->popup(QCursor::pos());
}

void GnuPG::sendPublicKey()
{
	QAction *action = qobject_cast<QAction*>(sender());
	QString fingerprint = "0x" + action->data().toString();

	GpgProcess gpg;
	QStringList arguments;
	arguments << "--armor"
			  << "--export"
			  << fingerprint;

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

	// do nothing if error is occurred
	if (gpg.exitCode()) {
		return;
	}

	QString key = QString::fromUtf8(gpg.readAllStandardOutput());

	QString jid = _activeTab->getYourJid();
	QString jidToSend = _activeTab->getJid();
	int account = 0;
	QString tmpJid;
	while (jid != (tmpJid = _accountInfo->getJid(account))) {
		++account;
		if (tmpJid == "-1") {
			return;
		}
	}

	_stanzaSending->sendMessage(account, jidToSend, key, "", "chat");
	_accountHost->appendSysMsg(account, jidToSend, _stanzaSending->escape(QString(tr("Public key %1 sent")).arg(action->text())));
}
