/*
 * QrfeProtocolHandler.cpp
 *
 *  Created on: 14.05.2009
 *      Author: stefan.detter
 */

#include <QrfeProtocolHandler.h>

#include <QrfeSleeper>

uint  				QrfeProtocolHandler::_traceLevel = 6;
QThread::Priority 	QrfeProtocolHandler::_priority = QThread::InheritPriority;
bool 				QrfeProtocolHandler::_emitISRsQueued = true;

QrfeProtocolHandler::QrfeProtocolHandler(QIODevice* dev, QrfeGlobal::DeviceType deviceType, QObject* parent)
	: QThread(parent)
	, QrfeTraceModule("QrfeProtocolHandler", _traceLevel)
	, m_device(dev)
	, m_deviceType(deviceType)
	, m_sendBuffer(0)
	, m_classInitialized(false)
{
	// Clear input buffer
	m_device->readAll();

	m_threadInitialized = false;
	this->start(_priority);

	while(!m_threadInitialized){
		QrfeSleeper::MSleep(10);
	}

	m_messageQueue.setSingleThread(false);

	m_deviceRemoved = false;

	m_MAX_WAIT_TIME_IN_MS = 1000;

	if(this != QThread::currentThread ())
		m_device->moveToThread(this);

	getDeviceSpecificName();
}

QrfeProtocolHandler::~QrfeProtocolHandler()
{
	releaseDeviceImmediatly();

	m_deviceRemoved = true;
}

QString QrfeProtocolHandler::deviceName() const
{
	return m_deviceName;
}

QrfeGlobal::DeviceType QrfeProtocolHandler::deviceType() const
{
	return m_deviceType;
}

void QrfeProtocolHandler::setTimeOut(uint timeOut)
{
	m_MAX_WAIT_TIME_IN_MS = timeOut;
}


void QrfeProtocolHandler::deleteLater ()
{
	waitForNoOneWaiting();

	blockSignals(true);

	// delete this object
	QObject::deleteLater();
}

bool QrfeProtocolHandler::deviceRemoved()
{
	return m_deviceRemoved;
}

void QrfeProtocolHandler::deviceWasRemoved()
{
	if(m_deviceRemoved == true)
		return;

	// note in a variable that the device was removed
	m_deviceRemoved = true;

	emit deviceClosed();

	// set the state of the reader
	this->blockSignals(true);
}


void QrfeProtocolHandler::releaseDeviceImmediatly()
{
	this->quit();
	while(!isFinished ()) 
		QrfeSleeper::MSleep(1);
	m_deviceRemoved = true;
}

bool QrfeProtocolHandler::waitForNoOneWaiting(uint msecs)
{
	if(msecs == 0){
		while(!m_deviceRemoved && m_messageQueue.currentlyWaiting()){
			QrfeSleeper::MSleepAlive(100);
		}
	}
	else
	{
		uint cycles = msecs / 10;
		uint i = 0;
		// wait until there is no function waiting for a flag
		while(!m_deviceRemoved && i++ < cycles && m_messageQueue.currentlyWaiting()){
			QrfeSleeper::MSleepAlive(100);
		}
	}

	if(m_messageQueue.currentlyWaiting())
		return false;

	return true;
}

void QrfeProtocolHandler::computeData 	( const QByteArray &/*data*/ )
{
	return;
}

void QrfeProtocolHandler::computeMessage ( const QByteArray &/*msg*/ )
{
	return;
}


void QrfeProtocolHandler::readFromDevice ( )
{
	// read from device into the buffer
	QByteArray buff;

	if(m_deviceRemoved)
		return;

	buff = readAllDeviceSpecific();
	if(buff.size() <= 0)
		return;

	trcBytes(6, "Received", buff);

	// let the specific protocolhandler compute the data
	if(m_classInitialized){
		computeData(buff);
	}
	else
		warning("Read from device but class is not initialized");
}

