#include "unify_protocol.h"

#ifdef DEBUG_LOG
#define LOGE(fmt, ...)                          printf("[%s:%d E] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#define LOGI(fmt, ...)                          printf("[%s:%d I] " fmt, __FUNCTION__, __LINE__, ##__VA_ARGS__)
#else
#define LOGE(fmt, ...)
#define LOGI(fmt, ...)
#endif

#define GENERAL_PREPARE_PACK_PARAM_CHECK()      do {\
                                                    if (!packBuffer || !dataBuffer)\
                                                    {\
                                                        LOGE("null point check\n");\
                                                        return -1;\
                                                    }\
                                                    if (packBufferLen < sizeof(packHead_s) + dataBufferLen)\
                                                    {\
                                                        LOGE("invalid packBufferLen %d\n", packBufferLen);\
                                                        return -1;\
                                                    }\
                                                } while(0);

#define MAX_PACK_BUFFER_LEN_CHECK()             do {\
                                                    if (realDataBuffLen + sizeof(packHead_s) > MAX_PACK_BUFFER_LEN)\
                                                    {\
                                                        LOGE("packBufferLen(%d) exceed max %d\n", packBufferLen, MAX_PACK_BUFFER_LEN);\
                                                        return -1;\
                                                    }\
                                                } while(0);

int32_t fill_pack_header(void* packHeader, int32_t width, int32_t height, int32_t size, dataType_e dataType, uint16_t dataCRC, int32_t windowID)
{
    static uint16_t seqNum  = 0;

    if (!packHeader)
        return -1;

    packHead_s* header       = (packHead_s*)packHeader;
    header->head             = HEADER_MAGIC;
    header->windowID         = windowID;
    header->seqNum           = seqNum++;
    header->dataType         = dataType;
    header->dataCheck        = dataCRC;

    switch (dataType)
    {
    case TEXT_LOG:
    case GRAPH_JPG:
        header->dataSize   = size;
        break;
    case GRAPH_RGB565:
    case GRAPH_RGB555:
    case GRAPH_GRAY8:
        header->dataSize   = height * 0x10000 + width;
        break;
    case CURVE_INT8:
    case CURVE_INT16:
    case CURVE_INT32:
    case CURVE_FLOAT:
    case WORKSPACE_FLOAT_TO_DEVICE:
    case WORKSPACE_INT32_TO_DEVICE:
        header->dataSize   = size;
        break;
    default:
        LOGE("invalid data type %d\n", dataType);
        return -1;
    }
    header->headCheck = crc_modbus((const unsigned char *)header, sizeof(packHead_s) - 2);

    LOGI("fill pack header: seq:%04d type:%d size:0x%04x width:%d height:%d crc:0x%04x\n",
        header->seqNum, header->dataType, header->dataSize, width, height, header->dataCheck);

    return 0;
}

int32_t fill_pack_for_pixel_image(void* packBuffer, int32_t packBufferLen, void* dataBuffer, int32_t dataBufferLen, dataType_e dataType, int32_t width, int32_t height)
{
    GENERAL_PREPARE_PACK_PARAM_CHECK();

    if (dataType != GRAPH_RGB565 && dataType != GRAPH_RGB555 && dataType != GRAPH_GRAY8)
    {
        LOGE("invalid dataType %d\n", dataType);
        return -1;
    }

    int32_t realDataBuffLen = 0;
    switch (dataType)
    {
    case GRAPH_RGB555:
    case GRAPH_RGB565:
        realDataBuffLen = width * height * sizeof(uint16_t);
        break;
    case GRAPH_GRAY8:
        realDataBuffLen = width * height * sizeof(uint8_t);
        break;
    default:
        break;
    }

    MAX_PACK_BUFFER_LEN_CHECK();

    int16_t crc_cal = crc_modbus((const unsigned char *)dataBuffer, realDataBuffLen);
    fill_pack_header(packBuffer, width, height, realDataBuffLen, dataType, crc_cal, 0);
    packBuffer += sizeof(packHead_s);
    memcpy(packBuffer, dataBuffer, realDataBuffLen);

    return realDataBuffLen + sizeof(packHead_s);
}

