/*
 * QrfeRfeUpdateController.cpp
 *
 *  Created on: 01.07.2010
 *      Author: stefan.detter
 */

#include "../inc/update/QrfeRfeUpdateController.h"

#include <QCoreApplication>

#include "../inc/reader/QrfeRfeReader.h"

uint QrfeRfeUpdateController::_traceLevel = 6;

QrfeRfeUpdateController::QrfeRfeUpdateController(QrfeRfeReader* reader, QObject* parent)
	: QObject(parent)
	, QrfeTraceModule("QrfeRfeUpdateController", _traceLevel)
	, m_reader(reader)
{

}

QrfeRfeUpdateController::~QrfeRfeUpdateController()
{
}

QrfeRfeUpdateController::UpdateResult QrfeRfeUpdateController::updateReader(const QFileInfo& imageInfo)
{
	QFile imageFile(imageInfo.absoluteFilePath());
	if (!imageFile.open(QIODevice::ReadOnly))
	{
		warning("Failed to open the image file: " + imageInfo.absoluteFilePath());
		return UPDATE_FILE_NOT_OPENED;
	}
	trc(2, "Opened file " + imageInfo.fileName());

	QByteArray imageData = imageFile.readAll();

	return updateReader(imageData.mid(0, 12), imageData);
}

QrfeRfeUpdateController::UpdateResult QrfeRfeUpdateController::updateReader(const ulong hwRev, const ulong swRev, const ulong readerTyp, const QFileInfo& imageInfo)
{
	QFile imageFile(imageInfo.absoluteFilePath());
	if (!imageFile.open(QIODevice::ReadOnly))
	{
		warning("Failed to open the image file: " + imageInfo.absoluteFilePath());
		return UPDATE_FILE_NOT_OPENED;
	}
	trc(2, "Opened file " + imageInfo.fileName());

	QByteArray imageData = imageFile.readAll();

	QByteArray firmwareInfo;
	firmwareInfo.append(QrfeGlobal::toQByteArray(hwRev));
	firmwareInfo.append(QrfeGlobal::toQByteArray(swRev));
	firmwareInfo.append(QrfeGlobal::toQByteArray(readerTyp));

	return updateReader(firmwareInfo, imageData);
}


/******************* command byte definition ********************/
#define FLAG_ERROR			0x99		/** @brief This command will be sent if a package was not received successfully */
#define FLAG_OK				0x66		/** @brief This command will be sent if a package was received successfully */
#define FLAG_FINISH			0xF0		/** @brief This command will be sent if the transmission was successfull and md5 check ok */
#define FLAG_RESET			0x0F		/** @brief This command will be sent if the transmission was successfull but the md5 check failed --> restart transmission */
#define FLAG_ERROR_ABORT	0x67		/** @brief This command will be sent if the data FLASHD_WRITE procedure fails 3 times in a row */
#define FLAG_CLEAR_LICENSE	0x33

#define MODI_START    		0x55		/** @brief This state is for start update session */
#define MODI_COMM  			0x33		/** @brief This state is a command */
#define MODI_DATA     		0x88 		/** @brief This state is for data transmission */
#define MODI_AUX_COM        0xAA

#define FIRMWAREHEADER		65			/** @brief Containes data size of header data in firmare file (old 33 + 32 byte random AES) */
#define SEND_DATA_SIZE		240			/** @brief Size of transmitted firmware bytes in protocol */
#define FLOWCONTROLSIZE		255			/** @brief Flow Control can jump back 255 Frames */

#define INDEX_BL_PR_MODI	0			/** @brief Containes data size of header data in firmare file */
#define INDEX_BL_PR_FLOW	1			/** @brief Size of transmitted firmware bytes in protocol */
#define INDEX_BL_PR_COMM	2			/** @brief Size of transmitted firmware bytes in protocol */


