/**
 * @file        Protocol_Lib.c
 * @brief       串口协议解析
 * @details     串口协议解析
 * @author      myliu
 * @date        2022.05.09
 * @version     V1.0
 * @copyright   myiu
 */

#include <stdio.h>
#include <string.h>
#include "Protocol_CRC16.h"
static UARTOpen          UARTOpen_Cbk;
static UARTSend          UARTSend_Cbk;
static UARTRead          UARTRead_Cbk;
static ProcParse         ProcParseCbk;
static UARTClose         UARTClose_Cbk;
static ProtocolSetData   ProtocolSetData_Cbk;
static Protocol_uint8_t *mDataBufPtr   = Protocol_NULL;
static Protocol_uint16_t mDataBufLen   = 0;
static Protocol_uint32_t DataBufMaxLen = 0;
//#define DEBUG_PRO_DATA 0
/**
 * @brief 初始化函数
 * @param[in] pMemSpace 分配给协议库的内存空间，用来缓存串口数据
 * @param[in] MemLen    分配的内存空间大小
 * @param[in] pFunc     回调函数，包括串口打开、发送、读取、关闭，也包括解析后数据回传
 * @param[in] ProcParseCbk 此回调函数，返回数据从FrameNo开始到CRC16之前，和ProtocolSetData_Cbk二选一
 * @param[in] ProtocolSetData_Cbk 此回调函数，返回数据区分命令字、电源状态等，和ProcParseCbk二选一，详见结构体Protocol_Data_t
 *
 * @warning 此函数KL30和Wakeup都要调用，否则未分配内存，功能不好使，也有可能造成野指针复位
 *
 * @since 1.0.0
 */
void Protocol_Init(Protocol_uint8_t *pMemSpace, Protocol_uint32_t MemLen, Protocol_Func_t *pFunc)
{
    mDataBufPtr         = pMemSpace;
    DataBufMaxLen       = MemLen;
    UARTOpen_Cbk        = pFunc->UARTOpen_Cbk;
    UARTSend_Cbk        = pFunc->UARTSend_Cbk;
    UARTRead_Cbk        = pFunc->UARTRead_Cbk;
    ProcParseCbk        = pFunc->ProcParseCbk;
    UARTClose_Cbk       = pFunc->UARTClose_Cbk;
    ProtocolSetData_Cbk = pFunc->ProtocolSetData_Cbk;

    if ( UARTOpen_Cbk != Protocol_NULL )
    {
        UARTOpen_Cbk( );
    }

    return;
}

/**
 * @brief 串口协议服务函数，包括读取数据，解析数据，如在外部读取数据，可不调用此函数
 *
 * @warning 此函数可自定义周期调用，建议20ms周期调用，最大不可超过协议的最小发送周期
 *
 * @since 1.0.0
 */
extern Protocol_uint32_t UpgradeStep;
void Protocol_Service(void)
{
    int               len;
    Protocol_uint32_t readNum = 0;

    if ( UARTRead_Cbk != Protocol_NULL )
    {
        readNum = UARTRead_Cbk(mDataBufPtr + mDataBufLen, 256 - mDataBufLen);
        if ( readNum > 0 )
        {
            mDataBufLen += readNum;
            // 解析协议
            if (UpgradeStep<10)//此时解析的是升级N32G031升级协议
            {
                len = UpdateN32G031Protocol_Parse(mDataBufPtr, mDataBufLen);
            }
            else//此时解析的是App协议
            {
                len = Protocol_Parse(mDataBufPtr, mDataBufLen);
            }
            if ( (len > 0) && (len < mDataBufLen) )
            {
                // 将未解析的数据移到头部
                // Move unparsed data to the head
                memcpy(mDataBufPtr, mDataBufPtr + len, mDataBufLen - len);
            }

            mDataBufLen -= len;
        }
    }
}

