/*
 * CAmsReaderManager.cpp
 *
 *  Created on: 27.01.2009
 *      Author: stefan.detter
 */

#include "../inc/QrfeReaderManager.h"
#include "../inc/QrfeReaderFactory.h"
#include "../inc/QrfeReaderInterface.h"
#include "../inc/reader/QrfeRfeReader.h"

#include <QDir>

#ifdef QRFE_HIDDEVICE
	#include <QrfeHidDevice>
#endif

#ifdef QT_NETWORK_LIB
	#include <QHostAddress>
	#include <QHostInfo>
#endif

#ifdef QRFE_BONJOUR
	#include <QrfeBonjourServiceResolver>
#endif



uint QrfeReaderManager::_traceLevel = 6;
uint QrfeReaderManager::_functionTraceLevel = 4;


QrfeReaderManager::QrfeReaderManager(QObject* parent)
	: QObject(parent)
	, QrfeTraceModule("QrfeReaderManager",_traceLevel, _functionTraceLevel)
{
#ifdef QRFE_BONJOUR
	m_bonjourBrowser = new QrfeBonjourServiceBrowser(this);
    m_bonjourBrowser->browseForServiceType(QLatin1String("_rfe_reader._tcp"));
    connect(m_bonjourBrowser, 	SIGNAL(currentBonjourRecordsChanged(const QList<QrfeBonjourRecord>&)),
    		this, 				SIGNAL(deviceListsChanged()));
#endif

#ifdef QRFE_DEVICEDETECTOR
	m_deviceDetecor.registerForUSBDevice(0x1325, 0xc02a);
	connect(&m_deviceDetecor, 	SIGNAL(usbDeviceAttached ( QString, quint16, quint16 )),
			this, 				SIGNAL(deviceListsChanged()) );
	connect(&m_deviceDetecor, 	SIGNAL(usbDeviceRemoved ( QString, quint16, quint16 )),
			this, 				SIGNAL(deviceListsChanged()) );
	connect(this,			 	SIGNAL(deviceListsChanged()),
			this, 				  SLOT(processDeviceListsChanges()) );
#endif

    m_aboutToDestroy = false;
    m_attachUsbReaderAutomatically = true;
}

QrfeReaderManager::~QrfeReaderManager()
{
	m_aboutToDestroy = true;
	cleanUp();
	foreach(QString id, m_readerIDs.keys())
	{
		delete m_readerIDs.value(id);
	}
}

QrfeReaderManager::Result QrfeReaderManager::addReader(QrfeReaderDeviceInformation devInfo)
{
	switch(devInfo.type)
	{
#ifdef QRFE_SERIALPORT
	case QrfeGlobal::DEVICE_Serial:
		return addSerialReader(devInfo.path);
#endif
#ifdef QRFE_HIDDEVICE
	case QrfeGlobal::DEVICE_HID:
		return addHidReader(devInfo.path);
#endif
#ifdef QT_NETWORK_LIB
	case QrfeGlobal::DEVICE_TCP:
		return addTcpReader(devInfo.path);
#endif
#ifdef QRFE_BONJOUR
	case QrfeGlobal::DEVICE_BONJOUR:
		return addBonjourReader(devInfo.data.value<QrfeBonjourRecord>());
#endif
    default:
        break;
	}

	return ERROR_PORT;
}

QrfeReaderManager::Result QrfeReaderManager::addReader(QrfeGlobal::DeviceType type, QString deviceName)
{
	QrfeTraceFunction f(this, "addReader");

	switch(type)
	{
#ifdef QRFE_SERIALPORT
	case QrfeGlobal::DEVICE_Serial:
		return addSerialReader(deviceName);
#endif

#ifdef QT_NETWORK_LIB
	case QrfeGlobal::DEVICE_TCP:
		return addTcpReader(deviceName);
#endif

#ifdef QRFE_HIDDEVICE
	case QrfeGlobal::DEVICE_HID:
		return addHidReader(deviceName);
#endif
	default:
		return ERROR_PORT;
	}

	return ERROR_PORT;
}

