/**
 * @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"
#include "gatts_table_creat_demo.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;

BAT32A239_ACK_Structure BAT32A239_ACK;
//#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;
}
extern Protocol_uint32_t UpdateBAT32A239Protocol_Parse(const Protocol_uint8_t *pData, Protocol_uint32_t len);
extern uint32_t SwitchMode;
/**
 * @brief 串口协议服务函数,包括读取数据,解析数据,如在外部读取数据,可不调用此函数
 *
 * @warning 此函数可自定义周期调用,建议20ms周期调用,最大不可超过协议的最小发送周期
 *
 * @since 1.0.0
 */
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(SwitchMode==0)//处理升级协议
            {
                len = Protocol_Parse(mDataBufPtr, mDataBufLen);
            }
            else//处理应用协议
            {
                len = UpdateBAT32A239Protocol_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--;
            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;
    }
}

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 UpdateBAT32A239Protocol_Send(UpdateProtocolStructure SendPd)
{
    int               i        = 0;
    Protocol_uint16_t checksum = 0;
    Protocol_uint8_t  dataBuf [ 256 ];
    Protocol_uint16_t checksumXor = 0;
    Protocol_uint32_t frameLen = 0;
    Protocol_uint32_t DAT_CRC;

    if ( SendPd.LEN  > 256 )
    {
        // printf("sendProtocol data is too len !!!\n");
        return 0;
    }

    frameLen = SendPd.LEN + 7;
    

    dataBuf [ 0 ] = 0xAA;
    dataBuf [ 1 ] = 0x55;        // 同步帧头 Sync frame header
    dataBuf [ 2 ] = SendPd.CMDH; //CMDH
    dataBuf [ 3 ] = SendPd.CMDL; // CMDL
    dataBuf [ 4 ] = frameLen;
    dataBuf [ 5 ] = frameLen >> 8;    // 命令字节 Command byte
    
    if(SendPd.CMDH == CMD_FLASH_DWNLD)
    {
        
        if(SendPd.LEN > 4)
        {
            memcpy(&dataBuf[6],&SendPd.DAT[0],SendPd.LEN - 4);
            DAT_CRC=CalcCrc32(&SendPd.DAT[0], SendPd.LEN - 4 );//计算Address + Data的CRC 32
            dataBuf[frameLen-5]=(Protocol_uint8_t)(DAT_CRC);
            dataBuf[frameLen-4]=(Protocol_uint8_t)(DAT_CRC>>8);
            dataBuf[frameLen-3]=(Protocol_uint8_t)(DAT_CRC>>16);
            dataBuf[frameLen-2]=(Protocol_uint8_t)(DAT_CRC>>24);
        }
    }
    if(SendPd.CMDH == CMD_FLASH_ERASE)
    {
        memcpy(&dataBuf[6],&SendPd.DAT[0],SendPd.LEN);
    }
    for(i = 0 ;i < (frameLen - 1); i++)
    {
        checksumXor^=dataBuf[i];
    }
    dataBuf[frameLen - 1]=(Protocol_uint8_t)checksumXor;
    //printf("dataBuf[frameLen - 1] = %2x\n", dataBuf[frameLen - 1]);
    //printf("frameLen = %d\n", frameLen);
    if ( UARTSend_Cbk != Protocol_NULL )
    {
        return UARTSend_Cbk(dataBuf, frameLen);
    }
    else
    {
        return 0;
    }
}