#include "OpenReaderDialog.h"

#include <QMessageBox>
#include <QKeyEvent>
#include <QApplication>
#include <QTimer>
#include <QProcess>
#include <QMenu>
#include <QInputDialog>

#include "../../Cache.h"
#include <QrfeReaderManager>
#include <QrfeResourceGlobal>

#include "SerialPortSettingsDialog.h"

#define OPEN_LAST_USED_TAB_KEY			"Open - LastUsedTab"
#define OPEN_TCP_IP_CACHE_KEY			"Open - TCP - IpCache"
#define OPEN_SERIAL_SHOW_ALL			"Open - Serial - ShowAll"


OpenReaderDialog::OpenReaderDialog(QrfeReaderManager* manager, QWidget *parent)
    : QDialog(parent)
    , m_manager(manager)
	, m_focusWidget(0)
{
	ui.setupUi(this);

	connect(m_manager, 					SIGNAL(deviceListsChanged()),
			this, 						  SLOT(reloadDeviceLists()));

	connect(ui.baudrateToolButton, 		SIGNAL(clicked()),
			this, 						  SLOT(changeBaud()) );
	connect(ui.showAllSerialBox, 		SIGNAL(stateChanged(int)),
			this, 						  SLOT(reloadSerialList()));
	connect(ui.ipListWidget, 			SIGNAL(itemClicked(QListWidgetItem*)),
			this, 	  					  SLOT(ipSelected(QListWidgetItem*)));
	connect(ui.ipListWidget, 			SIGNAL(customContextMenuRequested(const QPoint&)),
			this, 	  					  SLOT(showIpListPopup(const QPoint&)));
	connect(ui.tabWidget,				SIGNAL(currentChanged(int)), 
			this,						  SLOT(currentTabChanged(int)));


	connect(ui.okButton, 				SIGNAL(clicked()),
			this, 						  SLOT(tryAccept()) );
	connect(ui.cancelButton, 			SIGNAL(clicked()),
			this, 						  SLOT(reject()) );
	connect(ui.serialPortListWidget,	SIGNAL(itemDoubleClicked(QListWidgetItem*)),
			this, 						  SLOT(tryAccept()));
	connect(ui.ipListWidget,			SIGNAL(itemDoubleClicked(QListWidgetItem*)),
			this, 						  SLOT(tryAccept()));
	connect(ui.bonjourListWidget,		SIGNAL(itemDoubleClicked(QListWidgetItem*)),
			this, 						  SLOT(tryAccept()));
	connect(ui.hidListWidget,			SIGNAL(itemDoubleClicked(QListWidgetItem*)),
			this, 						  SLOT(tryAccept()));

	this->setFocusPolicy(Qt::StrongFocus);

	m_waitRunner = new QTimer(this);
	m_waitRunner->setInterval(100);

	connect(m_waitRunner,				SIGNAL(timeout()),
			this,						  SLOT(incrementProgress()) );

	ui.tabWidget->setCurrentIndex(0);
	ui.progressBar->setVisible(false);

	ui.ipListWidget->setContextMenuPolicy(Qt::CustomContextMenu);
	m_context = new QMenu("Tag Context Menu", this);

	m_context_delete = new QAction("Delete", this);
	m_context_delete->setIcon(QIcon(QrfeGlobal::getButtonIconResourcePath("clear")));
	connect(m_context_delete, 			SIGNAL(triggered (bool)),
			this, 						  SLOT(deleteIpListEntry()));

#ifdef Q_OS_WIN
	ui.customPathLineEdit->setVisible(false);
	ui.customPortSpinBox->setVisible(true);
#else
	ui.customPathLineEdit->setVisible(true);
	ui.customPortSpinBox->setVisible(false);
    ui.showAllSerialBox->setChecked(true);
#endif

    m_serialPortSettingsDialog = new SerialPortSettingsDialog(this);
}

OpenReaderDialog::~OpenReaderDialog()
{

}


