/*******************************************************************************
*                                                                              *
* Société de Transports de Montréal.                                    	   *
* 2015                                                                         *
*                                                                              *
* Projet Zones Tests                                                           *
*                                                                              *
*                                                                              *
*                                                                              *
*******************************************************************************/
/*
    Description:
    Classe qui contient toute la logique du protocole de communication par réseau
    entre la Zone Test et le logiciel d'analyse.

*/

/* ************************************************************************** */
/* Revision:
### 20121210 JFM
    Verision d'origine.

### YYYYMMDD Description du besoin ou du bug
    Description du changement.
 */

/* ************************************************************************** */

#include "TCPProtocol.h"
#include <QByteArray>
#include <QDataStream>
#include <QDebug>

CTCPProtocol::CTCPProtocol()
{
}
   //
unsigned int CTCPProtocol::AnalyzeNewData(QByteArray data)
{
    QDataStream stream(data);
    stream.device()->seek(0);

    switch(mProtocolRxState)
    {
        case TCP_PROTOCOL_GET_HEADER_STATE:
        {
            quint32 header = 0;
            qint32 msg = 0;

            stream >> header;

            if(header != TCP_PROTOCOL_HEADER)
            {
                return RET_ERROR;
            }

            stream >> msg;
            stream >> mCurTransactionDataSize;

            mCurTransactionMsg = (eTCPMessage)msg;

            if(mCurTransactionDataSize != 0)
            {
                mCurTransactionData = stream.device()->readAll();
                if(mCurTransactionData.size() < mCurTransactionDataSize)
                {
                    mProtocolRxState = TCP_PROTOCOL_GET_DATA_STATE;
                }
                else if(mCurTransactionData.size() > mCurTransactionDataSize)
                {
                    //error
                    qDebug("Rx data > Transaction data.  Aborting transaction");
                    ResetRxStateMachine();
                }
                else
                {
                    DecodeTCPMessage();
                    ResetRxStateMachine();
                }
            }
            else
            {
                DecodeTCPMessage();
                ResetRxStateMachine();
            }

            break;
        }
        case TCP_PROTOCOL_GET_MSG_STATE:
        {
            break;
        }
        case TCP_PROTOCOL_GET_DATASIZE_STATE:
        {
            break;
        }
        case TCP_PROTOCOL_GET_DATA_STATE:
        {
            mCurTransactionData.append(data);
            if(mCurTransactionData.size() == mCurTransactionDataSize)
            {
                //All data received
                DecodeTCPMessage();
                ResetRxStateMachine();
            }
            else if(mCurTransactionData.size() > mCurTransactionDataSize)
            {
                //error
                qDebug("Rx data > Transaction data.  Aborting transaction");
                ResetRxStateMachine();
            }
            else
            {
                qDebug() << "Chunk received.  New data size = " << mCurTransactionData.size();
            }
            break;
        }
    }

    return RET_OK;
}

QByteArray CTCPProtocol::GetHeartbeatPacket()
{
    QByteArray request;
    QDataStream stream(&request,QIODevice::WriteOnly);
    stream.device()->seek(0);

    request.clear();
    stream << TCP_PROTOCOL_HEADER << (qint32)TCP_MSG_HEARTBEAT_RESPONSE << (qint64)0; ;
    //no data...

  //  TCPTxRequest(request);


    return request;
}

QByteArray CTCPProtocol::GetStationNamePacket(QString StationName)
{
    QByteArray request;
    QDataStream stream(&request,QIODevice::WriteOnly);
    stream.device()->seek(0);


    request.clear();
    stream << TCP_PROTOCOL_HEADER;
    stream << (qint32)TCP_MSG_STATION_NAME_RESPONSE;
    stream << (qint64)StationName.size();
    request.append(StationName);

    //no data...

  //  TCPTxRequest(request);
    return request;
}