QrfeReaderManager::Result QrfeReaderManager::addReader(QIODevice* dev, QrfeGlobal::DeviceType type)
{
	QrfeTraceFunction f(this, "addReader");

	QrfeReaderInterface* reader = QrfeReaderFactory::getRfeReader(dev, type, this);

	if(reader == 0)
	{
		return QrfeReaderManager::ERROR_PROTOCOLL;
	}

	if(reader->initDevice() != QrfeGlobal::RES_OK)
	{
		reader->deleteLater();
		return QrfeReaderManager::ERROR_PROTOCOLL;
	}

	foreach(QString id, m_readerIDs.keys())
	{
		if(id == reader->readerId()){
			QrfeReaderInterface* r = m_readerIDs.value(id);
			if(r->currentState() != QrfeGlobal::STATE_OFFLINE)
				return QrfeReaderManager::ERROR_DOUBLE_ID;
			else
			{
				m_readerIDs.remove(id);
				r->deleteLater();
			}
		}
	}

#ifdef QRFE_SERIALPORT
    if(type == QrfeGlobal::DEVICE_Serial)
    {
        m_usedPorts.insert(reader->readerId(), qobject_cast<QrfeSerialPort*>(dev)->portName());
        trc(1, "Insert port: " + qobject_cast<QrfeSerialPort*>(dev)->portName() + " from reader: " + reader->readerId());
    }
#endif


	QObject::connect(reader, SIGNAL(lostConnection()), this, SLOT(lostConnectionToReader()));
	QObject::connect(reader, SIGNAL(requestForDelete()), this, SLOT(deleteRequest()));
	QObject::connect(reader, SIGNAL(destroyed(QObject*)), this, SLOT(readerDestroyed(QObject*)));
	m_readerIDs.insert(reader->readerId(), reader);

	emit gotReader(reader);


	return QrfeReaderManager::OK;
}


#ifdef QRFE_SERIALPORT
QrfeReaderManager::Result QrfeReaderManager::addSerialReader(QString port)
{
	QrfeTraceFunction f(this, "addSerialReader", QStringList() << port);

	return addSerialReader(port, SERIAL_BAUD_115200, false);

//	QrfeSerialPort* dev = 0;
//	QrfeReaderManager::Result res;
//	for(int i = 0; i < 2; i++)
//	{
//		dev = new QrfeSerialPort();
//
//		dev->setPortName(port);
//#if QRFE_SERIALPORT_VERSION >= 6
//		dev->setFlowControl(QrfeSerialPort::NoFlowControl);
//		dev->setParity(QrfeSerialPort::NoParity);
//		dev->setDataBits(QrfeSerialPort::Data8);
//		dev->setStopBits(QrfeSerialPort::OneStop);
//		dev->setBaudRate(QrfeSerialPort::Baud115200);
//#else
//		dev->setFlowControl(FLOW_OFF);
//		dev->setParity(PAR_NONE);
//		dev->setDataBits(DATA_8);
//		dev->setStopBits(STOP_1);
//		dev->setBaudRate(BAUD115200);
//#if QRFE_SERIALPORT_VERSION >= 5
//		dev->setTimeout(1);
//#else
//		dev->setTimeout(0,1);
//#endif
//#endif
//
//		if(dev->open(QIODevice::ReadWrite))
//			trc(1, dev->portName() + " opened.");
//		else{
//			trc(1, "Could not open " + dev->portName() + ".");
//			dev->deleteLater();
//			dev = 0;
//			return QrfeReaderManager::ERROR_PORT;
//		}
//
//		res = addReader(dev, QrfeGlobal::DEVICE_Serial);
//		if(res == QrfeReaderManager::OK)
//			return res;
//	}
//	return res;
}