bool QrfeProtocolHandler::send( const QByteArray &data )
{
	if (m_sendBuffer == 0 || !m_sendBuffer->isOpen())
		return false;
	qint64 sent = m_sendBuffer->write(data);
	return sent == data.size();
}

void QrfeProtocolHandler::sendToDevice()
{
	if(!m_device->isOpen())
		warning("Trying to write but device is not open!");

	QByteArray data = m_sendBuffer->readAll();
	writeDeviceSpecific(data);
	flushDeviceSpecific();

	trcBytes(6, "Sent", data);
}

void QrfeProtocolHandler::run()
{
	m_sendBuffer = new QrfeFifo();
	m_sendBuffer->open(QIODevice::ReadWrite);

	connectDeviceSpecific();

	trc(2, "Worker thread initialized");

	m_threadInitialized = true;

	exec();

	if(!m_deviceRemoved || m_device->isOpen())
		m_device->close();

	if(m_sendBuffer && m_sendBuffer->isOpen())
		m_sendBuffer->close();

	// TODO TESTEN!!!
	delete m_sendBuffer;
	delete m_device;
}



#ifdef QRFE_SERIALPORT
	#include <QrfeSerialPort>
#endif

#ifdef QRFE_HIDDEVICE
	#include <QrfeHidDevice>
#endif

#ifdef QT_NETWORK_LIB
	#include <QTcpSocket>
	#include <QHostAddress>
	#include <QLocalSocket>
#endif

#if QRFE_BASELIB_VERSION >= 6
	#include <QrfePipe>
#endif


inline QString QrfeProtocolHandler::getDeviceSpecificName()
{
	switch(m_deviceType)
	{
	#ifdef QRFE_SERIALPORT
	case QrfeGlobal::DEVICE_Serial:
		m_deviceName = ((QrfeSerialPort*)m_device)->portName();
		break;
	#endif

	#ifdef QRFE_HIDDEVICE
	case QrfeGlobal::DEVICE_HID:
		m_deviceName = ((QrfeHidDevice*)m_device)->devicePath();
		break;
	#endif

	#ifdef QT_NETWORK_LIB
	case QrfeGlobal::DEVICE_TCP:
		m_deviceName =((QTcpSocket*)m_device)->peerName() + ":" + QString::number(((QTcpSocket*)m_device)->peerPort());
		break;

	case QrfeGlobal::DEVICE_NAMEDPIPE:
		m_deviceName =((QLocalSocket*)m_device)->fullServerName();
		break;
	#endif

	#if QRFE_BASELIB_VERSION >= 6
	case QrfeGlobal::DEVICE_PIPE:
		m_deviceName =((QrfePipe*)m_device)->name();
		break;
	#endif

	default:
		m_deviceName = "Unknown";
		break;
	}

	return m_deviceName;
}

inline void QrfeProtocolHandler::connectDeviceSpecific()
{
	// connect to the ready read signal of the device
	connect(m_sendBuffer, 		SIGNAL(readyRead()),
			this, 				  SLOT(sendToDevice()), Qt::DirectConnection);
	connect(m_device, 			SIGNAL(readyRead()),
			this, 				  SLOT(readFromDevice()), Qt::DirectConnection);

	switch(m_deviceType)
	{
	#ifdef QT_NETWORK_LIB
	case QrfeGlobal::DEVICE_TCP:
		connect(m_device, SIGNAL(disconnected()), this, SLOT(deviceWasRemoved()), Qt::DirectConnection);
		connect(m_device, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(tcpSocketError(QAbstractSocket::SocketError)), Qt::DirectConnection);
		break;
	case QrfeGlobal::DEVICE_NAMEDPIPE:
		connect(m_device, SIGNAL(disconnected()), this, SLOT(deviceWasRemoved()), Qt::DirectConnection);
		connect(m_device, SIGNAL(error(QLocalSocket::LocalSocketError)), this, SLOT(localSocketError(QLocalSocket::LocalSocketError)), Qt::DirectConnection);
		break;
	#endif

	#if QRFE_BASELIB_VERSION >= 6
	case QrfeGlobal::DEVICE_PIPE:
		connect(m_device, SIGNAL(disconnected()), this, SLOT(deviceWasRemoved()), Qt::DirectConnection);
		break;
	#endif

	default:
		break;
	}
}