QrfeGlobal::Result QrfeRfeUpdateController::clearLicense()
{
    // Prepare the start frame for the reader
    QByteArray sendBuffer;
    QByteArray response;

    sendBuffer.append((uchar)MODI_AUX_COM);
    sendBuffer.append((uchar)FLAG_CLEAR_LICENSE);

    // Send start frame
    if(m_reader->bootloader(response, sendBuffer) != QrfeGlobal::RES_OK)
        return QrfeGlobal::RES_ERROR;

    warning("Received: " + response.toHex());

    if(response.size() != 5)
        return QrfeGlobal::RES_ERROR;

    if((uchar)response.at(0) != RFE_RET_SUCCESS)
        return QrfeGlobal::RES_ERROR;

    ulong val = 0;
    val |= (((ulong) (uchar) response.at(1)) << 24);
    val |= (((ulong) (uchar) response.at(2)) << 16);
    val |= (((ulong) (uchar) response.at(3)) << 8);
    val |= (ulong) (uchar) response.at(4);

    val &= 0x12345678;

    sendBuffer.clear();
    sendBuffer.append((uchar)MODI_AUX_COM);
    sendBuffer.append((uchar)FLAG_CLEAR_LICENSE);
    sendBuffer.append((uchar) (val >> 24));
    sendBuffer.append((uchar) (val >> 16));
    sendBuffer.append((uchar) (val >> 8));
    sendBuffer.append((uchar) (val >> 0));

    // Send start frame
    if(m_reader->bootloader(sendBuffer) != QrfeGlobal::RES_OK)
        return QrfeGlobal::RES_ERROR;

    return QrfeGlobal::RES_OK;
}