QrfeReaderManager::Result QrfeReaderManager::addSerialReader(QString port, SerialBaud baudrate, bool hwFlowControl)
{
	QrfeTraceFunction f(this, "addSerialReader", QStringList() << port);

	QrfeSerialPort* dev = 0;
	QrfeReaderManager::Result res;
	for(int i = 0; i < 2; i++)
	{
		dev = new QrfeSerialPort();

		dev->setPortName(port);
#if QRFE_SERIALPORT_VERSION >= 6
		if(hwFlowControl)
			dev->setFlowControl(QrfeSerialPort::HardwareControl);
		else
			dev->setFlowControl(QrfeSerialPort::NoFlowControl);
		dev->setParity(QrfeSerialPort::NoParity);
		dev->setDataBits(QrfeSerialPort::Data8);
		dev->setStopBits(QrfeSerialPort::OneStop);

		if(baudrate == SERIAL_BAUD_115200)
			dev->setBaudRate(QrfeSerialPort::Baud115200);
		else if(baudrate == SERIAL_BAUD_57600)
			dev->setBaudRate(QrfeSerialPort::Baud57600);
		else if(baudrate == SERIAL_BAUD_38400)
			dev->setBaudRate(QrfeSerialPort::Baud38400);
		else if(baudrate == SERIAL_BAUD_19200)
			dev->setBaudRate(QrfeSerialPort::Baud19200);
		else if(baudrate == SERIAL_BAUD_9600)
			dev->setBaudRate(QrfeSerialPort::Baud9600);
#else
		if(hwFlowControl)
			dev->setFlowControl(FLOW_HARDWARE);
		else
			dev->setFlowControl(FLOW_OFF);
		dev->setParity(PAR_NONE);
		dev->setDataBits(DATA_8);
		dev->setStopBits(STOP_1);
#if QRFE_SERIALPORT_VERSION >= 5
		dev->setTimeout(1);
#else
		dev->setTimeout(0,1);
#endif
		if(baudrate == SERIAL_BAUD_115200)
			dev->setBaudRate(BAUD115200);
		else if(baudrate == SERIAL_BAUD_57600)
			dev->setBaudRate(BAUD57600);
		else if(baudrate == SERIAL_BAUD_38400)
			dev->setBaudRate(BAUD38400);
		else if(baudrate == SERIAL_BAUD_19200)
			dev->setBaudRate(BAUD19200);
		else if(baudrate == SERIAL_BAUD_9600)
			dev->setBaudRate(BAUD9600);
#endif

		if(dev->open(QIODevice::ReadWrite))
			trc(1, dev->portName() + " opened.");
		else{
			trc(1, "Could not open " + dev->portName() + ".");
			dev->deleteLater();
			dev = 0;
			return QrfeReaderManager::ERROR_PORT;
		}

		res = addReader(dev, QrfeGlobal::DEVICE_Serial);
		if(res == QrfeReaderManager::OK)
			return res;
	}
	return res;
}
#endif

#ifdef QRFE_HIDDEVICE
QrfeReaderManager::Result QrfeReaderManager::addHidReader(QString path)
{
	QrfeTraceFunction f(this, "addHidReader", QStringList() << path);

	QrfeHidDevice* dev = new QrfeHidDevice(path);
	QrfeReaderManager::Result res;

	if(dev->open(QIODevice::ReadWrite))
		trc(1, dev->devicePath() + " opened.");
	else{
		trc(1, "Could not open " + dev->devicePath() + ".");
		dev->deleteLater();
		dev = 0;
		return QrfeReaderManager::ERROR_PORT;
	}

	res = addReader(dev, QrfeGlobal::DEVICE_HID);
	return res;
}
#endif

#ifdef QT_NETWORK_LIB
QrfeReaderManager::Result QrfeReaderManager::addTcpReader(QString deviceName)
{
	QrfeTraceFunction f(this, "addTcpReader", QStringList() << deviceName);

	QString ip;
	ushort port = 12345;

	if(deviceName.contains(QChar(':'))){
		ip = deviceName.split(QChar(':')).at(0);
		port = deviceName.split(QChar(':')).at(1).toUShort();
	}
	else
		ip = deviceName;

	QHostAddress addr;
	if(!addr.setAddress(ip))
		return QrfeReaderManager::ERROR_PORT;

	QTcpSocket* dev = new QTcpSocket();

	dev->connectToHost(addr, port, QIODevice::ReadWrite);
	if(dev->waitForConnected (2000))
		trc(1, ip + " opened.");
	else{
		trc(1, "Could not open " + ip + ".");
		delete dev;
		dev = 0;
		return QrfeReaderManager::ERROR_PORT;
	}

	return addReader(dev, QrfeGlobal::DEVICE_TCP);
}
#endif

