/*
 * Copyright (C) 2003-2005  Justin Karneges <justin@affinix.com>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 *
 */

#include "gpgop_p.h"
#include "gpgop.h"
#include "gpgaction.h"

namespace gpgQCAPlugin {

//----------------------------------------------------------------------------
// GpgOp
//----------------------------------------------------------------------------
GpgOp::Private::Private(GpgOp *_q)
	: QObject(_q)
	, sync(_q)
	, q(_q)
	, act(0)
	, waiting(false)
{
	reset(ResetAll);
}

GpgOp::Private::~Private()
{
	reset(ResetAll);
}

void GpgOp::Private::reset(ResetMode mode)
{
	if(act)
	{
		act->disconnect(this);
		act->setParent(0);
		act->deleteLater();

		act = 0;
	}

	if(mode >= ResetSessionAndData)
	{
		output = GpgAction::Output();
		result.clear();
		diagnosticText = QString();
		eventList.clear();
	}

	if(mode >= ResetAll)
	{
		opt_ascii = false;
		opt_noagent = false;
		opt_alwaystrust = false;
		opt_pubfile = QString();
		opt_secfile = QString();
	}
}

void GpgOp::Private::make_act(GpgOp::Type _op)
{
	reset(ResetSessionAndData);

	op = _op;

	act = new GpgAction(this);

	connect(act, SIGNAL(readyRead()), SLOT(act_readyRead()));
	connect(act, SIGNAL(bytesWritten(int)), SLOT(act_bytesWritten(int)));
	connect(act, SIGNAL(needPassphrase(const QString &)), SLOT(act_needPassphrase(const QString &)));
	connect(act, SIGNAL(needCard()), SLOT(act_needCard()));
	connect(act, SIGNAL(finished()), SLOT(act_finished()));
	connect(act, SIGNAL(readyReadDiagnosticText()), SLOT(act_readyReadDiagnosticText()));

	act->input.bin = bin;
	act->input.op = op;
	act->input.opt_ascii = opt_ascii;
	act->input.opt_noagent = opt_noagent;
	act->input.opt_alwaystrust = opt_alwaystrust;
	act->input.opt_pubfile = opt_pubfile;
	act->input.opt_secfile = opt_secfile;
}

void GpgOp::Private::eventReady(const GpgOp::Event &e)
{
	eventList += e;
	sync.conditionMet();
}

void GpgOp::Private::eventReady(GpgOp::Event::Type type)
{
	GpgOp::Event e;
	e.type = type;
	eventReady(e);
}

void GpgOp::Private::eventReady(GpgOp::Event::Type type, int written)
{
	GpgOp::Event e;
	e.type = type;
	e.written = written;
	eventReady(e);
}

void GpgOp::Private::eventReady(GpgOp::Event::Type type, const QString &keyId)
{
	GpgOp::Event e;
	e.type = type;
	e.keyId = keyId;
	eventReady(e);
}

void GpgOp::Private::act_readyRead()
{
	if(waiting)
		eventReady(GpgOp::Event::ReadyRead);
	else
		emit q->readyRead();
}

void GpgOp::Private::act_bytesWritten(int bytes)
{
	if(waiting)
		eventReady(GpgOp::Event::BytesWritten, bytes);
	else
		emit q->bytesWritten(bytes);
}

void GpgOp::Private::act_needPassphrase(const QString &keyId)
{
	if(waiting)
		eventReady(GpgOp::Event::NeedPassphrase, keyId);
	else
		emit q->needPassphrase(keyId);
}

void GpgOp::Private::act_needCard()
{
	if(waiting)
		eventReady(GpgOp::Event::NeedCard);
	else
		emit q->needCard();
}

void GpgOp::Private::act_readyReadDiagnosticText()
{
	QString s = act->readDiagnosticText();
	//printf("dtext ready: [%s]\n", qPrintable(s));
	diagnosticText += s;

	if(waiting)
		eventReady(GpgOp::Event::ReadyReadDiagnosticText);
	else
		emit q->readyReadDiagnosticText();
}

void GpgOp::Private::act_finished()
{
#ifdef GPG_PROFILE
	if(op == GpgOp::Encrypt)
		printf("<< doEncrypt: %d >>\n", timer.elapsed());
#endif

	result = act->read();
	diagnosticText += act->readDiagnosticText();
	output = act->output;

	QMap<int, QString> errmap;
	errmap[GpgOp::ErrorProcess] = "ErrorProcess";
	errmap[GpgOp::ErrorPassphrase] = "ErrorPassphrase";
	errmap[GpgOp::ErrorFormat] = "ErrorFormat";
	errmap[GpgOp::ErrorSignerExpired] = "ErrorSignerExpired";
	errmap[GpgOp::ErrorEncryptExpired] = "ErrorEncryptExpired";
	errmap[GpgOp::ErrorEncryptUntrusted] = "ErrorEncryptUntrusted";
	errmap[GpgOp::ErrorEncryptInvalid] = "ErrorEncryptInvalid";
	errmap[GpgOp::ErrorDecryptNoKey] = "ErrorDecryptNoKey";
	errmap[GpgOp::ErrorUnknown] = "ErrorUnknown";
	if(output.success)
		diagnosticText += "GpgAction success\n";
	else
		diagnosticText += QString("GpgAction error: %1\n").arg(errmap[output.errorCode]);

	if(output.wasSigned)
	{
		QString s;
		if(output.verifyResult == GpgOp::VerifyGood)
			s = "VerifyGood";
		else if(output.verifyResult == GpgOp::VerifyBad)
			s = "VerifyBad";
		else
			s = "VerifyNoKey";
		diagnosticText += QString("wasSigned: verifyResult: %1\n").arg(s);
	}

	//printf("diagnosticText:\n%s", qPrintable(diagnosticText));

	reset(ResetSession);

	if(waiting)
		eventReady(GpgOp::Event::Finished);
	else
		emit q->finished();
}

GpgOp::GpgOp(const QString &bin, QObject *parent)
	:QObject(parent)
{
	d = new Private(this);
	d->bin = bin;
}

GpgOp::~GpgOp()
{
	delete d;
}

void GpgOp::reset()
{
	d->reset(ResetAll);
}

bool GpgOp::isActive() const
{
	return (d->act ? true : false);
}

GpgOp::Type GpgOp::op() const
{
	return d->op;
}

void GpgOp::setAsciiFormat(bool b)
{
	d->opt_ascii = b;
}

void GpgOp::setDisableAgent(bool b)
{
	d->opt_noagent = b;
}

void GpgOp::setAlwaysTrust(bool b)
{
	d->opt_alwaystrust = b;
}

void GpgOp::setKeyrings(const QString &pubfile, const QString &secfile)
{
	d->opt_pubfile = pubfile;
	d->opt_secfile = secfile;
}

void GpgOp::doCheck()
{
	d->make_act(Check);
	d->act->start();
}

void GpgOp::doSecretKeyringFile()
{
	d->make_act(SecretKeyringFile);
	d->act->start();
}

void GpgOp::doPublicKeyringFile()
{
	d->make_act(PublicKeyringFile);
	d->act->start();
}

void GpgOp::doSecretKeys()
{
	d->make_act(SecretKeys);
	d->act->start();
}

void GpgOp::doPublicKeys()
{
	d->make_act(PublicKeys);
	d->act->start();
}

void GpgOp::doEncrypt(const QStringList &recip_ids)
{
#ifdef GPG_PROFILE
	d->timer.start();
	printf("<< doEncrypt >>\n");
#endif

	d->make_act(Encrypt);
	d->act->input.recip_ids = recip_ids;
	d->act->start();
}

void GpgOp::doDecrypt()
{
	d->make_act(Decrypt);
	d->act->start();
}

void GpgOp::doSign(const QString &signer_id)
{
	d->make_act(Sign);
	d->act->input.signer_id = signer_id;
	d->act->start();
}

void GpgOp::doSignAndEncrypt(const QString &signer_id, const QStringList &recip_ids)
{
	d->make_act(SignAndEncrypt);
	d->act->input.signer_id = signer_id;
	d->act->input.recip_ids = recip_ids;
	d->act->start();
}

void GpgOp::doSignClearsign(const QString &signer_id)
{
	d->make_act(SignClearsign);
	d->act->input.signer_id = signer_id;
	d->act->start();
}

void GpgOp::doSignDetached(const QString &signer_id)
{
	d->make_act(SignDetached);
	d->act->input.signer_id = signer_id;
	d->act->start();
}

void GpgOp::doVerify()
{
	d->make_act(Verify);
	d->act->start();
}

void GpgOp::doVerifyDetached(const QByteArray &sig)
{
	d->make_act(VerifyDetached);
	d->act->input.sig = sig;
	d->act->start();
}

void GpgOp::doImport(const QByteArray &in)
{
	d->make_act(Import);
	d->act->input.inkey = in;
	d->act->start();
}

void GpgOp::doExport(const QString &key_id)
{
	d->make_act(Export);
	d->act->input.export_key_id = key_id;
	d->act->start();
}

void GpgOp::doDeleteKey(const QString &key_fingerprint)
{
	d->make_act(DeleteKey);
	d->act->input.delete_key_fingerprint = key_fingerprint;
	d->act->start();
}

#ifdef QPIPE_SECURE
void GpgOp::submitPassphrase(const QCA::SecureArray &a)
#else
void GpgOp::submitPassphrase(const QByteArray &a)
#endif
{
	d->act->submitPassphrase(a);
}

void GpgOp::cardOkay()
{
	d->act->cardOkay();
}

QByteArray GpgOp::read()
{
	if(d->act)
	{
		return d->act->read();
	}
	else
	{
		QByteArray a = d->result;
		d->result.clear();
		return a;
	}
}

void GpgOp::write(const QByteArray &in)
{
	d->act->write(in);
}

void GpgOp::endWrite()
{
	d->act->endWrite();
}

QString GpgOp::readDiagnosticText()
{
	QString s = d->diagnosticText;
	d->diagnosticText = QString();
	return s;
}

GpgOp::Event GpgOp::waitForEvent(int msecs)
{
	if(!d->eventList.isEmpty())
		return d->eventList.takeFirst();

	if(!d->act)
		return GpgOp::Event();

	d->waiting = true;
	d->sync.waitForCondition(msecs);
	d->waiting = false;
	if(!d->eventList.isEmpty())
		return d->eventList.takeFirst();
	else
		return GpgOp::Event();
}

bool GpgOp::success() const
{
	return d->output.success;
}

GpgOp::Error GpgOp::errorCode() const
{
	return d->output.errorCode;
}

GpgOp::KeyList GpgOp::keys() const
{
	return d->output.keys;
}

QString GpgOp::keyringFile() const
{
	return d->output.keyringFile;
}

QString GpgOp::homeDir() const
{
	return d->output.homeDir;
}

QString GpgOp::encryptedToId() const
{
	return d->output.encryptedToId;
}

bool GpgOp::wasSigned() const
{
	return d->output.wasSigned;
}

QString GpgOp::signerId() const
{
	return d->output.signerId;
}

QDateTime GpgOp::timestamp() const
{
	return d->output.timestamp;
}

GpgOp::VerifyResult GpgOp::verifyResult() const
{
	return d->output.verifyResult;
}

}