/**
 * @brief 协议解析函数，如外部获取数据（例如中断），可直接调用此函数解析数据
 * @param[in] pData     协议数据内容
 * @param[in] len       需要处理的协议数据长度
 *
 * @return    剩余已处理的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Parse(const Protocol_uint8_t *pData, Protocol_uint32_t len)
{
    Protocol_uint32_t remainLen = len;    // 剩余数据长度 Remaining data length
    Protocol_uint32_t dataLen;            // 数据包长度 Packet length
    Protocol_uint32_t frameLen;           // 帧长度 Frame length
    Protocol_uint32_t frameSum;
    Protocol_Data_t   ProcData;
    int               i = 0;
    int               dataBuf [ 256 ];

    /**
     * 以下部分需要根据协议格式进行相应的修改，解析出每一帧的数据
     */
    while ( remainLen >= DATA_PACKAGE_MIN_LEN )
    {
        // 找到一帧数据的数据头
        // Find the data header of a frame of data
        while ( (remainLen >= 2) && ((pData [ 0 ] != CMD_HEAD1) || (pData [ 1 ] != CMD_HEAD2)) )
        {
            pData++;
            remainLen--;
            //printf("CMD_HEAD1 || CMD_HEAD2 error \n");
            continue;
        }

        if ( remainLen < DATA_PACKAGE_MIN_LEN )
        {
#ifdef DEBUG_PRO_DATA

            d_printf("too short!!!!!!\n");
#endif
            break;
        }

        dataLen  = pData [ 2 ];
        frameLen = dataLen + DATA_PACKAGE_FIXED_LEN;
        if ( frameLen > remainLen )
        {
            // 数据内容不全
#ifdef DEBUG_PRO_DATA

            d_printf("Incomplete data packet!!!!!!\n");
#endif
            break;
        }

        // 打印一帧数据，需要时在CommDef.h文件中打开DEBUG_PRO_DATA宏
#ifdef DEBUG_PRO_DATA
        for ( i = 0; i < frameLen; ++i )
        {
            d_printf("%x ", pData [ i ]);
        }
        d_printf("\n");
#endif

        // 检测校验码 Checksum
        frameSum = (pData [ frameLen - 2 ] << 8) | (pData [ frameLen - 1 ]);
        if ( frameLen > 4 )
        {
            if ( getCheckSum(pData + 2, frameLen - 4) == frameSum )
            {
                // 解析一帧数据
                if ( ProcParseCbk != Protocol_NULL )
                {
                    ProcParseCbk(pData + 3, dataLen - 2);
                }

                if ( ProtocolSetData_Cbk != Protocol_NULL )
                {
                    ProcData.FrameNo  = pData [ 3 ];
                    ProcData.PowerSts = (pData [ 4 ] >> 6) & 0x03;
                    ProcData.CmdID    = pData [ 4 ] & 0x3F;
                    if ( ProcData.CmdID == 0x10 )
                    {
#ifdef DEBUG_PRO_DATA
                        for ( i = 0; i < frameLen; ++i )
                        {
                            d_printf("%x ", pData [ i ]);
                        }
                        d_printf("\n");
#endif
                    }
                    if ( ProcData.CmdID == 0x12 )
                    {
#ifdef DEBUG_PRO_DATA
                        for ( i = 0; i < frameLen; ++i )
                        {
                            d_printf("%x ", pData [ i ]);
                        }
                        d_printf("\n");
#endif
                    }
                    ProcData.DataLen = dataLen - 4;
                    memcpy(ProcData.Data, pData + 5, ProcData.DataLen);
                    ProtocolSetData_Cbk(&ProcData);
                }
            }
            else
            {
                for ( i = 0; i < frameLen; ++i )
                {
                    dataBuf [ i ] = pData [ i ];
                }
                i = 0;
#ifdef DEBUG_PRO_DATA

                d_printf("CheckSum error: new = %x, old = %x!!!!!!\n", getCheckSum(pData + 2, frameLen - 4), frameSum);
#endif
            }
        }

        pData += frameLen;
        remainLen -= frameLen;
    }

    return len - remainLen;
}

/**
 * 根据协议格式进行拼接
 */