QrfeRfeUpdateController::UpdateResult QrfeRfeUpdateController::updateReader(const QByteArray firmwareInfo, const QByteArray& imageData)
{
	bool startFrameSent = false;
	int numFrames = 0;						// Count of sending 130 Byte Frames
	int curFrame = 0;								// count of current sending Frame

	uchar currentFlowControl = 0;
	uchar lastUsedFlowControl = 0;


	QByteArray sendBuffer;
	QByteArray readBuffer;

	if(firmwareInfo.size() != 12)
		return UPDATE_FILE_NOT_VALID;

	numFrames = (imageData.length() - FIRMWAREHEADER) / SEND_DATA_SIZE;	// compute count of sending frames
	if((imageData.length() - FIRMWAREHEADER) % SEND_DATA_SIZE == 0)
		numFrames--;

	m_runUpdate = true;

	while(curFrame <= numFrames )		// + 1 final package from reader
	{
		emit progressChanged(curFrame, numFrames);

		QCoreApplication::processEvents();

		if(!m_runUpdate)
			return UPDATE_STOPED;

		if(!startFrameSent)
		{
			// Prepare the start frame for the reader
			sendBuffer.clear();
			sendBuffer[0] = MODI_START;
			sendBuffer.append(firmwareInfo);
			sendBuffer[13] = 0 /*ui.lE_readerid->text().at(0).row()*/;
			sendBuffer[14] = 0 /*ui.lE_readerid->text().at(1).row()*/;
			sendBuffer[15] = 0 /*ui.lE_readerid->text().at(2).row()*/;
			sendBuffer[16] = 0 /*ui.lE_readerid->text().at(3).row()*/;
			sendBuffer.append(imageData.mid(12, 53));	// with AES

			trc(1 , "Sending Start Frame");
			trc(3 , "Frame " + sendBuffer.toHex());

			// Send start frame
			if(m_reader->bootloader(readBuffer, sendBuffer) != QrfeGlobal::RES_OK)
				return UPDATE_FAILED;

			curFrame = 0;
		}
		else
		{
			// Prepare the current data frame
			sendBuffer.clear();
			sendBuffer[0] = (quint8)MODI_DATA;
			sendBuffer[1] = currentFlowControl;
			sendBuffer.append(imageData.mid(FIRMWAREHEADER + (curFrame * SEND_DATA_SIZE), SEND_DATA_SIZE));

			trc(1, "Sending Frame " + QString("%1 / %2 - %3").arg(curFrame, 3).arg(numFrames, 3).arg(currentFlowControl, 3) );
			trc(3, "Frame " + sendBuffer.toHex());

			// Send data frame, if error occures, send ERROR_FLAG
			if(m_reader->bootloader(readBuffer, sendBuffer) != QrfeGlobal::RES_OK)
			{
				sendBuffer.resize(3);
				sendBuffer[0] = (quint8)MODI_COMM;
				sendBuffer[2] = (quint8)FLAG_ERROR;
				int err_count = 0;
				while(1)
				{
					if(m_reader->bootloader(readBuffer, sendBuffer) == QrfeGlobal::RES_OK)
						break;
                    if(err_count++ == 3)
						return UPDATE_FAILED;
				}
			}
		}

		// store last used flow control
		lastUsedFlowControl = currentFlowControl;

		trc(3, "Received Reply Frame: " + readBuffer.toHex());

		QCoreApplication::processEvents();

		switch(readBuffer.at(INDEX_BL_PR_MODI))
		{
		case MODI_COMM :

			if((uchar) readBuffer.at(INDEX_BL_PR_COMM) == FLAG_OK)
			{
				curFrame++;
				currentFlowControl = readBuffer.at(INDEX_BL_PR_FLOW);

				uchar expectedFlowControl = lastUsedFlowControl + 1;
				if(startFrameSent == true && currentFlowControl != expectedFlowControl)
				{
					int diff = 0;
					if(expectedFlowControl == 0xFF)
						diff = (0xFF - expectedFlowControl) + currentFlowControl;
					else
						diff = expectedFlowControl - currentFlowControl;

					trc(1, "Flow Control does not match.");
					trc(1, "   LastFlow     = " + QString::number(lastUsedFlowControl));
					trc(1, "   ReceivedFlow = " + QString::number(currentFlowControl));
					trc(1, "   ExpectedFlow = " + QString::number(expectedFlowControl));
					trc(1, "   CalcDiff     = " + QString::number(diff));

					curFrame = curFrame - diff;
				}

				if(startFrameSent == false)
				{
					curFrame = 0;
					startFrameSent = true;
				}

				trc(1, "Received Frame - Flag OK - " + QString("%1").arg(currentFlowControl, 3));
			}
			if((uchar) readBuffer.at(INDEX_BL_PR_COMM) == FLAG_FINISH)
			{
				trc(1, "Received Frame - Flag FINISH");
				trc(1, " Count transmitted frames: " + QString::number( curFrame  ));
				trc(1, " Transmitted data bytes: " + QString::number( imageData.length() ));
				trc(1, "Update successful!");
				return UPDATE_OK;
			}
			if( (uchar) readBuffer.at(INDEX_BL_PR_COMM) == FLAG_ERROR )
			{
				trc(1, "Received Frame - Flag ERROR");
				if(lastUsedFlowControl > currentFlowControl)
				{
					curFrame = curFrame - ( lastUsedFlowControl - currentFlowControl );
				} else
				{
					// This calculation is needed, because the maximum size of flow control
					// is 255 and we can assume, therefore, only 255 packets can jump back.
					curFrame = curFrame - ( FLOWCONTROLSIZE - currentFlowControl + lastUsedFlowControl + 1 );
				}
				trc(1, " Restarting with frame " + QString::number(curFrame));
			}
			if( (uchar) readBuffer.at(INDEX_BL_PR_COMM) == FLAG_RESET )
			{
				trc(1, "Received Frame - Flag RESET");
				curFrame = 0;
				startFrameSent = false;
				trc(1, " Restarting with frame " + QString::number(curFrame));
			}
			break;
		default:
			trc(1, "Invalid Bootloader reply - FAILED");
			return UPDATE_FAILED;
		}

	}

	trc(1, "Did not receive the FINISH flag - FAILED");
	return UPDATE_FAILED;

}

void QrfeRfeUpdateController::stopCurrentUpdate()
{
	m_runUpdate = false;
}
