/*
 *  Copyright (C) 2007-2008 CMM Sigma Andrzej Chmielowiec <cmmsigma@cmmsigma.eu>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License version 3
 *  as published by the Free Software Foundation.
 *
 *  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.
 */

#include <QtGui>

#include "MainWindow.h"
#include "ConnectAction.h"
#include "VpnItem.h"
#include "VpnNewWindow.h"
#include "VpnInfoWindow.h"
#include "VpnEditWindow.h"
#include "AboutBox.h"

#ifndef STOKEN_SOFT
#include "crd_ut10xx.h"
#endif

extern QString lang;

static const int StateClear = 0;
static const int StateInit = 1;
static const int StateConf = 2;
static const int StateLock = 3;

#ifndef STOKEN_SOFT
static int
sTokenState(CRD_UT10XX_t *ut)
{
	if (ut->info.info & 0x04)
		return StateLock;
	
	else if (ut->info.info & 0x02)
		return StateConf;
	
	else if (ut->info.info & 0x01)
		return StateInit;
	
	else
		return StateClear;
}
#endif

MainWindow::MainWindow(QWidget *parent): QWidget(parent)
{
	createActions();
	createTrayIcon();
	createAboutBox();
	mTrayIcon->show();
	
#ifndef STOKEN_SOFT
	setWindowTitle(tr("sToken VPN"));
#else
	setWindowTitle(tr("sToken SoftVPN"));
#endif
	
	resize(400, 300);
	
	mIsConnected = false;
	mIsInitialized = false;
	mVpn = new QProcess(this);
	connect(mVpn, SIGNAL(readyReadStandardError()), this, SLOT(vpnOutputSlot()));
	connect(mVpn, SIGNAL(readyReadStandardOutput()), this, SLOT(vpnOutputSlot()));
	connect(mVpn, SIGNAL(finished(int, QProcess::ExitStatus)), this, 
		SLOT(vpnFinishSlot(int, QProcess::ExitStatus)));
	
	mVpnActiveItem = 0;
	mVpnConnectItem = 0;
	
	QGridLayout *layout = new QGridLayout;
	QLabel *label = new QLabel();
	label->setPixmap(QPixmap(QString(":img/stoken_logo_120_60.png")));
	label->setAlignment(Qt::AlignRight);
	layout->addWidget(label, 0, 0);
	layout->addWidget(createVpnSessions(), 1, 0);
	layout->addWidget(createVpnInfo(), 2, 0);
	
	setLayout(layout);
	
	readConfFiles();
}

void
MainWindow::closeEvent(QCloseEvent *event)
{
	if (mTrayIcon->isVisible())
	{
		hide();
		event->ignore();
	}
}