inline qint64 QrfeProtocolHandler::writeDeviceSpecific(const QByteArray &data)
{
	switch(m_deviceType)
	{
	case QrfeGlobal::DEVICE_Serial:
	case QrfeGlobal::DEVICE_TCP:
	case QrfeGlobal::DEVICE_NAMEDPIPE:
	case QrfeGlobal::DEVICE_PIPE:
		return m_device->write(data);

	case QrfeGlobal::DEVICE_HID:
	{
		int size = data.size();
		int dataOffset = 0;
		while(size > 0)
		{
			uchar tempSize = (size > 63)?63:size;
			QByteArray buffer(65, (char)0x00);
			buffer[0] = 0;
			buffer[1] = tempSize;
			for(uchar i = 0; i < tempSize; i++)
			{
				buffer[2 + i] = data[dataOffset + i];
			}
			m_device->write(buffer);

			dataOffset += tempSize;
			size -= tempSize;
		}
		return data.size();
	}
	default:
		return 0;
	}

	return 0;
}

inline QByteArray QrfeProtocolHandler::readAllDeviceSpecific()
{
	switch(m_deviceType)
	{
	case QrfeGlobal::DEVICE_Serial:
	case QrfeGlobal::DEVICE_TCP:
	case QrfeGlobal::DEVICE_NAMEDPIPE:
	case QrfeGlobal::DEVICE_PIPE:
		return m_device->readAll();
	case QrfeGlobal::DEVICE_HID:
	{
		QByteArray data;
		QByteArray temp = m_device->readAll();

		if(temp.size()%65 != 0){
			error("Received unwknown HID packet size!");
			return QByteArray();
		}

		for(int i = 0; i < temp.size()/65; i++)
		{
			QByteArray report = temp.mid((i*65), 65);
			data += report.mid(2, report.at(1));
		}
		return data;
	}
	default:
		return QByteArray();
	}

	return QByteArray();
}

inline void QrfeProtocolHandler::flushDeviceSpecific()
{
	switch(m_deviceType)
	{
	case QrfeGlobal::DEVICE_Serial:
		#ifdef QRFE_SERIALPORT
			if(qobject_cast<QrfeSerialPort*>(m_device) != 0){
				qobject_cast<QrfeSerialPort*>(m_device)->flush();
			}
		#endif
		break;
	case QrfeGlobal::DEVICE_HID:
		#ifdef QRFE_HIDDEVICE
			if(qobject_cast<QrfeHidDevice*>(m_device) != 0){
				qobject_cast<QrfeHidDevice*>(m_device)->flush();
			}
		#endif
		break;
	case QrfeGlobal::DEVICE_TCP:
		#ifdef QT_NETWORK_LIB
			if(qobject_cast<QAbstractSocket*>(m_device) != 0){
				qobject_cast<QAbstractSocket*>(m_device)->flush();
			}
		#endif
		break;
	case QrfeGlobal::DEVICE_NAMEDPIPE:
		#ifdef QT_NETWORK_LIB
			if(qobject_cast<QLocalSocket*>(m_device) != 0){
				qobject_cast<QLocalSocket*>(m_device)->flush();
			}
		#endif
		break;

	default:
			break;
	}

}

#ifdef QT_NETWORK_LIB
void QrfeProtocolHandler::tcpSocketError ( QAbstractSocket::SocketError socketError )
{
	error("TCP: Got error: " + QString::number(socketError));
	deviceWasRemoved();
}

void QrfeProtocolHandler::localSocketError ( QLocalSocket::LocalSocketError socketError )
{
	error("PIPE: Got error: " + QString::number(socketError));
	deviceWasRemoved();
}
#endif