int OpenReaderDialog::exec()
{
	connect(qApp,						SIGNAL(focusChanged ( QWidget*, QWidget*)),
			this,						SLOT(focusChanged ( QWidget*, QWidget*)) );

	//m_serialBaudrate = QrfeReaderManager::SERIAL_BAUD_115200;
	m_serialPortSettingsDialog->resetValues();

	reloadSerialList();
	reloadIpCache();
	reloadBonjourList();
	reloadHidList();

	bool ok = false;
	int lastUsedTab = Cache::d.value(OPEN_LAST_USED_TAB_KEY, 0).toInt(&ok);
	if(!ok)
		lastUsedTab = 0;

	ui.showAllSerialBox->setChecked(Cache::d.value(OPEN_SERIAL_SHOW_ALL, false).toBool());

	ui.tabWidget->setCurrentIndex(lastUsedTab);
	currentTabChanged(ui.tabWidget->currentIndex());

	ui.customPortSpinBox->setValue(1);
	ui.customPortSpinBox->stepUp();
	ui.customPortSpinBox->stepDown();

	return QDialog::exec();
}

void OpenReaderDialog::keyPressEvent ( QKeyEvent * event )
{
	if(event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
		tryAccept();

	if(event->key() == Qt::Key_Escape)
		reject();

	QWidget::keyPressEvent(event);
}

void OpenReaderDialog::currentTabChanged ( int index )
{
	switch(index){
		case 0:
			ui.customPortSpinBox->setFocus(Qt::TabFocusReason);
			break;
		case 1:
			ui.ip1Edit->setFocus(Qt::TabFocusReason);
			break;
		case 2:
			ui.bonjourListWidget->setFocus(Qt::TabFocusReason);
			break;
	}
}

void OpenReaderDialog::focusChanged ( QWidget * /*old*/, QWidget * now )
{
	if(now == ui.customPortSpinBox)
		m_focusWidget = ui.customPortSpinBox;
	else if(now == ui.customPathLineEdit)
		m_focusWidget = ui.customPathLineEdit;
	else if(now == ui.serialPortListWidget)
		m_focusWidget = ui.serialPortListWidget;
	else if(now == ui.ipListWidget)
		m_focusWidget = ui.ipListWidget;
	else if(now == ui.ip1Edit || now == ui.ip2Edit || now == ui.ip3Edit || now == ui.ip4Edit || now == ui.portEdit)
		m_focusWidget = ui.ip1Edit;
	else if(now == ui.bonjourListWidget)
		m_focusWidget = ui.bonjourListWidget;
	else if(now == ui.hidListWidget)
		m_focusWidget = ui.hidListWidget;
}


void OpenReaderDialog::tryAccept()
{
	QrfeReaderManager::Result res = QrfeReaderManager::ERROR_PORT;
	QString dev;
	QWidget* lastFocus = 0;

	if(ui.tabWidget->currentWidget() == ui.serialTab)
	{
		QString port;
		if( (lastFocus = ui.serialPortListWidget) == m_focusWidget)
		{
			QListWidgetItem *item = ui.serialPortListWidget->currentItem();
			if(item == 0)
			{
				QMessageBox::critical(this, "Add Reader", "No serial port selected.");
				return;
			}
			QVariant variant = item->data(Qt::UserRole);
			QrfeReaderDeviceInformation info = variant.value<QrfeReaderDeviceInformation>();
			port = info.path;
		}
		else if( (lastFocus = ui.customPortSpinBox) == m_focusWidget){
			port = ui.customPortSpinBox->text();
		}
		else if( (lastFocus = ui.customPathLineEdit) == m_focusWidget){
			port = ui.customPathLineEdit->text();
		}
		else
			return;

		startWait();
		dev = port;
		res = m_manager->addSerialReader(port, m_serialPortSettingsDialog->serialBaudrate(), m_serialPortSettingsDialog->hwFlowControl());
	}

	if(ui.tabWidget->currentWidget() == ui.ethernetTab)
	{
		QString ip;
		if( (lastFocus = ui.ipListWidget) == m_focusWidget)
		{
			QListWidgetItem *item = ui.ipListWidget->currentItem();
			if(item == 0)
			{
				QMessageBox::critical(this, "Add Reader", "No ip selected.");
				return;
			}
			ip = item->text();
		}
		else if( (lastFocus = ui.ip1Edit) == m_focusWidget)
		{
			ushort port;
			if(ui.ip1Edit->text().toInt() > 255 || ui.ip2Edit->text().toInt() > 255 || ui.ip3Edit->text().toInt() > 255 ||
					ui.ip4Edit->text().toInt() > 255 || ui.portEdit->text().toInt() > 65535 )
			{
				QString errorIp = ui.ip1Edit->text() + "." + ui.ip2Edit->text() + "." + ui.ip3Edit->text() + "." + ui.ip4Edit->text() + ":" + ui.portEdit->text();
				QMessageBox::critical(this, "Add Reader", "The specified ip is not valid.\n" + errorIp + " is not a valid ip.");
				return;
			}
			uint ip1, ip2, ip3, ip4;
			ip1 = ui.ip1Edit->text().toInt();
			ip2 = ui.ip2Edit->text().toInt();
			ip3 = ui.ip3Edit->text().toInt();
			ip4 = ui.ip4Edit->text().toInt();
			port = ui.portEdit->text().toInt();
			ip = QString("%1.%2.%3.%4:%5").arg(ip1).arg(ip2).arg(ip3).arg(ip4).arg(port);
		}
		else
			return;

		startWait();
		dev = ip;
		res = m_manager->addTcpReader(dev);
	}

	if(ui.tabWidget->currentWidget() == ui.bonjourTab)
	{
		if( (lastFocus = ui.bonjourListWidget) == m_focusWidget)
		{
#ifdef QRFE_BONJOUR
			QListWidgetItem *processItem = ui.bonjourListWidget->currentItem();
			if(processItem == 0)
			{
				QMessageBox::critical(this, "Add Reader", "No bonjour server selected.");
				return;
			}
			QVariant variant = processItem->data(Qt::UserRole);
			QrfeReaderDeviceInformation info = variant.value<QrfeReaderDeviceInformation>();
			startWait();
			dev = info.humanReadableName;
			res = m_manager->addReader(info);
#else
			res = QrfeReaderManager::ERROR_PORT;
#endif
		}
		else
			return;
	}

	if(ui.tabWidget->currentWidget() == ui.hidTab)
	{
		if( (lastFocus = ui.hidListWidget) == m_focusWidget)
		{
#ifdef QRFE_HIDDEVICE
			QListWidgetItem *processItem = ui.hidListWidget->currentItem();
			if(processItem == 0)
			{
				QMessageBox::critical(this, "Add Reader", "No HID device selected.");
				return;
			}
			QVariant variant = processItem->data(Qt::UserRole);
			QrfeReaderDeviceInformation path = variant.value<QrfeReaderDeviceInformation>();
			startWait();
			dev = path.humanReadableName;
			res = m_manager->addReader(path);
#else
			res = QrfeReaderManager::ERROR_PORT;
#endif
		}
		else
			return;
	}

	stopWait();
	this->setEnabled(true);

	if(res != QrfeReaderManager::OK)
	{
		if(res == QrfeReaderManager::ERROR_PORT)
			QMessageBox::critical(this, "Add Reader", "Could not open the device " + dev + ".");
		else if( res == QrfeReaderManager::ERROR_PROTOCOLL)
			QMessageBox::critical(this, "Add Reader", "The connected reader at the device " + dev + " is not supported.");
		else if( res == QrfeReaderManager::ERROR_DOUBLE_ID)
			QMessageBox::critical(this, "Add Reader", "The connected reader has a reader ID that is already in use.");

		lastFocus->setFocus(Qt::TabFocusReason);
		return;
	}
	else
	{
		if(ui.tabWidget->currentIndex() == 1)
			storeIpInCache(dev);

		Cache::d.setValue(OPEN_SERIAL_SHOW_ALL, ui.showAllSerialBox->isChecked() );
		Cache::d.setValue(OPEN_LAST_USED_TAB_KEY, ui.tabWidget->currentIndex());
	}

	disconnect(	qApp,		SIGNAL(focusChanged ( QWidget*, QWidget*)),
				this,		  SLOT(focusChanged ( QWidget*, QWidget*)) );

	QDialog::accept();
}


void OpenReaderDialog::changeBaud()
{
//	QStringList bauds;
//	bauds << "115200";
//	bauds << "57600";
//	bauds << "38400";
//	bauds << "19200";
//
//	bool ok = false;
//	QString baud = QInputDialog::getItem(this, "Select Baudrate", "Baud", bauds, m_serialBaudrate, false, &ok);
//
//	if(!ok)
//		return;
//
//	int pos = bauds.indexOf(baud);
//	if(pos == -1)
//		return;
//
//	m_serialBaudrate = (QrfeReaderManager::SerialBaud) pos;

	m_serialPortSettingsDialog->exec();
}


void OpenReaderDialog::reloadDeviceLists()
{
	reloadSerialList();
	reloadBonjourList();
	reloadHidList();
}

void OpenReaderDialog::reloadSerialList()
{
	QList<QrfeReaderDeviceInformation> ports = m_manager->availablePortList();
	ui.serialPortListWidget->clear();
	foreach(QrfeReaderDeviceInformation info, ports)
	{
		QrfeSerialPortInfo serInfo = info.data.value<QrfeSerialPortInfo>();

#if QRFE_SERIALPORT_VERSION >= 6
		if( (!ui.showAllSerialBox->isChecked()) && (serInfo.vendorIdentifier() != 0x1325 || serInfo.productIdentifier() != 0xC02A) )
#else
		if( (!ui.showAllSerialBox->isChecked()) && (serInfo.vendorID != 0x1325 || serInfo.productID != 0xC02A) )
#endif
			continue;

        QVariant variant;
        variant.setValue(info);
#ifdef Q_OS_WIN32
        QListWidgetItem *processItem = new QListWidgetItem(info.humanReadableName, ui.serialPortListWidget);
#else
        QListWidgetItem *processItem = new QListWidgetItem(info.path, ui.serialPortListWidget);
#endif
        processItem->setData(Qt::UserRole, variant);

		ui.serialPortListWidget->addItem(processItem);
	}
	ui.serialPortListWidget->setCurrentRow(0);
}

void OpenReaderDialog::reloadIpCache()
{
	QStringList ipCache = Cache::d.value(OPEN_TCP_IP_CACHE_KEY).toStringList();
	if(!ipCache.contains("192.168.0.100:52460"))
		ipCache.append("192.168.0.100:52460");
	ipCache.sort();
	ui.ipListWidget->clear();
	ui.ipListWidget->addItems(ipCache);
	ui.ipListWidget->setCurrentRow(0);
}

void OpenReaderDialog::reloadBonjourList()
{
	QList<QrfeReaderDeviceInformation> records = m_manager->availableBonjourServices();
	ui.bonjourListWidget->clear();
	foreach(QrfeReaderDeviceInformation rec, records)
	{
        QVariant variant;
        variant.setValue(rec);
        QListWidgetItem *processItem = new QListWidgetItem(rec.humanReadableName, ui.bonjourListWidget);
        processItem->setData(Qt::UserRole, variant);

		ui.bonjourListWidget->addItem(processItem);
	}
	ui.bonjourListWidget->setCurrentRow(0);
}

void OpenReaderDialog::reloadHidList()
{
	QList<QrfeReaderDeviceInformation> paths = m_manager->availableHidList();
	ui.hidListWidget->clear();
	foreach(QrfeReaderDeviceInformation path, paths)
	{
        QVariant variant;
        variant.setValue(path);
        QListWidgetItem *processItem = new QListWidgetItem(path.humanReadableName, ui.hidListWidget);
        processItem->setData(Qt::UserRole, variant);

		ui.hidListWidget->addItem(processItem);
	}
	ui.hidListWidget->setCurrentRow(0);
}



void OpenReaderDialog::storeIpInCache(QString ip)
{
	QStringList ipCache = Cache::d.value(OPEN_TCP_IP_CACHE_KEY).toStringList();
	if(!ipCache.contains(ip)){
		ipCache.append(ip);
		Cache::d.setValue(OPEN_TCP_IP_CACHE_KEY, ipCache);
	}
}

void OpenReaderDialog::ipSelected(QListWidgetItem * item)
{
	if(item == 0)
		return;

	QString text = item->text();
	QStringList ipPort = text.split(":");
	if(ipPort.size() != 2)
		return;
	QStringList ip = ipPort.at(0).split(".");
	if(ip.size() != 4)
		return;

	ui.ip1Edit->setText(ip.at(0));
	ui.ip2Edit->setText(ip.at(1));
	ui.ip3Edit->setText(ip.at(2));
	ui.ip4Edit->setText(ip.at(3));
	ui.portEdit->setText(ipPort.at(1));
}

void OpenReaderDialog::showIpListPopup(const QPoint &iPoint)
{
	QListWidgetItem* item = ui.ipListWidget->itemAt(iPoint);
	m_ipListEntryToDelete = item->text();
	m_context->clear();
	m_context->addAction(m_context_delete);
	m_context->exec(QPoint(ui.ipListWidget->mapToGlobal(iPoint).x()+3, ui.ipListWidget->mapToGlobal(iPoint).y()+3));
}

void OpenReaderDialog::deleteIpListEntry()
{
	QStringList ipCache = Cache::d.value(OPEN_TCP_IP_CACHE_KEY).toStringList();
	if(ipCache.contains(m_ipListEntryToDelete)){
		ipCache.removeAll(m_ipListEntryToDelete);
		Cache::d.setValue(OPEN_TCP_IP_CACHE_KEY, ipCache);
	}

	reloadIpCache();
}

void OpenReaderDialog::linkActivated ( const QString & link )
{
	QProcess* process = new QProcess();
	process->start("\"C:\\Windows\\system32\\cmd.exe\"", QStringList() << "/C" << link);
	connect(process, SIGNAL(finished(int, QProcess::ExitStatus)), process, SLOT(deleteLater()));
}



void OpenReaderDialog::startWait()
{
	ui.tabWidget->setEnabled(false);
	ui.okButton->setEnabled(false);
	ui.cancelButton->setEnabled(false);

	ui.progressBar->setValue(0);
	ui.progressBar->setInvertedAppearance(false);
	m_waitRunner->start();
	ui.progressBar->setVisible(true);
}

void OpenReaderDialog::stopWait()
{
	ui.tabWidget->setEnabled(true);
	ui.okButton->setEnabled(true);
	ui.cancelButton->setEnabled(true);

	ui.progressBar->setVisible(false);
	m_waitRunner->stop();

	ui.customPortSpinBox->setFocus(Qt::TabFocusReason);
	ui.customPortSpinBox->setValue(1);
	ui.customPortSpinBox->stepUp();
	ui.customPortSpinBox->stepDown();

}

void OpenReaderDialog::incrementProgress()
{
	if(ui.progressBar->invertedAppearance() == false)
	{
		ui.progressBar->setValue(ui.progressBar->value() + 1);
		if(ui.progressBar->value() == ui.progressBar->maximum())
			ui.progressBar->setInvertedAppearance(true);
	}
	else
	{
		ui.progressBar->setValue(ui.progressBar->value() - 1);
		if(ui.progressBar->value() == ui.progressBar->minimum())
			ui.progressBar->setInvertedAppearance(false);
	}
}
