/**
 * @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 "debugger.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
 */
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;
            // 解析协议
            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_uint8_t  frameSum;           // 校验和 Checksum
    Protocol_uint8_t  calcSum = 0;        // 计算得到的校验和 Calculated Checksum
    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 [ 3 ];
        // 计算帧总长度 (起始字节2 + ID1 + Len1 + 数据内容dataLen + 校验和1 + 结束字节2)
        frameLen = 2 + 1 + 1 + dataLen + 1 + 2;

        if ( frameLen > remainLen )
        {
            // 数据内容不全
#ifdef DEBUG_PRO_DATA
            d_printf("Incomplete data packet!!!!!!\n");
#endif
            break;
        }

        // 检查结束字节
        if ( (pData [ frameLen - 2 ] != CMD_END1) || (pData [ frameLen - 1 ] != CMD_END2) )
        {
#ifdef DEBUG_PRO_DATA
            d_printf("End bytes error!!!!!!\n");
#endif
            pData++;
            remainLen--;
            continue;
        }

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

        // 计算和校验 (ID + Len + 数据内容)
        calcSum = 0;
        for ( i = 2; i < frameLen - 3; i++ )
        {
            calcSum += pData [ i ];
        }

        // 获取接收到的校验和
        frameSum = pData [ frameLen - 3 ];

        if ( calcSum == frameSum )
        {
            // 解析一帧数据
            if ( ProcParseCbk != Protocol_NULL )
            {
                // 传递从ID开始到校验和之前的数据
                ProcParseCbk(pData + 2, dataLen + 2);
            }

            if ( ProtocolSetData_Cbk != Protocol_NULL )
            {
                // 填充协议数据结构
                ProcData.ID      = pData [ 2 ];
                ProcData.DataLen = dataLen;
                // 复制数据内容
                memcpy(ProcData.Data, pData + 4, dataLen);
                // 回调处理
                ProtocolSetData_Cbk(&ProcData);
            }
        }
        else
        {
#ifdef DEBUG_PRO_DATA
            d_printf("CheckSum error: calc = %x, recv = %x!!!!!!\n", calcSum, 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;
    Protocol_uint8_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 ] = ( Protocol_uint8_t )cmdID;    // 报文ID
    dataBuf [ 3 ] = len;                          // 数据长度

    frameLen = 4;

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

    // 计算校验和 (ID + Len + 数据内容)
    checksum = 0;
    for ( i = 2; i < frameLen; i++ )
    {
        checksum += dataBuf [ i ];
    }

    // 添加校验和
    dataBuf [ frameLen ] = checksum;
    frameLen++;

    // 添加结束字节
    dataBuf [ frameLen ] = CMD_END1;
    frameLen++;
    dataBuf [ frameLen ] = CMD_END2;
    frameLen++;

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