#include"pgptest.h"

#include<qlayout.h>
#include<qmessagebox.h>
#include<qinputdialog.h>
#include<qapplication.h>
#include<qguardedptr.h>

static QByteArray stringToArray(const QString &s)
{
	QByteArray a;
	QCString cs = s.utf8();
	a.resize(cs.length());
	memcpy(a.data(), cs.data(), a.size());
	return a;
}

QString plain2rich(const QString &plain, bool nobr=false);

QString plain2rich(const QString &plain, bool nobr)
{
	QString rich;
	int col = 0;

	for(int i = 0; i < (int)plain.length(); ++i) {
		if(nobr && col == 0)
			rich += "<nobr>";

		if(plain[i] == '\n') {
			if(nobr)
				rich += "</nobr>";
			rich += "<br>";
			col = 0;
			continue;
		}
		else if(plain[i] == '\t') {
			rich += QChar::nbsp;
			while(col % 4) {
				rich += QChar::nbsp;
				++col;
			}
		}
		else if(plain[i].isSpace()) {
			if(i > 0 && plain[i-1] == ' ')
				rich += QChar::nbsp;
			else
				rich += ' ';
		}
		else if(plain[i] == '<')
			rich += "&lt;";
		else if(plain[i] == '>')
			rich += "&gt;";
		else if(plain[i] == '\"')
			rich += "&quot;";
		else if(plain[i] == '\'')
			rich += "&apos;";
		else if(plain[i] == '&')
			rich += "&amp;";
		else
			rich += plain[i];
		++col;
	}

	if(col > 0 && nobr)
		rich += "</nobr>";

	return rich;
}

//----------------------------------------------------------------------------
// View
//----------------------------------------------------------------------------
View::View(OpenPGP::Engine *_pgp, QWidget *parent)
:QDialog(parent, 0, false, WDestructiveClose)
{
	pix_key = new QPixmap("key.png");
	pgp = _pgp;
	connect(pgp, SIGNAL(initFinished(bool, const QString &)), SLOT(pgp_initFinished(bool, const QString &)));

	QVBoxLayout *vb = new QVBoxLayout(this, 4);
	lv = new QListView(this);
	vb->addWidget(lv);
	QHBoxLayout *hb = new QHBoxLayout(vb);
	te = new QTextEdit(this);
	hb->addWidget(te);
	te2 = new QTextEdit(this);
	hb->addWidget(te2);
	hb = new QHBoxLayout(vb);
	hb->addStretch(1);

	pb_encrypt = new QPushButton("&Encrypt", this);
	connect(pb_encrypt, SIGNAL(clicked()), SLOT(encrypt()));
	hb->addWidget(pb_encrypt);

	pb_decrypt = new QPushButton("&Decrypt", this);
	connect(pb_decrypt, SIGNAL(clicked()), SLOT(decrypt()));
	hb->addWidget(pb_decrypt);

	pb_sign = new QPushButton("&Sign", this);
	connect(pb_sign, SIGNAL(clicked()), SLOT(sign()));
	hb->addWidget(pb_sign);

	pb_verify = new QPushButton("&Verify", this);
	connect(pb_verify, SIGNAL(clicked()), SLOT(verify()));
	hb->addWidget(pb_verify);

	QPushButton *pb = new QPushButton("&Close", this);
	connect(pb, SIGNAL(clicked()), SLOT(reject()));
	hb->addWidget(pb);

	lv->addColumn("Key ID");
	lv->addColumn("User ID");
	lv->setAllColumnsShowFocus(true);
	lv->setResizeMode(QListView::LastColumn);
	resize(600,400);

	lv->setSelectionMode(QListView::Extended);

	disableButtons();
	pgp->init();
}

View::~View()
{
	delete pgp;
	delete pix_key;
}

void View::enableButtons()
{
	pb_encrypt->setEnabled(true);
	pb_decrypt->setEnabled(true);
	pb_sign->setEnabled(true);
	pb_verify->setEnabled(true);
}

void View::disableButtons()
{
	pb_encrypt->setEnabled(false);
	pb_decrypt->setEnabled(false);
	pb_sign->setEnabled(false);
	pb_verify->setEnabled(false);
}

void View::pgp_initFinished(bool ok, const QString &str)
{
	if(!ok) {
		QMessageBox::information(this, tr("Error"), tr("Unable to load %1.\nReason: %2").arg(pgp->name()).arg(str));
		close();
	}
	else {
		enableButtons();
		connect(pgp, SIGNAL(keysUpdated()), SLOT(pgp_keysUpdated()));
		loadKeys();
	}
}