#ifdef QRFE_BONJOUR
QrfeReaderManager::Result QrfeReaderManager::addBonjourReader(QrfeBonjourRecord rec)
{
	QrfeTraceFunction f(this, "addBonjourReader");

	QrfeBonjourServiceResolver resolver;

	QHostInfo info;
	int port;
	if(!resolver.resolveBonjourRecord(rec, info, port))
		return QrfeReaderManager::ERROR_PORT;

    const QList<QHostAddress> &addresses = info.addresses();
    if (addresses.isEmpty())
		return QrfeReaderManager::ERROR_PORT;

    for(int i = 0; i < addresses.size(); i++)
        trc(1, "addr" + QString::number(i) + ": " + addresses.at(i).toString());

	QTcpSocket* dev = new QTcpSocket();

	dev->connectToHost(QHostAddress(addresses.first().toIPv4Address()), port);
	if(!dev->waitForConnected (2000))
	{
		dev->deleteLater();
		dev = 0;
		return QrfeReaderManager::ERROR_PORT;
	}

	return addReader(dev, QrfeGlobal::DEVICE_TCP);
}
#endif


QList<QrfeReaderDeviceInformation> QrfeReaderManager::availablePortList()
{
	QrfeTraceFunction f(this, "availablePortList");
	QList<QrfeReaderDeviceInformation> result;
#ifdef QRFE_SERIALPORT

#if QRFE_SERIALPORT_VERSION >= 6
	QList<QrfeSerialPortInfo> list = QrfeSerialPortInfo::availablePorts();
#else
	QList<QrfeSerialPortInfo> list = QrfeSerialPort::availablePorts();
#endif

	foreach(QrfeSerialPortInfo data, list)
	{
		QrfeReaderDeviceInformation di;
		di.type = QrfeGlobal::DEVICE_Serial;
#if QRFE_SERIALPORT_VERSION >= 6
        di.humanReadableName = data.description() + " (" + data.portName() + ")";
		di.path = data.systemLocation();
#else
		di.humanReadableName = data.friendName;
		di.path = data.portName;
#endif
		di.data.setValue<QrfeSerialPortInfo>(data);
		result << di;
	}
#endif
	return result;
}

QList<QrfeReaderDeviceInformation> QrfeReaderManager::availableBonjourServices()
{
	QrfeTraceFunction f(this, "availableBonjourServices");
	QList<QrfeReaderDeviceInformation> result;
#ifdef QRFE_BONJOUR
	QList<QrfeBonjourRecord> list = m_bonjourBrowser->currentRecords();
	foreach(QrfeBonjourRecord data, list)
	{
		QrfeReaderDeviceInformation di;
		di.type = QrfeGlobal::DEVICE_BONJOUR;
		di.humanReadableName = data.serviceName;
		di.path = "";
		di.data.setValue<QrfeBonjourRecord>(data);
		result << di;
	}
#endif
	return result;
}

QList<QrfeReaderDeviceInformation> QrfeReaderManager::availableHidList()
{
	QrfeTraceFunction f(this, "availableHidList");
	QList<QrfeReaderDeviceInformation> result;
#if (defined QRFE_DEVICEDETECTOR) && (defined QRFE_HIDDEVICE)
	QStringList paths = m_deviceDetecor.getConnectedHIDDevicePaths(0x1325, 0xc02a);
	foreach(QString path, paths)
	{
		QrfeHidDevice dev(path);
		QrfeReaderDeviceInformation di;
		di.type = QrfeGlobal::DEVICE_HID;
		di.humanReadableName = "HID-Reader " +  dev.getSerialString();
		di.path = path;
		di.data.setValue<QString>(path);
		result << di;
	}
#endif
	return result;
}



void QrfeReaderManager::lostConnectionToReader()
{
	QrfeTraceFunction f(this, "lostConnectionToReader");

	trc(1, "Lost connection to reader");
	QrfeReaderInterface* reader = qobject_cast<QrfeReaderInterface*>(sender());

	if(!m_readerIDs.values().contains(reader))
		return;

	emit lostReader(reader);

    if(m_usedPorts.contains(m_readerIDs.key(reader))){
        trc(1, "Remove port: " + m_usedPorts.value(m_readerIDs.key(reader)) + " from reader: " + m_readerIDs.key(reader));
        m_usedPorts.remove(m_readerIDs.key(reader));
    }

	m_readerIDs.remove(m_readerIDs.key(reader));
	m_toDelete.insert(m_readerIDs.key(reader), reader);
}