void
MainWindow::vpnConnectSlot()
{
	if (!isVisible())
		show();
	
	mVpnOutput->clear();
	
	QStringList arguments;
	
	// Get PIN and check if it is correct.
	bool ok;
	QString pin = QInputDialog::getText(this, tr("Enter private key password"),
		tr("Password:"), QLineEdit::Password, QString(), &ok);
	
	if (!ok)
		return;
	
#ifndef STOKEN_SOFT
	if ((CRD_UT10XX_lib_init() < 0) || (CRD_UT10XX_lib_find_devices() < 0)
		|| (CRD_UT10XX_lib_actualize_devices() < 0))
	{
		QMessageBox::critical(this, tr("Error"), tr("Cannot initialize token library!"));
		qApp->quit();
	}
	
	QString tokenSerial = mVpnActiveItem->key();
	tokenSerial.chop(tokenSerial.size() - tokenSerial.indexOf('/'));
	tokenSerial.remove(0, tokenSerial.lastIndexOf(':') + 1);
	
	CRD_UT10XX_t *ut;
	
	if ((ut = CRD_UT10XX_find_by_serial_number(tokenSerial.toUtf8().data())) == 0)
	{
		QMessageBox::critical(this, tr("Error"), tr("Token supporting this connection is not found!"));
		CRD_UT10XX_lib_finish();
		return;
	}
	
	if (sTokenState(ut) != StateConf)
	{
		QMessageBox::critical(this, tr("Error"), tr("Token supporting this connection is not ready!"));
		CRD_UT10XX_lib_finish();
		return;
	}
	
	CRD_UT10XX_session_open(ut);
	if (CRD_UT10XX_cmd_try_pin(ut, pin.toUtf8().data(), CRD_UT10XX_USER_PIN_ID) < 0)
	{
		QMessageBox::critical(this, tr("Error"), tr("Incorrect password."));
		CRD_UT10XX_session_close(ut);
		CRD_UT10XX_lib_finish();
		return;
	}
	CRD_UT10XX_session_close(ut);
	
	CRD_UT10XX_lib_finish();
#endif
	
	// Set up arguments for VPN client.
	arguments << "--config" << "conf";
	arguments << "--dev" << "tun";
	arguments << "--float";
	arguments << "--pull";
	arguments << "--tls-client";
	arguments << "--engkey";
#ifndef STOKEN_SOFT
	arguments << "--engine" << QString("stoken:log_file:log:user_pin:").append(pin);
#else
	arguments << "--engine" << QString("stokensoft:user_pin:").append(pin);
#endif
	
	mVpn->setWorkingDirectory(QDir::current().path() + QDir::separator() + mVpnActiveItem->confName());
	
	// Start VPN client.
#ifdef Q_WS_WIN
	mVpn->start(QCoreApplication::applicationDirPath().append(QString("/../bin/stoken_openvpn.exe")), 
		arguments);
#else
	mVpn->start(QCoreApplication::applicationDirPath().append(QString("/../bin/stoken_openvpn")), 
		arguments);
#endif
	
	if (!mVpn->waitForStarted())
	{
		QMessageBox::critical(this, tr("Error"), tr("Cannot initialize VPN connection!"));
		return;
	}
	
	mIsConnected = true;
	mConnectMenu->setEnabled(!mIsConnected);
	mDisConnectAction->setEnabled(mIsConnected);
	mConnectInfoAction->setEnabled(mIsConnected);
	
	mVpnActiveItem->setIsConnected(true);
	mVpnConnectItem = mVpnActiveItem;
	vpnItemActiveSlot(mVpnActiveItem);
	
	for (int i = 0; i < mVpnList->count(); i++)
	{
		VpnItem *item = static_cast<VpnItem*>(mVpnList->item(i));
		
		if (mVpnConnectItem == item)
			continue;
		
		item->setIsLock(true);
	}
}

void
MainWindow::vpnDisConnectSlot()
{
	if (mVpn->state() != QProcess::NotRunning)
		mVpn->kill();
}

void
MainWindow::vpnFinishSlot(int exitCode, QProcess::ExitStatus exitStatus)
{
	exitCode = 0;
	exitStatus = QProcess::NormalExit;
	
#	ifdef Q_WS_WIN
	mTrayIcon->setIcon(QIcon(QString(":img/vpn_dis_16.png")));
#	else
	mTrayIcon->setIcon(QIcon(QString(":img/vpn_dis_22.png")));
#	endif
	mTrayIcon->setToolTip(tr("sToken VPN - no connection"));
	mTrayIcon->showMessage(tr("Disconnected"), tr("VPN connection was closed."));
	
	mIsConnected = false;
	mIsInitialized = false;
	mConnectMenu->setEnabled(!mIsConnected);
	mDisConnectAction->setEnabled(mIsConnected);
	mConnectInfoAction->setEnabled(mIsConnected);
	
	mVpnConnectItem->setIsConnected(false);
	mVpnConnectItem->setIcon(QIcon(QString(":img/vpn_22.png")));
	vpnItemActiveSlot(mVpnActiveItem);
	
	for (int i = 0; i < mVpnList->count(); i++)
	{
		VpnItem *item = static_cast<VpnItem*>(mVpnList->item(i));
		
		if (mVpnConnectItem == item)
			continue;
		
		item->setIsLock(false);
	}
	
	mVpnConnectItem = 0;
}

void
MainWindow::vpnOutputSlot()
{
	mVpnOutput->append(QString(mVpn->readAll()));
	
	if (!mIsInitialized)
	{
		QString output = mVpnOutput->toPlainText();
		
		if (output.indexOf(QString("Initialization Sequence Completed")) != -1)
		{
			mIsInitialized = true;
			mVpnConnectItem->setIcon(QIcon(QString(":img/vpn_con_22.png")));
#ifdef Q_WS_WIN
			mTrayIcon->setIcon(QIcon(QString(":img/vpn_con_16.png")));
#else
			mTrayIcon->setIcon(QIcon(QString(":img/vpn_con_22.png")));
#endif
			mTrayIcon->setToolTip(tr("sToken VPN - %1").arg(mVpnConnectItem->sTokenVpnName()));
			mTrayIcon->showMessage(tr("Connected"), tr("VPN connection was initialized properly."));
		}
	}
}