void View::encrypt()
{
	QStringList recipients;
	for(QListViewItem *i = lv->firstChild(); i; i = i->nextSibling()) {
		if(i->isSelected())
			recipients += i->text(0);
	}
	if(recipients.isEmpty()) {
		QMessageBox::information(this, "Encrypt", "You must select a public key to encrypt with.");
		return;
	}

	OpenPGP::Request *r = new OpenPGP::Request(pgp);
	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
	disableButtons();
	r->encrypt(stringToArray(te->text()), recipients);
}

void View::decrypt()
{
	OpenPGP::Request *r = new OpenPGP::Request(pgp);
	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
	connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
	disableButtons();
	r->decrypt(te->text());
}

void View::sign()
{
	OpenPGP::Request *r = new OpenPGP::Request(pgp);
	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
	connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
	disableButtons();
	QString defkey = pgp->secretKeys().first().keyID();
	r->sign(stringToArray(te->text()), defkey);
}

void View::verify()
{
	OpenPGP::Request *r = new OpenPGP::Request(pgp);
	connect(r, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
	disableButtons();
	r->verify(stringToArray(te->text()), te2->text());
}

void View::pgp_finished(bool ok)
{
	OpenPGP::Request *r = (OpenPGP::Request *)sender();
	if(r->type() == OpenPGP::Encrypt) {
		if(!ok)
			QMessageBox::information(this, tr("Encrypt"), tr("Encountered an error while encrypting."));
		else
			te2->setText(r->encrypted());
	}
	else if(r->type() == OpenPGP::Decrypt) {
		if(!ok)
			QMessageBox::information(this, tr("Encrypt"), tr("Encountered an error while decrypting."));
		else {
			QByteArray dec = r->decrypted();
			te2->setText(QString::fromUtf8(dec.data(), dec.size()));
		}
	}
	else if(r->type() == OpenPGP::Sign) {
		if(!ok)
			QMessageBox::information(this, tr("Sign"), tr("Encountered an error while signing."));
		else
			te2->setText(r->signature());
	}
	else if(r->type() == OpenPGP::Verify) {
		int x = r->verifyResult();
		QString keyID = r->keyID();
		if(x == OpenPGP::VerifyGood)
			QMessageBox::information(this, "Verify", tr("<nobr>GOOD signature from <b>%1</b>.</nobr>").arg(keyID));
		else if(x == OpenPGP::VerifyBad)
			QMessageBox::information(this, "Verify", tr("<nobr>BAD signature from <b>%1</b>.</nobr>").arg(keyID));
		else if(x == OpenPGP::VerifyNoKey)
			QMessageBox::information(this, "Verify", tr("<nobr>Cannot verify without key <b>%1</b>.</nobr>").arg(keyID));
		else
			QMessageBox::information(this, tr("Verify"), tr("Encountered an error while verifying."));
	}

	r->deleteLater();
	enableButtons();
}

void View::pgp_needPassphrase()
{
	QGuardedPtr<OpenPGP::Request> r = (OpenPGP::Request *)sender();
	bool ok;
	QString pp = QInputDialog::getText("Passphrase", "Please enter your passphrase:", QLineEdit::Password, QString::null, &ok, this);
	if(r) {
		if(ok)
			r->submitPassphrase(pp);
		else {
			r->deleteLater();
			enableButtons();
		}
	}
}

void View::pgp_keysUpdated()
{
	loadKeys();
}

void View::loadKeys()
{
	// display the keys
	OpenPGP::KeyList pubkeys = pgp->publicKeys();
	lv->clear();
	for(OpenPGP::KeyList::ConstIterator it = pubkeys.begin(); it != pubkeys.end(); ++it) {
		const OpenPGP::Key &key = *it;
		QListViewItem *lvi = new QListViewItem(lv);
		lvi->setPixmap(0, *pix_key);
		lvi->setText(0, key.keyID());
		lvi->setText(1, key.userID());
	}
}


//----------------------------------------------------------------------------
// main
//----------------------------------------------------------------------------
int main(int argc, char **argv)
{
	QApplication app(argc, argv);

	OpenPGP::Engine *pgp = OpenPGP::createEngine("gpg");
	if(!pgp->checkAvailability()) {
		QMessageBox::information(0, QObject::tr("Error"), QObject::tr("%1 is not available.").arg(pgp->name()));
		return 0;
	}

	View *v = new View(pgp, 0);
	app.setMainWidget(v);
	v->show();
	app.exec();
	return 0;
}