/**
 * @brief 串口协议数据拼接，如初始化发送函数，调用此函数后，数据已通过串口发送
 * @param[in] cmdID     命令字
 * @param[in] pData     协议数据内容（不包括协议头、长度、帧序号、命令字、校验和，从数据域算起）
 * @param[in] len       数据域长度
 *
 * @return    已发送的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t Protocol_Send(const Protocol_uint16_t cmdID, const Protocol_uint8_t *pData, Protocol_uint8_t len)
{
    int               i        = 0;
    Protocol_uint16_t checksum = 0;
    Protocol_uint8_t  dataBuf [ 256 ];
    Protocol_uint32_t frameLen;
    if ( len + DATA_PACKAGE_MIN_LEN > 256 )
    {
          //  printf("sendProtocol data is too len !!!\n");
        return 0;
    }

    dataBuf [ 0 ] = CMD_HEAD1;
    dataBuf [ 1 ] = CMD_HEAD2;    // 同步帧头 Sync frame header

    dataBuf [ 2 ] = len + 4;
    dataBuf [ 3 ] = 0;    // 命令字节 Command byte

    dataBuf [ 4 ] = ( Protocol_uint8_t )cmdID;

    frameLen = 5;

    // 数据 Data
    for ( i = 0; i < len; ++i )
    {
        dataBuf [ frameLen ] = pData [ i ];
        frameLen++;
    }

    // 校验码 Checksum
    checksum             = getCheckSum(dataBuf + 2, frameLen - 2);
    dataBuf [ frameLen ] = (checksum >> 8) & 0x00FF;
    frameLen++;
    dataBuf [ frameLen ] = checksum & 0x00FF;
    frameLen++;

    if ( UARTSend_Cbk != Protocol_NULL )
    {
        return UARTSend_Cbk(dataBuf, frameLen);
    }
    else
    {
        return 0;
    }
}


N32G031_ACK_Structure N32G031_ACK;
/**
 * @brief 协议解析函数，如外部获取数据（例如中断），可直接调用此函数解析数据
 * @param[in] pData     协议数据内容
 * @param[in] len       需要处理的协议数据长度
 *
 * @return    剩余已处理的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t UpdateN32G031Protocol_Parse(const Protocol_uint8_t *pData, Protocol_uint32_t len)
{
    Protocol_uint8_t remainLen = len;    // 剩余数据长度 Remaining data length
    Protocol_uint8_t dataLen;            // 数据包长度 Packet length
    Protocol_uint8_t frameLen=0;           // 帧长度 Frame length
    Protocol_uint8_t  checksumXor=0;

    int               i = 0;

    /**
     * 以下部分需要根据协议格式进行相应的修改，解析出每一帧的数据
     */
    while ( remainLen >= 9 )
    {
        // 找到一帧数据的数据头
        // Find the data header of a frame of data
        while ( (remainLen >= 2) && ((pData [ 0 ] != 0xAA) || (pData [ 1 ] != 0x55)) )
        {
            pData++;
            remainLen--;
            continue;
        }
        
        dataLen  = pData [ 4 ];//实际是[4][5]小端排序，但实际么有那么多直接取低字节
        // 0     1        2       3      4     5       6~n     6+n+1  6+n+2  6+n+3    
        //STA1 + STA2+{< CMD_H + CMD_L + LENL+LENH > + (DAT) + <CR1 + CR2> }+XOR
        frameLen = dataLen + 9;//准确的一帧数据长度
        
        if ( frameLen > remainLen )// 数据内容不全
        {           
#ifdef DEBUG_PRO_DATA
            printf("Incomplete data packet!!!!!!\n");
#endif
            break;
        }
        
        // checksumXor=pData[0];
        /*在 BOOT V1.0 的版本（获取版本信息命令见章节 2.3.2），异或运算不对 CR2
        运算，只对 CR2 前面的字节进行运算， 即（STA1 + STA2 + {< CMD_H + CMD_L 
        + LEN > + (DAT) + <CR1 >}）。
        */
       if(remainLen==frameLen)
       {
            for ( i = 0; i < remainLen-2; i++)
            {
                checksumXor^=pData[i];
            }
            // printf("Dataxor =%2x-- pData[remainLen-2] = %2x",checksumXor,pData[remainLen-2] );
       }
        if (checksumXor!=pData[remainLen-1])//抛弃坏帧
        {
        // 打印一帧数据，需要时在CommDef.h文件中打开DEBUG_PRO_DATA宏
#ifdef DEBUG_PRO_DATA
        for ( i = 0; i < frameLen; ++i )
        {
            printf("%x ", pData [ i ]);
        }printf("\n");
#endif
            break;
        }
        memcpy(&N32G031_ACK.CMDH,&pData[2],frameLen-1);

        pData += frameLen;
        remainLen -= frameLen;
    }

    return len - remainLen;
}