int32_t fill_pack_for_jpg(void* packBuffer, int32_t packBufferLen, void* dataBuffer, int32_t dataBufferLen, dataType_e dataType)
{
    GENERAL_PREPARE_PACK_PARAM_CHECK();

    if (dataType != GRAPH_JPG)
    {
        LOGE("invalid dataType %d\n", dataType);
        return -1;
    }

    int32_t realDataBuffLen = dataBufferLen;
    MAX_PACK_BUFFER_LEN_CHECK();

    int16_t crc_cal = crc_modbus((const unsigned char *)dataBuffer, realDataBuffLen);
    fill_pack_header(packBuffer, 0, 0, realDataBuffLen, dataType, crc_cal, 0);
    packBuffer += sizeof(packHead_s);
    memcpy(packBuffer, dataBuffer, realDataBuffLen);

    return realDataBuffLen + sizeof(packHead_s);
}

int32_t fill_pack_for_text_log(void* packBuffer, int32_t packBufferLen, int32_t windowID, const char *format, ...)
{
    va_list args;
    va_start(args, format);  // 初始化不定长参数列表

    void* dataBuffer = packBuffer + sizeof(packHead_s);
    int32_t written = vsprintf(dataBuffer, format, args);

    va_end(args);  // 结束参数列表

    int32_t realDataBuffLen = strlen(dataBuffer);
    MAX_PACK_BUFFER_LEN_CHECK();

    int16_t crc_cal = crc_modbus((const unsigned char *)dataBuffer, realDataBuffLen);
    fill_pack_header(packBuffer, 0, 0, realDataBuffLen, TEXT_LOG, crc_cal, windowID);
    packBuffer += sizeof(packHead_s);
    memcpy(packBuffer, dataBuffer, realDataBuffLen);

    return realDataBuffLen + sizeof(packHead_s);
}

int32_t fill_pack_for_curve(void* packBuffer, int32_t packBufferLen, dataType_e dataType, int32_t windowID, int32_t curveNum, ...)
{
    va_list args;       // 定义参数列表指针
    va_start(args, curveNum);

    if (dataType != CURVE_INT8 && dataType != CURVE_INT16 && dataType != CURVE_INT32 && dataType != CURVE_FLOAT)
    {
        LOGE("invalid dataType %d\n", dataType);
        return -1;
    }

    int32_t realDataBuffLen = 0;
    switch (dataType)
    {
    case CURVE_INT8:
        realDataBuffLen = curveNum;
        break;
    case CURVE_INT16:
        realDataBuffLen = curveNum * sizeof(int16_t);
        break;
    case CURVE_INT32:
        realDataBuffLen = curveNum * sizeof(int32_t);
        break;
    case CURVE_FLOAT:
        realDataBuffLen = curveNum * sizeof(float);
        break;
    default:
        break;
    }
    MAX_PACK_BUFFER_LEN_CHECK();

    if (curveNum > MAX_CURVE_NUM)
    {
        LOGE("invalid curveNum %d\n", curveNum);
        return -1;
    }

    void* dataBuffer = packBuffer + sizeof(packHead_s);
    switch (dataType)
    {
    case CURVE_INT8:
        for (int32_t i = 0; i < curveNum; i++) {
            ((int8_t*)dataBuffer)[i] = (int8_t)va_arg(args, int32_t);
        }
        break;
    case CURVE_INT16:
        for (int32_t i = 0; i < curveNum; i++) {
            ((int16_t*)dataBuffer)[i] = (int16_t)va_arg(args, int32_t);
        }
        break;
    case CURVE_INT32:
        for (int32_t i = 0; i < curveNum; i++) {
            ((int32_t*)dataBuffer)[i] = (int32_t)va_arg(args, int32_t);
        }
        break;
    case CURVE_FLOAT:
        for (int32_t i = 0; i < curveNum; i++) {
            ((float*)dataBuffer)[i] = (float)va_arg(args, double);
        }
        break;
    default:
        break;
    }

    int16_t crc_cal = crc_modbus((const unsigned char *)packBuffer + sizeof(packHead_s), realDataBuffLen);
    fill_pack_header(packBuffer, 0, 0, realDataBuffLen, dataType, crc_cal, windowID);

    return realDataBuffLen + sizeof(packHead_s);
}