void
MainWindow::vpnItemActiveSlot(QListWidgetItem *item)
{
	mVpnActiveItem = static_cast<VpnItem*>(item);
	
	mVpnInfo->setEnabled(true);
	
	if (mIsConnected)
	{
		mVpnNew->setEnabled(false);
		mVpnEdit->setEnabled(false);
		mVpnDelete->setEnabled(false);
	}
	else
	{
		mVpnNew->setEnabled(true);
		mVpnEdit->setEnabled(true);
		mVpnDelete->setEnabled(true);
	}
	
	if (mVpnActiveItem->isLock())
	{
		mVpnConnect->setEnabled(false);
		mVpnDisConnect->setEnabled(false);
	}
	else if (mVpnActiveItem->isConnected())
	{
		mVpnConnect->setEnabled(false);
		mVpnDisConnect->setEnabled(true);
	}
	else
	{
		mVpnConnect->setEnabled(true);
		mVpnDisConnect->setEnabled(false);
	}
}

void
MainWindow::vpnNewSlot()
{
	VpnNewWindow *dialog = new VpnNewWindow(this);
	dialog->exec();
	
	if (dialog->result() == QDialog::Accepted)
		readConfFiles();
	
	delete dialog;
}

void
MainWindow::vpnInfoSlot()
{
	VpnInfoWindow *dialog = new VpnInfoWindow(mVpnActiveItem, this);
	
	if (!isVisible())
		show();
	
	dialog->exec();
	delete dialog;
}

void
MainWindow::vpnEditSlot()
{
	VpnEditWindow *dialog = new VpnEditWindow(mVpnActiveItem, this);
	dialog->exec();
	
	if (dialog->result() == QDialog::Accepted)
		readConfFiles();
	
	delete dialog;
}

