#include "CUpdateDialog.h"
#include <QrfeProgressDialog>

#include "../../permission/Permissions.h"
#include "../../update/FirmwareUpdateManager.h"

#include <QrfeReaderInterface>
#include <reader/QrfeRfeReader>
#include <update/QrfeRfeUpdateController>


CUpdateDialog::CUpdateDialog(FirmwareUpdateManager* updateManager, QWidget *parent)
	: QDialog(parent)
	, QrfeTraceModule("CUpdateDialog")
	, m_updateManager(updateManager)
	, m_deviceType(QrfeGlobal::DEVICE_Serial)
	, m_hardwareRevision(0)
	, m_reader(0)
	, m_readerType(0)
	, m_softwareRevision(0)
	, m_updateActive(false)
	, m_updateController(0)
{
	trc(1, "Starting...");
	ui.setupUi(this);

	connect(ui.pB_update,			SIGNAL( clicked() )	,			this, SLOT( updateReader() ));
	connect(ui.pB_switch2Firmware,	SIGNAL( clicked() )	,			this, SLOT( switch2Firmware() ));
	connect(ui.pB_cancel,			SIGNAL( clicked() )	,			this, SLOT( reject() ));

	ui.updateInfoWidget->setVisible(false);

	QTableWidgetItem* item;
	QFont f;

	ui.readerInfoTable->setRowCount(4);
	ui.readerInfoTable->setColumnCount(2);
	ui.readerInfoTable->setColumnWidth(0, 200);

	ui.readerInfoTable->setItem( 0, 0, item = new QTableWidgetItem("Reader Serial"));
	f = item->font();
	f.setBold(true);
	item->setFont(f);

	ui.readerInfoTable->setItem( 1, 0, item = new QTableWidgetItem("Reader Type"));
	item->setFont(f);
	ui.readerInfoTable->setItem( 2, 0, item = new QTableWidgetItem("Hardware Revision"));
	item->setFont(f);
	ui.readerInfoTable->setItem( 3, 0, item = new QTableWidgetItem("Current Software Revision"));
	item->setFont(f);
}

CUpdateDialog::~CUpdateDialog()
{

}

int CUpdateDialog::exec(QrfeReaderInterface* ph)
{
	m_reader = dynamic_cast<QrfeRfeReader*> (ph);

	if(m_reader->currentSystem() == QrfeGlobal::FIRMWARE)
		return QDialog::Accepted;

	QrfeProgressDialog* pb = new QrfeProgressDialog("Initializing update dialog...", 2, qobject_cast<QWidget*>(parent()));
	pb->show();

	m_updateController = new QrfeRfeUpdateController(m_reader, this);
	connect(m_updateController, 	SIGNAL(progressChanged(int, int)),
			this, 					  SLOT(setProgress(int, int)));

	ui.pB_cancel->setEnabled(true);
	ui.pB_switch2Firmware->setEnabled(true);
	ui.pB_update->setEnabled(true);
	ui.proB_progress->setVisible(false);

	m_updateActive = false;
	ui.pB_update->setText("Start Update");

	ui.tabWidget->setTabEnabled(ui.tabWidget->indexOf(ui.bootloaderTab), Permissions::d->readerUpdateBootloader());
	if(!Permissions::d->readerUpdateBootloader())
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(ui.bootloaderTab), "");
	else
		ui.tabWidget->setTabText(ui.tabWidget->indexOf(ui.bootloaderTab), "Bootloader");
	ui.tabWidget->setCurrentIndex(0);

	pb->increasePorgressBar();

	m_deviceName 			= m_reader->deviceName();
	m_deviceType 			= m_reader->deviceType();
	m_readerType 			= m_reader->readerTypeExact();
	m_hardwareRevision		= m_reader->hardwareRevision();
	m_softwareRevision		= m_reader->softwareRevision();
	m_bootloaderRevision 	= m_reader->bootloaderRevision();

	loadReaderInformation();
	loadFirmwareRepository();
	loadTestFirmwareRepository();
	loadBootloaderRepository();

	pb->increasePorgressBar();

	pb->hide();
	delete pb;

	return QDialog::exec();
}