QByteArray CTCPProtocol::GetZTStatusPacket(CTCPZTStatus *Status)
{
    QByteArray request;
    request.clear();
    QByteArray data;
    data.clear();

    QDataStream datastream(&data,QIODevice::WriteOnly);
    datastream.device()->seek(0);
    datastream << *Status;

    QDataStream requeststream(&request,QIODevice::WriteOnly);
    requeststream.device()->seek(0);

    requeststream << TCP_PROTOCOL_HEADER
                  << (qint32)TCP_MSG_ZT_STATUS_RESPONSE
                  << (qint64)data.size();

    request.append(data);

  //  TCPTxRequest(request);

    return request;
}

QByteArray CTCPProtocol::GetZTLogPacket(QString *ZTLog)
{
    QByteArray request;
    request.clear();
    QByteArray data;
    data.clear();

    data = ZTLog->toUtf8();

    QDataStream requeststream(&request,QIODevice::WriteOnly);
    requeststream.device()->seek(0);

    requeststream << TCP_PROTOCOL_HEADER
                  << (qint32)TCP_MSG_ZTLOG_DOWNLOAD_RESPONSE
                  << (qint64)data.size();

    request.append(data);
    return request;
}

QByteArray CTCPProtocol::GetTrainLogsDownloadAckPacket(qint32 FileCount)
{
    QByteArray request;
    request.clear();

    QDataStream requeststream(&request,QIODevice::WriteOnly);
    requeststream.device()->seek(0);

    requeststream << TCP_PROTOCOL_HEADER
                  << (qint32)TCP_MSG_TRAINSLOGS_DOWNLOAD_ACK
                  << (qint64)sizeof(FileCount)
                  << FileCount;

    return request;
}

QByteArray *CTCPProtocol::GetTrainLogDataPacket(const QByteArray &FileData)
{
    QByteArray *request = new QByteArray;
    request->clear();

    QDataStream requeststream(request,QIODevice::WriteOnly);
    requeststream.device()->seek(0);

    requeststream << TCP_PROTOCOL_HEADER
                  << (qint32)TCP_MSG_TRAINLOG_FILE_DATA
                  << (qint64)FileData.size();

    request->append(FileData);

    return request; //MUST BE DELETED BY CALLER !!!!
}

QByteArray CTCPProtocol::GetTrainLogsDownloadFinishedPacket()
{
    QByteArray request;
    QDataStream stream(&request,QIODevice::WriteOnly);
    stream.device()->seek(0);

    request.clear();
    stream << TCP_PROTOCOL_HEADER << (qint32)TCP_MSG_TRAINLOGS_DOWNLOAD_FINISHED << (qint64)0; ;

    return request;
}

QByteArray CTCPProtocol::GetDeleteZTLogsAckPacket(qint32 Acknowledge)
{
    QByteArray request;
    QDataStream stream(&request,QIODevice::WriteOnly);
    stream.device()->seek(0);

    request.clear();
    stream << TCP_PROTOCOL_HEADER << (qint32)TCP_MSG_DELETE_ZTLOG_ACK << (qint64)4 << Acknowledge;

    return request;
}

QByteArray CTCPProtocol::GetSetZTFunctionsConfigAckPacket(qint32 Ack)
{
    QByteArray request;
    QDataStream stream(&request,QIODevice::WriteOnly);
    stream.device()->seek(0);

    request.clear();
    stream << TCP_PROTOCOL_HEADER << (qint32)TCP_MSG_SET_ZT_FUNCTIONS_ACK << (qint64)4 << Ack;

    return request;
}


void CTCPProtocol::TCPTxRequest(QByteArray request)
{
    Q_UNUSED(request)
    qDebug("No reimplementation of TXRequest in TCPProtocol.c.  Request not sent...");
}

void CTCPProtocol::ResetRxStateMachine()
{
    mCurTransactionData.clear();
    mCurTransactionDataSize = 0;
    mCurTransactionMsg = TCP_MSG_UNKNOWN;
    mProtocolRxState = TCP_PROTOCOL_GET_HEADER_STATE;

}