/**
 * 计算发送程序的CRC32
*/
Protocol_uint32_t CalcCrc32(Protocol_uint8_t buf[], int Len);

/**
 * 根据协议格式进行拼接
 */
/**
 * @brief 串口协议数据拼接，如初始化发送函数，调用此函数后，数据已通过串口发送
 * @param[in] cmdID     命令字
 * @param[in] pData     协议数据内容（不包括协议头、长度、帧序号、命令字、校验和，从数据域算起）
 * @param[in] len       数据域长度
 *
 * @return    已发送的数据长度
 *
 * @since 1.0.0
 */
Protocol_uint32_t UpdateN32G031Protocol_Send(UpdateProtocolStructure SendPd)
{
    int               i        = 0;
    static Protocol_uint8_t  dataBuf [ 256 ];
    Protocol_uint16_t checksumXor = 0;
    Protocol_uint8_t frameLen;
    Protocol_uint32_t DAT_CRC;
    dataBuf [ 0 ] = 0xAA;
    dataBuf [ 1 ] = 0x55;    // 同步帧头 Sync frame header

    dataBuf [ 2 ] = SendPd.CMDH; //CMDH
    dataBuf [ 3 ] = SendPd.CMDL; // CMDL
    dataBuf [ 4 ] = ( Protocol_uint8_t )SendPd.LEN;
    dataBuf [ 5 ] = ( Protocol_uint8_t )(SendPd.LEN>>8);
    dataBuf [ 6 ] = SendPd.PAR[0];
    dataBuf [ 7 ] = SendPd.PAR[1];
    dataBuf [ 8 ] = SendPd.PAR[2];
    dataBuf [ 9 ] = SendPd.PAR[3]; 
    if (SendPd.LEN>0)
    {
        memcpy(&dataBuf[10],&SendPd.DAT[0],SendPd.LEN);
    }

    if (SendPd.CMDH==CMD_FLASH_DWNLD)
    {
        DAT_CRC=CalcCrc32(&SendPd.DAT[16], SendPd.LEN-16-4 );//减掉CRC和前16字节DAT
        dataBuf[10+SendPd.LEN-4]=(Protocol_uint8_t)(DAT_CRC);
        dataBuf[11+SendPd.LEN-4]=(Protocol_uint8_t)(DAT_CRC>>8);
        dataBuf[12+SendPd.LEN-4]=(Protocol_uint8_t)(DAT_CRC>>16);
        dataBuf[13+SendPd.LEN-4]=(Protocol_uint8_t)(DAT_CRC>>24);
    }
    //总发送字节数包括校验位
    frameLen=10+SendPd.LEN;
    // 校验码 checksumXor
    for(i=0;i<frameLen;i++)
    {
        checksumXor^=dataBuf[i];
    }
    dataBuf[frameLen]=(Protocol_uint8_t)checksumXor;

    if ( UARTSend_Cbk != Protocol_NULL )
    {
        return UARTSend_Cbk(dataBuf, frameLen+1);
    }
    else
    {
        return 0;
    }
}

Protocol_uint32_t CalcCrc32(Protocol_uint8_t buf[], int Len)
{
    int i,j;
    int n = 0;
    Protocol_uint8_t LE32buf[4];
    Protocol_uint32_t crc = 0xFFFFFFFF; // Initial value
    
    for (n = 0; n < Len; n+=4) 
    {
      LE32buf[0]=buf[n+3];//[n+0];//
      LE32buf[1]=buf[n+2];//[n+1];//
      LE32buf[2]=buf[n+1];//[n+2];//
      LE32buf[3]=buf[n+0];//[n+3];//
      for(j=0;j<4;j++)
      {
        crc ^= (Protocol_uint32_t)LE32buf[j] << 24; 
        for (i = 0; i < 8; ++i)
        {
            if (crc & 0x80000000) 
            {
              crc = (crc << 1) ^ 0x04C11DB7; // 0xEDB88320= reverse 0x04C11DB7
            } 
            else 
            {
              crc = (crc << 1);
            }
        }    
      }


    }
    
    return crc;
}