void CUpdateDialog::tryAccept(bool isBootloader)
{ 
	if(!isBootloader)
	{
		QList<QVariant> params;
		params.append(m_reader->readerId());
		params.append((uint)m_reader->deviceType());
		params.append(m_reader->deviceName());
		params.append((uint)m_reader->readerCaps());
		emit appendGlobalJob(JOB_SwitchReaderSystemWithoutCommand, params);
	}
	else
	{
		QList<QVariant> params;

		params.clear();
		params.append("Do not disconnect the reader from power!");
		params.append((uint)1000);
		params.append(QVariant::fromValue(qobject_cast<QWidget*>(this->parentWidget())));
		emit appendGlobalJob(JOB_WaitForSpeciifedTimeWithDialog, params);

		params.clear();
		params.append(m_reader->readerId());
		params.append((uint)m_reader->deviceType());
		params.append(m_reader->deviceName());
		params.append((uint)m_reader->readerCaps());
		emit appendGlobalJob(JOB_SwitchReaderSystemWithoutCommandAndShowSettings, params);
	}


	QDialog::accept();
}

void CUpdateDialog::done ( int r )
{
	delete m_updateController;

	QDialog::done(r);
}

void CUpdateDialog::switch2Firmware()
{
	ui.pB_cancel->setEnabled(false);
	ui.pB_update->setEnabled(false);

	QList<QVariant> params;
	params.append(m_reader->readerId());
	emit appendGlobalJob(JOB_SwitchReaderToFirmware, params);

	QDialog::accept();
}

void CUpdateDialog::updateReader()
{
	if(m_updateActive)
	{
		m_updateController->stopCurrentUpdate();
		return;
	}

	trc(2, "Starting Reader Update...");

	QTreeWidget* tree = 0;
	bool isBootloader = false;
	if(ui.tabWidget->currentWidget() == ui.productiveTab)
		tree = ui.firmwareRepository;
	else if(ui.tabWidget->currentWidget() == ui.testTab)
		tree = ui.testFirmwareRepository;
	else if(ui.tabWidget->currentWidget() == ui.bootloaderTab)
	{
		isBootloader = true;
		tree = ui.bootloaderRepository;
	}
	else
		return;

	if(tree->selectedItems().size() == 0)
	{
		QMessageBox::information(this, this->windowTitle(), "No firmware selected.");
		return;
	}

	QTreeWidgetItem* item = tree->selectedItems().at(0);
	if(item->parent() == 0)
	{
		QMessageBox::information(this, this->windowTitle(), "No firmware selected.");
		return;
	}

	if(isBootloader)
	{
		QMessageBox::StandardButton btn = QMessageBox::warning(this, this->windowTitle(),
				"CAUTION:\n\n"
				"Do not disconnect the reader from power while and after update.\n"
				"Otherwise the reader firmware can be corrupted.", QMessageBox::Ok | QMessageBox::Cancel);
		if(btn != QMessageBox::Ok)
			return;
	}


	// Prepare GUI for update
	ui.pB_update->setText("Stop Update");
	ui.pB_switch2Firmware->setEnabled(false);
	ui.pB_cancel->setEnabled(false);
	ui.proB_progress->setVisible(true);
	ui.proB_progress->setMaximum(1);
	ui.proB_progress->setValue(0);

    quint64 infoData = item->data(0, Qt::UserRole).toULongLong();
    QFileInfo imageInfo = m_updateManager->getImageFile((quint32)(infoData>>32), m_hardwareRevision, (quint32)(infoData), isBootloader);

	m_updateActive = true;

	QrfeRfeUpdateController::UpdateResult ret = QrfeRfeUpdateController::UPDATE_FAILED;
	if(isBootloader)
		ret = m_updateController->updateReader(m_hardwareRevision, 0xFFFFFFFF, m_readerType, imageInfo);
	else
		ret = m_updateController->updateReader(imageInfo);

	if( ret == QrfeRfeUpdateController::UPDATE_OK )
	{
		m_reader->destroy();
		tryAccept(isBootloader);
		QMessageBox::information(this, this->windowTitle(), "Update successful.");
	}
	else if ( ret == QrfeRfeUpdateController::UPDATE_FILE_NOT_OPENED )
	{
		QMessageBox::information(this, this->windowTitle(), "Could not open the firmware file.");
	}
	else if( ret == QrfeRfeUpdateController::UPDATE_FAILED )
	{
		m_reader->destroy();
		tryAccept(false);
		QMessageBox::information(this, this->windowTitle(), "Update failed!");
	}

	m_updateActive = false;
	ui.proB_progress->setValue(0);
	ui.proB_progress->setVisible(false);
	ui.pB_update->setText("Start Update");
	ui.pB_update->setEnabled(true);
}

