#include "BootloaderProtocol.h"
#include "PICUploader.h"




CBootloaderProtocol::CBootloaderProtocol(QObject *parent) : QObject(parent)
{
    mCRCEngine.InitTable();
}

CBootloaderProtocol::~CBootloaderProtocol()
{

}

int CBootloaderProtocol::BootloaderProtocolInit()
{
    BootloaderProtocolResetStateMachine();
}

int CBootloaderProtocol::BootloaderProtocolResetStateMachine()
{
    mBootloaderDataSize = 0;
    mBootloaderHeader = 0;
    mBootloaderBufPtr = 0;
    mBootloaderDataBuffer.clear();
    mBootloaderCommand = 0;
    mBootloaderCRC = 0;
    mBootloaderComputedCRC = CRC_START_32;
    mBootloaderState = RxHeader1;
    mBootloaderDataCtr = 0;

}

int CBootloaderProtocol::BooloaderProtocolRxFrame(QByteArray Frame)
{
    for(int i = 0; i < Frame.size(); i++)
    {
        BootloaderProtocolStateMachine(Frame.at(i));
    }
}

void CBootloaderProtocol::BootloaderProtocolStateMachine(unsigned char Data)
{

    switch(mBootloaderState)
        {
            case Initialization:	//Reset all pointers and data...
            {
                mBootloaderDataSize = 0;
                mBootloaderBufPtr = 0;
                mBootloaderDataBuffer.clear();
                mBootloaderCommand = 0;
                mBootloaderCRC = 0;
                mBootloaderComputedCRC = CRC_START_32;
                mBootloaderState = RxHeader1;
                break;
            }
            case RxHeader1:	//Wait for data header...
            {
                mBootloaderHeader += Data;	//0xDE
                mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

                if(Data == BOOTLOADER_FRAME_HEADER_1)
                {
                    mBootloaderState = RxHeader2;
                }
                else
                {
                    BootloaderProtocolResetStateMachine();
                }

                break;
            }
        case RxHeader2:	//Wait for data header...
        {
            mBootloaderHeader <<= 8;
            mBootloaderHeader += Data;	//0xAD
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            if(Data == BOOTLOADER_FRAME_HEADER_2)
            {
                mBootloaderState = RxHeader3;
            }
            else
            {
                BootloaderProtocolResetStateMachine();
            }
            break;
        }
        case RxHeader3:	//Wait for data header...
        {
            mBootloaderHeader <<= 8;
            mBootloaderHeader += Data;	//0xBE
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            if(Data == BOOTLOADER_FRAME_HEADER_3)
            {
                mBootloaderState = RxHeader4;
            }
            else
            {
                BootloaderProtocolResetStateMachine();
            }
            break;
        }
        case RxHeader4:	//Wait for data header...
        {
            mBootloaderHeader <<= 8;
            mBootloaderHeader += Data;	//0xEF
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            if(mBootloaderHeader != BOOTLOADER_FRAME_HEADER)
            {
                //TODO, send NACK?
                BootloaderProtocolResetStateMachine();
                break;
            }
            else
            {
                mBootloaderState = RxCmd;
            }

            break;
        }
        case RxCmd:
        {
            mBootloaderCommand = Data;
            mBootloaderState = RxPayloadSize1;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);
            break;
        }
        case RxPayloadSize1:
        {
            mBootloaderDataSize = Data;
            mBootloaderState = RxPayloadSize2;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);
            break;
        }
        case RxPayloadSize2:
        {
            mBootloaderDataSize <<= 8;
            mBootloaderDataSize += Data;

            mBootloaderState = RxPayloadSize3;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);
            break;
        }
        case RxPayloadSize3:
        {
            mBootloaderDataSize <<= 8;
            mBootloaderDataSize += Data;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            mBootloaderState = RxPayloadSize4;
            break;
        }
        case RxPayloadSize4:
        {
            mBootloaderDataSize <<= 8;
            mBootloaderDataSize += Data;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            if(mBootloaderDataSize > MAX_BOOTLOADER_PAYLOAD_SIZE)
            {
                //TODO, send NACK?
                BootloaderProtocolResetStateMachine();
                break;
            }

            mBootloaderState = RxPayload;
            break;
        }
        case RxPayload:	//Data size
        {
            mBootloaderDataBuffer.append(Data);
            mBootloaderDataCtr++;
            mBootloaderComputedCRC = mCRCEngine.UpdateCRC32(mBootloaderComputedCRC,Data);

            if(mBootloaderDataCtr == mBootloaderDataSize)
            {
                mBootloaderState = RxCRC1;
                break;
            }

            break;
        }
        case RxCRC1:	//Data size
        {
            mBootloaderCRC = Data;
            mBootloaderState = RxCRC2;
            break;
        }
        case RxCRC2:	//Data size
        {
            mBootloaderCRC <<= 8;
            mBootloaderCRC += Data;
            mBootloaderState = RxCRC3;
            break;
        }
        case RxCRC3:	//Data size
        {
            mBootloaderCRC <<= 8;
            mBootloaderCRC += Data;
            mBootloaderState = RxCRC4;
            break;
        }
        case RxCRC4:	//Data size
        {
            mBootloaderCRC <<= 8;
            mBootloaderCRC += Data;

            mBootloaderComputedCRC ^= 0xffffffffL;
            //if(mBootloaderCRC == 0xBAADCAFE)
            if(mBootloaderCRC == mBootloaderComputedCRC)
            {
                mProgramHandle->BootloaderRxCmd(mBootloaderCommand,mBootloaderDataBuffer);
            }
            else
            {
                qDebug("Bootloader Protocol bad CRC...");
            }

            BootloaderProtocolResetStateMachine();
            break;
        }
        default:
        {
            BootloaderProtocolResetStateMachine();
            break;
        }
    }
}

QByteArray CBootloaderProtocol::BootloaderProtocolGetFrame(char Cmd, QByteArray Data)
{
    QByteArray Frame;
    Frame.clear();

    // Header
    Frame.append(BOOTLOADER_FRAME_HEADER_1);
    Frame.append(BOOTLOADER_FRAME_HEADER_2);
    Frame.append(BOOTLOADER_FRAME_HEADER_3);
    Frame.append(BOOTLOADER_FRAME_HEADER_4);

    //Cmd
    Frame.append(Cmd);

    //Size
    unsigned int Size = Data.size();

    char nibble = (char)((Size >> 24) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)((Size >> 16) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)((Size >> 8) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)(Size &0x000000FF);
    Frame.append(nibble);

    //Payload
    Frame.append(Data);

    //CRC
    unsigned int CRC = mCRCEngine.ComputeCRC32(Frame);

    nibble = (char)((CRC >> 24) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)((CRC >> 16) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)((CRC >> 8) &0x000000FF);
    Frame.append(nibble);

    nibble = (char)(CRC &0x000000FF);
    Frame.append(nibble);

/*    Frame.append(0xBA);
    Frame.append(0xAD);
    Frame.append(0xCA);
    Frame.append(0xFE);*/

    //Send
    return Frame;


}