void QrfeReaderManager::deleteRequest()
{
	QrfeTraceFunction f(this, "deleteRequest");

	trc(1, "Reader requests to be deleted");
	QrfeReaderInterface* reader = qobject_cast<QrfeReaderInterface*>(sender());

	if(!m_readerIDs.values().contains(reader))
		return;

	emit lostReader(reader);

	m_readerIDs.remove(m_readerIDs.key(reader));
	m_toDelete.insert(m_readerIDs.key(reader), reader);
}

void QrfeReaderManager::readerDestroyed(QObject* obj)
{
	QrfeTraceFunction f(this, "readerDestroyed");

	if(m_aboutToDestroy)
		return;

	QrfeReaderInterface* reader = qobject_cast<QrfeReaderInterface*>(obj);
	if(reader == 0 )
		return;

	if(m_readerIDs.values().contains(reader))
	{
		m_readerIDs.remove(m_readerIDs.key(reader));
	}
	else if(m_toDelete.values().contains(reader))
	{
		m_toDelete.remove(m_toDelete.key(reader));
	}
}

#if (defined QRFE_DEVICEDETECTOR) && (defined QRFE_HIDDEVICE)
void QrfeReaderManager::hidDeviceAttached ( QString devicePath, quint16 /*vendorID*/, quint16 /*productID*/ )
{
	QrfeHidDevice dev(devicePath);
	QrfeReaderDeviceInformation di;
	di.type = QrfeGlobal::DEVICE_HID;
	di.humanReadableName = "HID-Reader " +  dev.getSerialString();
	di.path = devicePath;
	di.data.setValue<QString>(devicePath);

	emit newReaderDeviceDetected(di);
}
#endif

void QrfeReaderManager::cleanUp()
{
	QrfeTraceFunction f(this, "cleanUp");

	trc(2, "Cleaning up...");
	foreach(QString id, m_toDelete.keys()){
		QrfeReaderInterface* reader = m_toDelete.value(id);
        reader->deleteLater();
	}
	m_toDelete.clear();
}

QList<QString> QrfeReaderManager::readerList() const
{
	QrfeTraceFunction f(this, "readerList");

	return m_readerIDs.keys();
}

QrfeReaderInterface* QrfeReaderManager::readerInterface(QString readerId) const
{
	QrfeTraceFunction f(this, "readerInterface");

	return m_readerIDs.value(readerId);
}

void QrfeReaderManager::attachUsbReaderAutomatically(bool attachAutomatically)
{
	QrfeTraceFunction f(this, "attachUsbReaderAutomatically", QStringList() << ((attachAutomatically)?"ON":"OFF"));
	m_attachUsbReaderAutomatically = attachAutomatically;
}

void QrfeReaderManager::processDeviceListsChanges()
{
    QTimer::singleShot(50, this, SLOT(checkForNewDevice()));
}

void QrfeReaderManager::checkForNewDevice()
{
    if(!m_attachUsbReaderAutomatically)
        return;

    QrfeTraceFunction f(this, "processDeviceListsChanges");

    trc(1, "Checking available ports...");

    QList<QrfeReaderDeviceInformation> avPorts = availablePortList();

    foreach(QrfeReaderDeviceInformation info, avPorts)
    {
        QrfeSerialPortInfo serInfo = info.data.value<QrfeSerialPortInfo>();

        trc(1, " Port " + serInfo.portName());

#if QRFE_SERIALPORT_VERSION >= 6
        if( (serInfo.vendorIdentifier() != 0x1325 || serInfo.productIdentifier() != 0xC02A) )
#else
        if( (serInfo.vendorID != 0x1325 || serInfo.productID != 0xC02A) )
#endif
        {
            trc(1, "  -> Port ntz suitable");
            continue;
        }

        if(m_usedPorts.values().contains(serInfo.portName()))
        {
            trc(1, "  -> Port already used");
            continue;
        }

        trc(1, "  -> Add port");
        addSerialReader(serInfo.portName());
    }
}