void CUpdateDialog::setProgress(int cur, int max)
{
	ui.proB_progress->setValue(cur);
	ui.proB_progress->setMaximum(max);
}



void CUpdateDialog::loadReaderInformation()
{
	ui.readerInfoTable->setItem( 0, 1, new QTableWidgetItem(m_reader->serialNumber()));
	ui.readerInfoTable->setItem( 1, 1, new QTableWidgetItem(m_reader->readerTypeExactString()));
	ui.readerInfoTable->setItem( 2, 1, new QTableWidgetItem(m_reader->hardwareRevisionString()));
	ui.readerInfoTable->setItem( 3, 1, new QTableWidgetItem(m_reader->softwareRevisionString()));
}

void CUpdateDialog::loadFirmwareRepository()
{
	QMap<ulong, QMap<ulong, ImageInfo> > images;
	QSet<ulong>	readerTypes;

	if(Permissions::d->showWildcardAllFirmwareFiles() == true)
	{
		readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, 0x000000FF);
	}
	else
	{
		readerTypes << m_readerType;

		if(QrfeGlobal::isPUR(m_readerType))
		{
			if(Permissions::d->showWildcardPurHIDKeyboardFirmwareFiles() == true)
			{
				readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_HID_KEYBOARD);
				readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_STANDARD);
			}
            if(Permissions::d->showWildcardPurIdtronicRussiaFirmwareFiles() == true)
            {
                readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_IDTRONIC_RUSSIA);
                readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_STANDARD);
            }
        }
	}

	images = m_updateManager->getAvailableSoftwareRevisions(readerTypes, m_hardwareRevision);

	ui.updateInfoWidget->setVisible(false);

	ui.firmwareRepository->clear();
	ui.firmwareRepository->setColumnCount(2);
	ui.firmwareRepository->setHeaderLabels(QStringList() << "Software Revisions" << "");;
	ui.firmwareRepository->setColumnWidth(0, 200);

	QTreeWidgetItem* rtItem;
	QTreeWidgetItem* swItem = 0;

	foreach(ulong readerType, images.uniqueKeys())
	{
		QMap<ulong, ImageInfo> rtImages = images.value(readerType);

		QString header = QrfeGlobal::readerTypeToString(readerType);
		if(Permissions::d->showWildcardAllFirmwareFiles())
			header +=  " - " + QrfeGlobal::applicationToString(readerType);

		rtItem = new QTreeWidgetItem(QStringList() << header);
		rtItem->setBackground(0, QColor(Qt::black));
		rtItem->setForeground(0, QColor(Qt::white));

		QFont f = rtItem->font(0);
		f.setBold(true);
		f.setPointSize(f.pointSize()+2);
		rtItem->setFont(0, f);
		rtItem->setTextAlignment(0, Qt::AlignCenter);

		ui.firmwareRepository->addTopLevelItem(rtItem);

		foreach(ulong softwareRevision, rtImages.keys())
		{
			if(softwareRevision > m_softwareRevision)
				ui.updateInfoWidget->setVisible(true);

			ImageInfo ii = rtImages.value(softwareRevision);
			QFileInfo info = ii.file;

			QStringList swRevInfo;
			swRevInfo << QrfeGlobal::softwareRevisionToString(softwareRevision);
			if(softwareRevision == m_softwareRevision)
				swRevInfo << "installed";

			swItem = new QTreeWidgetItem(QStringList() << swRevInfo.join(" - "));

			QFont f = swItem->font(0);
			f.setBold(true);
			swItem->setFont(0, f);

			if(softwareRevision == m_softwareRevision && readerType == m_readerType){
				QLinearGradient lg(0, 0, 0, 60 );
				lg.setColorAt(0, QColor(0x0C, 0x08, 0x5C));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
				swItem->setForeground(0, QColor(Qt::white));
			}
			else{
				QLinearGradient lg(0, 0, 0, 45 );
				lg.setColorAt(0, QColor(0x96, 0x99, 0xA2));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
			}

            quint64 infoData = 0;
            infoData += ((quint64)readerType) << 32;
            infoData += ((quint64)softwareRevision) << 0;

			QTreeWidgetItem *fileNameItem, *sizeItem, *lastModifiedItem;
			fileNameItem 		= new QTreeWidgetItem(QStringList() << "File Name" << info.fileName());
			sizeItem			= new QTreeWidgetItem(QStringList() << "Size" << QString("%L1").arg(info.size()) + " byte");
			lastModifiedItem	= new QTreeWidgetItem(QStringList() << "Last Modified" << ii.creation.toString());

			swItem->addChild(fileNameItem);
			swItem->addChild(sizeItem);
			swItem->addChild(lastModifiedItem);

			fileNameItem->setData(0, Qt::UserRole, QVariant(infoData) );
			sizeItem->setData(0, Qt::UserRole, QVariant(infoData) );
			lastModifiedItem->setData(0, Qt::UserRole, QVariant(infoData) );
			swItem->setData(0, Qt::UserRole, QVariant(infoData) );

			rtItem->insertChild(0, swItem);

			swItem->setFirstColumnSpanned(true);
			swItem->setExpanded(true);
		}

		rtItem->setFirstColumnSpanned(true);
		rtItem->setExpanded(true);
	}

	if(ui.firmwareRepository->topLevelItemCount() == 1 && swItem != 0)
		swItem->setSelected(true);
	if(ui.firmwareRepository->topLevelItemCount() > 1)
		ui.firmwareRepository->collapseAll ();

	ui.firmwareRepository->setFocus(Qt::TabFocusReason);
}