int CTCPProtocol::DecodeTCPMessage()
{
    switch(mCurTransactionMsg)
    {
        case TCP_MSG_HEARTBEAT_REQUEST:
        {
            TCPHeartbeatRequest();
            break;
        }
        case TCP_MSG_STATION_NAME_REQUEST:
        {
            TCPStationNameRequest();
            break;
        }
        case TCP_MSG_ZT_STATUS_REQUEST:
        {
            TCPStatusRequest();
            break;
        }
        case TCP_MSG_ZTLOG_DOWNLOAD_REQUEST:
        {
            TCPZTLogDownloadRequest();
            break;
        }
        case TCP_MSG_TRAINLOGS_DOWNLOAD_REQUEST:
        {
            TCPTrainLogsDownloadRequest();
            break;
        }
        case TCP_MSG_TRAINLOG_FILE_DATA_ACK:
        {
            qint32 ack = TCP_PROTOCOL_INVALID_ACK;
            QDataStream strm(mCurTransactionData);
            strm.device()->seek(0);
            strm >> ack;

            TCPTrainLogFileDataAck((eTCPProtocolAcknowledge)ack);

            break;
        }
        case TCP_MSG_DELETE_ZTLOG_REQUEST:
        {
            TCPDeleteZTLogRequest();
            break;
        }
            case TCP_MSG_SET_ZT_FUNCTIONS:
        {
            TCPSetZTFunctionsConfigRequest(mCurTransactionData);
            break;
        }

        case TCP_MSG_SET_ZT_FUNCTIONS_ACK:
        case TCP_MSG_DELETE_ZTLOG_ACK:
        case TCP_MSG_ZT_STATUS_RESPONSE:
        case TCP_MSG_HEARTBEAT_RESPONSE:
        case TCP_MSG_STATION_NAME_RESPONSE:
        case TCP_MSG_ZTLOG_DOWNLOAD_RESPONSE:
        case TCP_MSG_TRAINSLOGS_DOWNLOAD_ACK:
        case TCP_MSG_TRAINLOG_FILE_DATA:
        case TCP_MSG_TRAINLOGS_DOWNLOAD_FINISHED:
        case TCP_MSG_UNKNOWN:
        {
            qDebug() << "Received invalid msg:" << mCurTransactionMsg;
            break;
        }
    }

    return RET_OK;
}

QDataStream &operator<<(QDataStream &out, const CTCPZTStatus &source)
{
    out << source.mZT1Status
        << source.mZT2Status
        << source.mNbPass
        << source.mNbTrigs
        << source.mZTStartDateTime
        << source.mActualDateTime
        << source.mPGTreshold
        << source.mModeMaintenanceON
        << source.mCalibModeON
        << source.mFNTKActive
        << source.mFNAnalysisActive
        << source.mPGTKActive
        << source.mPGAnalysisActive
        << source.mPP1TKActive
        << source.mPP1AnalysisActive
        << source.mPP2TKActive
        << source.mPP2AnalysisActive
        << source.mZT1TKActive
        << source.mZT1AnalysisActive
        << source.mZT2TKActive
        << source.mZT2AnalysisActive
        << source.mUSBKeyConnected;

    return out;
}
QDataStream &operator>>(QDataStream &in, CTCPZTFunctionsStatus &dest)
{
    in  >> dest.mFNTKActive
        >> dest.mFNAnalysisActive
        >> dest.mPGTKActive
        >> dest.mPGAnalysisActive
        >> dest.mPP1TKActive
        >> dest.mPP1AnalysisActive
        >> dest.mPP2TKActive
        >> dest.mPP2AnalysisActive
        >> dest.mZT1TKActive
        >> dest.mZT1AnalysisActive
        >> dest.mZT2TKActive
        >> dest.mZT2AnalysisActive;

    return in;
}

QDataStream &operator<<(QDataStream &out, const CTCPZTFunctionsStatus &source)
{
    out << source.mFNTKActive
        << source.mFNAnalysisActive
        << source.mPGTKActive
        << source.mPGAnalysisActive
        << source.mPP1TKActive
        << source.mPP1AnalysisActive
        << source.mPP2TKActive
        << source.mPP2AnalysisActive
        << source.mZT1TKActive
        << source.mZT1AnalysisActive
        << source.mZT2TKActive
        << source.mZT2AnalysisActive;

    return out;
}