void
MainWindow::vpnDeleteSlot()
{
	int button = QMessageBox::question(this, tr("Question"), tr("Do you want to delete "
		"selected VPN connection?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No);
	
	if (button == QMessageBox::No)
		return;
	
	QDir confDir(QDir::current().path() + QString("/") + mVpnActiveItem->confName());
	QStringList files(confDir.entryList(QDir::Files));
	
	for (int i = 0; i < files.size(); i++)
		confDir.remove(files[i]);
	
	QDir::current().rmdir(mVpnActiveItem->confName());
	
	readConfFiles();
}

void
MainWindow::aboutSlot()
{
	if (!isVisible())
		show();
	
	mAboutBox->setActiveAbout();
	mAboutBox->exec();
}

void
MainWindow::trayIconActive(QSystemTrayIcon::ActivationReason reason)
{
	if (reason == QSystemTrayIcon::Trigger)
	{
		if (isVisible()) hide();
		else show();
	}
}

void
MainWindow::exit()
{
	if (mVpn->state() != QProcess::NotRunning)
		mVpn->kill();
	
	qApp->quit();
}

void
MainWindow::langEnSlot()
{
	lang = "";
	langExclamation();
}

void
MainWindow::langPlSlot()
{
	lang = "pl";
	langExclamation();
}

QGroupBox*
MainWindow::createVpnSessions()
{
	QGroupBox *box = new QGroupBox(tr("Available VPN connections"));
	QGridLayout *layout = new QGridLayout;
	
	mVpnList = new QListWidget;
	mVpnList->setSpacing(3);
	connect(mVpnList, SIGNAL(itemClicked(QListWidgetItem*)), this, 
		 SLOT(vpnItemActiveSlot(QListWidgetItem*)));
	
	mVpnConnect = new QPushButton(tr("&Connect"));
	mVpnConnect->setEnabled(false);
	connect(mVpnConnect, SIGNAL(clicked()), this, SLOT(vpnConnectSlot()));
	
	mVpnDisConnect = new QPushButton(tr("&Disconnect"));
	mVpnDisConnect->setEnabled(false);
	connect(mVpnDisConnect, SIGNAL(clicked()), this, SLOT(vpnDisConnectSlot()));
	
	mVpnNew = new QPushButton(tr("&New connection"));
	connect(mVpnNew, SIGNAL(clicked()), this, SLOT(vpnNewSlot()));
	
	mVpnInfo = new QPushButton(tr("&Information"));
	mVpnInfo->setEnabled(false);
	connect(mVpnInfo, SIGNAL(clicked()), this, SLOT(vpnInfoSlot()));
	
	mVpnEdit = new QPushButton(tr("&Edit"));
	mVpnEdit->setEnabled(false);
	connect(mVpnEdit, SIGNAL(clicked()), this, SLOT(vpnEditSlot()));
	
	mVpnDelete = new QPushButton(tr("De&lete"));
	mVpnDelete->setEnabled(false);
	connect(mVpnDelete, SIGNAL(clicked()), this, SLOT(vpnDeleteSlot()));
	
	layout->addWidget(mVpnList, 0, 0, 7, 1);
	layout->addWidget(mVpnConnect, 0, 1);
	layout->addWidget(mVpnDisConnect, 1, 1);
	layout->addWidget(mVpnNew, 3, 1);
	layout->addWidget(mVpnInfo, 4, 1);
	layout->addWidget(mVpnEdit, 5, 1);
	layout->addWidget(mVpnDelete, 6, 1);
	
	layout->setRowStretch(2, 0);
	
	box->setLayout(layout);
	
	return box;
}

QGroupBox*
MainWindow::createVpnInfo()
{
	QGroupBox *box = new QGroupBox(tr("VPN connection log"));
	QGridLayout *layout = new QGridLayout;
	
	mVpnOutput = new QTextEdit(this);
	mVpnOutput->setReadOnly(true);
	layout->addWidget(mVpnOutput, 0, 0);
	
	box->setLayout(layout);
	
	return box;
}

void
MainWindow::readConfFiles()
{
	QStringList confDirs(QDir::current().entryList(QDir::Dirs | QDir::NoDotAndDotDot));
	
	mVpnInfo->setEnabled(false);
	mVpnEdit->setEnabled(false);
	mVpnConnect->setEnabled(false);
	mVpnDisConnect->setEnabled(false);
	mVpnDelete->setEnabled(false);
	
	mVpnList->clear();
	mConnectMenu->clear();
	
	for (int i = 0; i < confDirs.size(); i++)
	{
		VpnItem *item = new VpnItem(QDir::current().filePath(confDirs[i]).append(QString("/conf")), 
			confDirs[i]);
		ConnectAction *action = new ConnectAction(item, this);
		mVpnList->addItem(item);
		mConnectMenu->addAction(action);
		connect(action, SIGNAL(vpnItemActiveSig(QListWidgetItem*)), this, 
			SLOT(vpnItemActiveSlot(QListWidgetItem*)));
		connect(action, SIGNAL(vpnConnectSig()), this, SLOT(vpnConnectSlot()));
	}
}

void
MainWindow::langExclamation()
{
	if (!isVisible())
		show();
	
	QMessageBox::information(this, tr("Information"), tr("Application language was "
		"changed. Please restart application to apply changes."));
}

void
MainWindow::createActions()
{
	mOpenAction = new QAction(QIcon(QString(":img/restore_22.png")), tr("&Open"), this);
	connect(mOpenAction, SIGNAL(triggered()), this, SLOT(show()));
	
	mDisConnectAction = new QAction(QIcon(QString(":img/vpn_dis_22.png")), tr("&Disconnect"), this);
	mDisConnectAction->setEnabled(false);
	connect(mDisConnectAction, SIGNAL(triggered()), this, SLOT(vpnDisConnectSlot()));
	
	mConnectInfoAction = new QAction(tr("Connection deta&ils"), this);
	mConnectInfoAction->setEnabled(false);
	connect(mConnectInfoAction, SIGNAL(triggered()), this, SLOT(vpnInfoSlot()));
	
	mAboutAction = new QAction(QIcon(QString(":img/info_22.png")), tr("About"), this);
	connect(mAboutAction, SIGNAL(triggered()), this, SLOT(aboutSlot()));
	
	mExitAction = new QAction(QIcon(QString(":img/exit_22.png")), tr("E&xit"), this);
	connect(mExitAction, SIGNAL(triggered()), this, SLOT(exit()));
	
	mLang = new QActionGroup(this);
	mLangEn = new QAction(tr("English"), this);
	mLangEn->setCheckable(true);
	connect(mLangEn, SIGNAL(triggered()), this, SLOT(langEnSlot()));
	mLangPl = new QAction(tr("Polish"), this);
	mLangPl->setCheckable(true);
	connect(mLangPl, SIGNAL(triggered()), this, SLOT(langPlSlot()));
	
	mLang->addAction(mLangEn);
	mLang->addAction(mLangPl);
}

void
MainWindow::createTrayIcon()
{
	mTrayIconMenu = new QMenu(this);
	mTrayIconMenu->addAction(mOpenAction);
	mTrayIconMenu->addSeparator();
	mConnectMenu = mTrayIconMenu->addMenu(QIcon(QString(":img/vpn_con_22.png")), tr("&Connect"));
	mTrayIconMenu->addAction(mDisConnectAction);
	mTrayIconMenu->addAction(mConnectInfoAction);
	mTrayIconMenu->addSeparator();
	
	QMenu *language;
	language = mTrayIconMenu->addMenu(tr("&Language"));
	language->addAction(mLangEn);
	language->addAction(mLangPl);
	if(lang == "pl")
		mLangPl->setChecked(true);
	else
		mLangEn->setChecked(true);
	
	mTrayIconMenu->addSeparator();
	mTrayIconMenu->addAction(mAboutAction);
	mTrayIconMenu->addSeparator();
	mTrayIconMenu->addAction(mExitAction);
	
	mTrayIcon = new QSystemTrayIcon(this);
	mTrayIcon->setContextMenu(mTrayIconMenu);
#ifdef Q_WS_WIN
	mTrayIcon->setIcon(QIcon(QString(":img/vpn_dis_16.png")));
#else
	mTrayIcon->setIcon(QIcon(QString(":img/vpn_dis_22.png")));
#endif
	mTrayIcon->setToolTip(tr("sToken VPN - disconnected"));
	connect(mTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, \
		SLOT(trayIconActive(QSystemTrayIcon::ActivationReason)));
	
#ifndef STOKEN_SOFT
	setWindowIcon(QIcon(QString(":img/stoken_vpn_032.png")));
#else
	setWindowIcon(QIcon(QString(":img/stoken_softvpn_032.png")));
#endif
}

void
MainWindow::createAboutBox()
{
	mAboutBox = new AboutBox(this);
	
	// About informations.
#ifndef STOKEN_SOFT
	mAboutBox->setAboutPixmap(QPixmap(QString(":img/stoken_about_vpn_384.png")));
	mAboutBox->setAboutInfo(tr("<h2>sToken VPN v1.2</h2>") +
#else
	mAboutBox->setAboutPixmap(QPixmap(QString(":img/stoken_about_softvpn_384.png")));
	mAboutBox->setAboutInfo(tr("<h2>sToken SoftVPN v1.2</h2>") +
#endif
	tr("<br>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 3 of the License, or (at your option) any "
	"later version.<br><br>") +
#ifndef STOKEN_SOFT
	tr("<a href=\"http://www.stoken.eu/index.en.html\">http://www.stoken.eu</a>")
#else
	tr("<a href=\"http://www.stoken.eu/soft/index.en.html\">http://www.stoken.eu/soft</a>")
#endif
	);
	
	// Help informations.
#ifndef STOKEN_SOFT
	mAboutBox->setHelpPixmap(QPixmap(QString(":img/stoken_about_vpn_384.png")));
	mAboutBox->setHelpInfo(tr("<h2>sToken VPN v1.2</h2>") +
#else
	mAboutBox->setHelpPixmap(QPixmap(QString(":img/stoken_about_softvpn_384.png")));
	mAboutBox->setHelpInfo(tr("<h2>sToken SoftVPN v1.2</h2>") +
#endif
	tr("<br>Online instructions to this application are avaliable on the following "
	"website: <a href=\"http://www.stoken.eu/products/man_vpn/index.en.html\">"
	"http://www.stoken.eu</a>"));
	
	// License.
	QFile file(QString(":LICENSE"));
	file.open(QIODevice::ReadOnly);
	QByteArray license = file.readAll();
	file.close();
	mAboutBox->setLicense(QString(license.data()));
	
	// Thanks.
#ifndef STOKEN_SOFT
	mAboutBox->setThanksPixmap(QPixmap(QString(":img/stoken_about_vpn_384.png")));
#else
	mAboutBox->setThanksPixmap(QPixmap(QString(":img/stoken_about_softvpn_384.png")));
#endif
	
	mAboutBox->setThanksInfo(tr("<h4>Thanks to</h4>"
		"Andrzej Chmielowiec, Wiktor Kazana"
		"<h4>Special thanks</h4>"
		"Special thanks to all people who participate in development process of "
		"<a href=\"www.openssl.org\">OpenSSL</a> library and "
		"<a href=\"www.openvpn.net\">OpenVPN</a> software. Both of this projects "
		"are crucial for this application."
		));
}