void CUpdateDialog::loadTestFirmwareRepository()
{
	QMap<ulong, QMap<ulong, ImageInfo> > images;
	QSet<ulong>	readerTypes;

	if(Permissions::d->showWildcardAllFirmwareFiles() == true)
	{
		readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, 0x000000FF);
	}
	else
	{
		readerTypes << m_readerType;

		if(QrfeGlobal::isPUR(m_readerType))
		{
			if(Permissions::d->showWildcardPurHIDKeyboardFirmwareFiles() == true)
			{
				readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_HID_KEYBOARD);
				readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_STANDARD);
			}
            if(Permissions::d->showWildcardPurIdtronicRussiaFirmwareFiles() == true)
            {
                readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_IDTRONIC_RUSSIA);
                readerTypes << QrfeGlobal::readerTypeWithApplication(m_readerType, QrfeGlobal::PUR_APP_STANDARD);
            }
		}
	}
	images = m_updateManager->getAvailableTestSoftwareRevisions(readerTypes, m_hardwareRevision);

	ui.updateInfoWidget->setVisible(false);

	ui.testFirmwareRepository->clear();
	ui.testFirmwareRepository->setColumnCount(2);
	ui.testFirmwareRepository->setHeaderLabels(QStringList() << "Software Revisions" << "");;
	ui.testFirmwareRepository->setColumnWidth(0, 200);

	QTreeWidgetItem* rtItem;
	QTreeWidgetItem* swItem = 0;

	foreach(ulong readerType, images.keys())
	{
		QMap<ulong, ImageInfo> rtImages = images.value(readerType);

		QString header = QrfeGlobal::readerTypeToString(readerType);
		if(Permissions::d->showWildcardAllFirmwareFiles())
			header +=  " - " + QrfeGlobal::applicationToString(readerType);

		rtItem = new QTreeWidgetItem(QStringList() << header);
		rtItem->setBackground(0, QColor(Qt::black));
		rtItem->setForeground(0, QColor(Qt::white));

		QFont f = rtItem->font(0);
		f.setBold(true);
		f.setPointSize(f.pointSize()+2);
		rtItem->setFont(0, f);
		rtItem->setTextAlignment(0, Qt::AlignCenter);

		ui.testFirmwareRepository->addTopLevelItem(rtItem);

		foreach(ulong softwareRevision, rtImages.keys())
		{
			if(softwareRevision > m_softwareRevision)
				ui.updateInfoWidget->setVisible(true);

			ImageInfo ii = rtImages.value(softwareRevision);
			QFileInfo info = ii.file;

			QStringList swRevInfo;
			swRevInfo << QrfeGlobal::softwareRevisionToString(softwareRevision);
			if(softwareRevision == m_softwareRevision)
				swRevInfo << "installed";

			swItem = new QTreeWidgetItem(QStringList() << swRevInfo.join(" - "));

			QFont f = swItem->font(0);
			f.setBold(true);
			swItem->setFont(0, f);

			if(softwareRevision == m_softwareRevision && readerType == m_readerType){
				QLinearGradient lg(0, 0, 0, 60 );
				lg.setColorAt(0, QColor(0x0C, 0x08, 0x5C));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
				swItem->setForeground(0, QColor(Qt::white));
			}
			else{
				QLinearGradient lg(0, 0, 0, 45 );
				lg.setColorAt(0, QColor(0x96, 0x99, 0xA2));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
			}

            quint64 infoData = 0;
            infoData += ((quint64)readerType) << 32;
            infoData += ((quint64)softwareRevision) << 0;

			QTreeWidgetItem *fileNameItem, *sizeItem, *lastModifiedItem;
			fileNameItem 		= new QTreeWidgetItem(QStringList() << "File Name" << info.fileName());
			sizeItem			= new QTreeWidgetItem(QStringList() << "Size" << QString("%L1").arg(info.size()) + " byte");
			lastModifiedItem	= new QTreeWidgetItem(QStringList() << "Last Modified" << ii.creation.toString());

			swItem->addChild(fileNameItem);
			swItem->addChild(sizeItem);
			swItem->addChild(lastModifiedItem);

			fileNameItem->setData(0, Qt::UserRole, QVariant(infoData) );
			sizeItem->setData(0, Qt::UserRole, QVariant(infoData) );
			lastModifiedItem->setData(0, Qt::UserRole, QVariant(infoData) );
			swItem->setData(0, Qt::UserRole, QVariant(infoData) );

			rtItem->insertChild(0, swItem);

			swItem->setFirstColumnSpanned(true);
			swItem->setExpanded(true);
		}

		rtItem->setFirstColumnSpanned(true);
		rtItem->setExpanded(true);
	}

	if(ui.testFirmwareRepository->topLevelItemCount() == 1 && swItem != 0)
		swItem->setSelected(true);
	if(ui.testFirmwareRepository->topLevelItemCount() > 1)
		ui.testFirmwareRepository->collapseAll ();

	ui.testFirmwareRepository->setFocus(Qt::TabFocusReason);
}

void CUpdateDialog::loadBootloaderRepository()
{
	ulong t_readerType = m_readerType;

    QMap<ulong, QMap<ulong, ImageInfo> > images = m_updateManager->getAvailableBootloaderRevisions(t_readerType, m_hardwareRevision, Permissions::d->showWildcardAllFirmwareFiles());

	ui.updateInfoWidget->setVisible(false);

	ui.bootloaderRepository->clear();
	ui.bootloaderRepository->setColumnCount(2);
	ui.bootloaderRepository->setHeaderLabels(QStringList() << "Bootoader Revisions" << "");;
	ui.bootloaderRepository->setColumnWidth(0, 200);

	QTreeWidgetItem* rtItem;
	QTreeWidgetItem* swItem = 0;

	foreach(ulong readerType, images.keys())
	{
		QMap<ulong, ImageInfo> rtImages = images.value(readerType);

		rtItem = new QTreeWidgetItem(QStringList() << QrfeGlobal::readerTypeToString(readerType));
		rtItem->setBackground(0, QColor(Qt::black));
		rtItem->setForeground(0, QColor(Qt::white));

		QFont f = rtItem->font(0);
		f.setBold(true);
		f.setPointSize(f.pointSize()+2);
		rtItem->setFont(0, f);
		rtItem->setTextAlignment(0, Qt::AlignCenter);

		ui.bootloaderRepository->addTopLevelItem(rtItem);

		foreach(ulong softwareRevision, rtImages.keys())
		{
			ImageInfo ii = rtImages.value(softwareRevision);
			QFileInfo info = ii.file;

			QStringList swRevInfo;
			swRevInfo << QrfeGlobal::bootloaderRevisionToString(softwareRevision);
			if(softwareRevision == m_bootloaderRevision)
				swRevInfo << "installed";

			swItem = new QTreeWidgetItem(QStringList() << swRevInfo.join(" - "));

			QFont f = swItem->font(0);
			f.setBold(true);
			swItem->setFont(0, f);

			if(softwareRevision == m_bootloaderRevision && readerType == m_readerType){
				QLinearGradient lg(0, 0, 0, 60 );
				lg.setColorAt(0, QColor(0x0C, 0x08, 0x5C));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
				swItem->setForeground(0, QColor(Qt::white));
			}
			else{
				QLinearGradient lg(0, 0, 0, 45 );
				lg.setColorAt(0, QColor(0x96, 0x99, 0xA2));
				lg.setColorAt(1, Qt::white);
				swItem->setBackground(0, QBrush(lg));
			}

            quint64 infoData = 0;
            infoData += ((quint64)readerType) << 32;
            infoData += ((quint64)softwareRevision) << 0;

			QTreeWidgetItem *fileNameItem, *sizeItem, *lastModifiedItem;
			fileNameItem 		= new QTreeWidgetItem(QStringList() << "File Name" << info.fileName());
			sizeItem			= new QTreeWidgetItem(QStringList() << "Size" << QString("%L1").arg(info.size()) + " byte");
			lastModifiedItem	= new QTreeWidgetItem(QStringList() << "Last Modified" << ii.creation.toString());

			swItem->addChild(fileNameItem);
			swItem->addChild(sizeItem);
			swItem->addChild(lastModifiedItem);

			fileNameItem->setData(0, Qt::UserRole, QVariant(infoData) );
			sizeItem->setData(0, Qt::UserRole, QVariant(infoData) );
			lastModifiedItem->setData(0, Qt::UserRole, QVariant(infoData) );
			swItem->setData(0, Qt::UserRole, QVariant(infoData) );

			rtItem->insertChild(0, swItem);

			swItem->setFirstColumnSpanned(true);
			swItem->setExpanded(true);
		}

		rtItem->setFirstColumnSpanned(true);
		rtItem->setExpanded(true);
	}

	if(ui.bootloaderRepository->topLevelItemCount() == 1 && swItem != 0)
		swItem->setSelected(true);
	if(ui.bootloaderRepository->topLevelItemCount() > 1)
		ui.bootloaderRepository->collapseAll ();

	ui.bootloaderRepository->setFocus(Qt::TabFocusReason);
}

