#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdint.h>
#include <pthread.h>

#include "bmser_can_init.h"
#include "bmser_can_parse_cfg.h"
#include "xieneng_can_supervisor.h"
#include "sqlite_api.h"
#include "project_userdef.h"

static int parse_reg_read_proc(cJSON *root)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(root, OPRT_PARA_NULL);

    int i = 0, j = 0, num = 0;
    int k = 0, cnt = 0;

    cJSON *reg_read = NULL, *item = NULL;
    cJSON *desc = NULL, *start = NULL, *stop = NULL, *period = NULL;
    char *reg_cfg_read_list[BMSER_REG_TYPE] = {"bau_reg_read", "bcu_reg_read", "bmu_reg_read","bdu_reg_read", "env_reg_read"};

    BMSER_CAN_BUS_CFG_S *cfg = NULL;
    BMSER_CAN_BUS_CFG_TABLE_S *cfg_table = bmser_can_get_cfg_tables_mgr();
    num = cfg_table->num;
    for (i = 0; i < num; i++) {
        cfg = &cfg_table->can_cfg[i];
        for (j = 0; j < BMSER_REG_TYPE; j++) {
            
            if (true == cfg->disable) {
                continue;
            }

            if ((j == BMSER_BAU_REG || j == BMSER_ENV_REG) && false == cfg->main_can) {
                continue;
            }

            reg_read = cJSON_GetObjectItem(root, reg_cfg_read_list[j]);
            if (NULL == reg_read) {
                continue;
            }
            cnt = cJSON_GetArraySize(reg_read);
            for (k = 0; k < cnt && k < CAN_BUS_MAX_REG_READ_CNT; k++) {
                item = cJSON_GetArrayItem(reg_read, k);
                if (NULL == item) {
                    continue;
                }
                desc    = cJSON_GetObjectItem(item, "desc");
                start   = cJSON_GetObjectItem(item, "start");
                stop    = cJSON_GetObjectItem(item, "stop");
                period  = cJSON_GetObjectItem(item, "period");

                BMSER_CHECK_PARA_NULL_RETURN_VAL(desc, OPRT_PARA_NULL);
                BMSER_CHECK_PARA_NULL_RETURN_VAL(start, OPRT_PARA_NULL);
                BMSER_CHECK_PARA_NULL_RETURN_VAL(stop, OPRT_PARA_NULL);
                BMSER_CHECK_PARA_NULL_RETURN_VAL(period, OPRT_PARA_NULL);

                cfg->reg_read_table[j].reg_read[k].start        = cJSON_GetNumberValue(start);
                cfg->reg_read_table[j].reg_read[k].stop         = cJSON_GetNumberValue(stop);      
                cfg->reg_read_table[j].reg_read[k].period_ms    = cJSON_GetNumberValue(period); 
                snprintf(cfg->reg_read_table[j].reg_read[k].desc, CAN_BUS_REG_DESC_MAX_LEN + 1, "%s", cJSON_GetStringValue(desc));
                cfg->reg_read_table[j].cnt++;
                cfg->reg_read_table[j].reg_read[k].enable = true;
                #ifdef LOCAL_DEBUG
				dy_syslog(LOG_DEBUG, "%s, desc = %s, start = %u, stop = %u, period_ms = %u.", reg_cfg_read_list[j], cfg->reg_read_table[j].reg_read[k].desc, \
                cfg->reg_read_table[j].reg_read[k].start, cfg->reg_read_table[j].reg_read[k].stop, cfg->reg_read_table[j].reg_read[k].period_ms);
				#endif
            }
        }
    }
    return 0;
}

/**
 * @brief       解析 can cfg配置
 * @param[in]   cJSON *can_cfg
 * @param[in]   int idx
 * @return      int
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-03-08           ZHY        创建初始版本          \n
 * 2023-03-08           JHC        增加BDU配置解析          \n
*/
static int parse_can_cfg_item(cJSON *can_cfg, int idx)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(can_cfg, OPRT_PARA_NULL);
    if (idx < 0 || idx >= MAX_CAN_PORT) {
        return OPRT_OVER_RANGER;
    }

    BMSER_CAN_BUS_CFG_TABLE_S *cfg_table = bmser_can_get_cfg_tables_mgr();

    int ret = 0;
    cJSON *bitrate = NULL, *bus = NULL, *disable = NULL, *rack_id_start = NULL, *rack_id_end = NULL, *main_can = NULL;
    cJSON *p_bduIdStart = NULL, *p_bduIdEnd = NULL;
    BMSER_CAN_BUS_CFG_S *cfg = &cfg_table->can_cfg[idx];

    bus             = cJSON_GetObjectItem(can_cfg, "bus");
    main_can        = cJSON_GetObjectItem(can_cfg, "main_can");
    bitrate         = cJSON_GetObjectItem(can_cfg, "bitrate");
    disable         = cJSON_GetObjectItem(can_cfg, "disable");
    rack_id_start   = cJSON_GetObjectItem(can_cfg, "rack_id_start");
    rack_id_end     = cJSON_GetObjectItem(can_cfg, "rack_id_end");
    p_bduIdStart    = cJSON_GetObjectItem(can_cfg, "bdu_id_start");
    p_bduIdEnd      = cJSON_GetObjectItem(can_cfg, "bdu_id_end");

    BMSER_CHECK_PARA_NULL_RETURN_VAL(bus, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(main_can, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(bitrate, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(disable, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(rack_id_start, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(rack_id_end, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(p_bduIdStart, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(p_bduIdEnd, OPRT_PARA_NULL);

    cfg->main_can       = cJSON_GetNumberValue(main_can);
    cfg->bitrate        = cJSON_GetNumberValue(bitrate);
    cfg->disable        = cJSON_GetNumberValue(disable);
    cfg->rack_id_start  = cJSON_GetNumberValue(rack_id_start);
    cfg->rack_id_end    = cJSON_GetNumberValue(rack_id_end);
    cfg->bdu_id_start   = cJSON_GetNumberValue(p_bduIdStart);
    cfg->bdu_id_end     = cJSON_GetNumberValue(p_bduIdEnd);
    snprintf(cfg->name, CAN_BUS_NAME_MAX_LEN + 1, "%s", cJSON_GetStringValue(bus));
    
    cfg_table->num++;

	#ifdef LOCAL_DEBUG
    dy_syslog(LOG_DEBUG, "main can = %d, bitrate = %u, disable = %d, rack_id_start = %hhu, rack_id_end = %hhu, can_name = %s.", \
    cfg->main_can, cfg->bitrate, cfg->disable, cfg->rack_id_start, cfg->rack_id_end, cfg->name);
	#endif
    return 0;
}

/**
 * @brief       解析 general_cfg cfg配置
 * @param[in]   cJSON *general_cfg
 * @return      int
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-08-08           QZY        创建初始版本          \n
*/
static int parse_general_cfg_item(cJSON *general_cfg)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(general_cfg, OPRT_PARA_NULL);
    BMSER_CAN_BUS_CFG_TABLE_S *cfg_table = bmser_can_get_cfg_tables_mgr();
    BMSER_CAN_GENERAL_CFG_S *genCfg = &cfg_table->general_cfg;
    int ret = 0;
    cJSON *j1939_req_enable = NULL, *j1939_listen_enable = NULL;
    cJSON *j1939_send_detail_only = NULL, *j1939_send_detail_compress = NULL;

    j1939_req_enable     = cJSON_GetObjectItem(general_cfg, "j1939_send_enable");
    j1939_listen_enable  = cJSON_GetObjectItem(general_cfg, "j1939_recv_enable");
    j1939_send_detail_only  = cJSON_GetObjectItem(general_cfg, "j1939_send_detail_only");
    j1939_send_detail_compress = cJSON_GetObjectItem(general_cfg, "j1939_send_detail_compress");

    BMSER_CHECK_PARA_NULL_RETURN_VAL(j1939_req_enable, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(j1939_listen_enable, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(j1939_send_detail_only, OPRT_PARA_NULL);
    BMSER_CHECK_PARA_NULL_RETURN_VAL(j1939_send_detail_compress, OPRT_PARA_NULL);

    genCfg->j1939_req_enable     = cJSON_GetNumberValue(j1939_req_enable);
    genCfg->j1939_listen_enable  = cJSON_GetNumberValue(j1939_listen_enable);
    genCfg->j1939_send_detail_only  = cJSON_GetNumberValue(j1939_send_detail_only);
    genCfg->j1939_send_detail_compress = cJSON_GetNumberValue(j1939_send_detail_compress);

    return 0;
}

/**
 * @description: 解析canbus json配置文件 
 * @param {char} *can_cfg_file 文件路径名
 * @return {*} 0:success, others:fail
 */
int bmser_can_parse_cfg_file(const char* can_cfg_file)
{
    BMSER_CHECK_PARA_NULL_RETURN_VAL(can_cfg_file, OPRT_PARA_NULL);
    
    char *p = NULL;
    FILE *fd = NULL;
    int ret = 0, len = 0, read_len = 0, i = 0, node_cnt = 0;
    cJSON *root = NULL, *item = NULL, *can_cfg = NULL, *general_cfg = NULL;
    
    fd = fopen(can_cfg_file, "r");
    BMSER_CHECK_PARA_NULL_RETURN_VAL(fd, OPRT_OPEN_FILE_FAIL);

    fseek(fd, 0, SEEK_END);
    len = ftell(fd);
    if (len <= 0) {
        goto ERR_EXIT;
    }
    
    fseek(fd, 0, SEEK_SET);

    p = (char *)malloc(len + 1);
    BMSER_CHECK_PARA_NULL_GOTO(p, ERR_EXIT);

    memset(p, 0, len + 1);
    read_len = fread(p, 1, len, fd);
    if (read_len != len) {
        goto ERR_EXIT;
    }
    fclose(fd);
    fd = NULL;

    root = cJSON_Parse((const char *)p);
    BMSER_CHECK_PARA_NULL_GOTO(root, ERR_EXIT);
    free(p);
    p = NULL;

    // can_cfg = cJSON_GetObjectItem(root, "can_cfg");
    // BMSER_CHECK_PARA_NULL_GOTO(can_cfg, ERR_EXIT);

    // node_cnt = cJSON_GetArraySize(can_cfg);
    // for (i = 0; i < node_cnt && i < MAX_CAN_PORT + 1; i++) {
    //     item = cJSON_GetArrayItem(can_cfg, i);
    //     BMSER_CALL_FUNC_RETURN_VAL(parse_can_cfg_item(item, i));
    // }

    general_cfg = cJSON_GetObjectItem(root, "general_cfg");
    BMSER_CHECK_PARA_NULL_GOTO(general_cfg, ERR_EXIT);
    BMSER_CALL_FUNC_RETURN_VAL(parse_general_cfg_item(general_cfg));

    BMSER_CALL_FUNC_RETURN_VAL(parse_reg_read_proc(root));
    cJSON_Delete(root);
    return 0;

ERR_EXIT:
    if (root) {
        cJSON_Delete(root);
        root = NULL;
    }    
    if (p) {
        free(p);
        p = NULL;
    }
    if (fd) {
        fclose(fd);
        fd = NULL;
    }
    return OPRT_COMM_ERROR;
}

/**
 * @brief       从DB3配置文件解析can总线配置
 * @return      int 
 * @retval      0:正常  !0:错误
 * @par         修改日志:
 * Date                Author     Description           \n
 * 2023-09-22           JHC        创建初始版本          \n
*/
int bmser_can_parse_db3_canbusConfig()
{
    int32_t idx = 0, ret = 0;
    sqlResult_t sqlResult = {0};
    int32_t bus1racknum = 0, bus2racknum = 0, canconfigNum = 0;
    BMSER_CAN_BUS_CFG_TABLE_S *cfg_table = bmser_can_get_cfg_tables_mgr();

    BMSER_CALL_FUNC_RETURN_VAL(sqliteApi_query(INSTALL_PARA_TABLE,INSTALL_PARA_BUS1_RACK_NUM_KEY,(idx+1),INTEGER,&sqlResult));
    bus1racknum = sqlResult.integer;
    cfg_table->can_cfg[0].rack_id_start = 1;
    cfg_table->can_cfg[0].rack_id_end = bus1racknum;

    dy_syslog(LOG_DEBUG, "rack_id_end bus1racknum = %d.", bus1racknum);

    BMSER_CALL_FUNC_RETURN_VAL(sqliteApi_query(INSTALL_PARA_TABLE,INSTALL_PARA_BUS2_RACK_NUM_KEY,(idx+1),INTEGER,&sqlResult));
    bus2racknum = sqlResult.integer;
    cfg_table->can_cfg[1].rack_id_start = bus1racknum + 1;
    cfg_table->can_cfg[1].rack_id_end = bus1racknum+bus2racknum;

    dy_syslog(LOG_DEBUG, "bus2racknum = %d.", bus2racknum);

    sqliteApi_getRowCount(CANBUS_CONFIG_TABLE,INTEGER,&sqlResult);
    canconfigNum = sqlResult.integer;
    cfg_table->num = MIN(canconfigNum,CAN_OBJ_ID_MAX);
    for(idx=0;idx<MIN(canconfigNum,CAN_OBJ_ID_MAX);idx++)
    {
        //这一版本BDU数据只需要监听即可，不需要主动去读取
        cfg_table->can_cfg[idx].bdu_id_start = 0;
        cfg_table->can_cfg[idx].bdu_id_end = 0;
        //先赋值一个默认数据
        cfg_table->can_cfg[idx].bitrate = 500000;
        cfg_table->can_cfg[idx].disable = 1;
        cfg_table->can_cfg[idx].main_can = 0;
        if(0 == sqliteApi_query(CANBUS_CONFIG_TABLE,CANBUS_CONFIG_BAUDRATE_KEY,(idx+1),INTEGER,&sqlResult))
        {
            cfg_table->can_cfg[idx].bitrate = sqlResult.integer;
            dy_syslog(LOG_DEBUG,"can_cfg[%d].bitrate=%d\r\n",idx,cfg_table->can_cfg[idx].bitrate);
        }else{ ret |= -1;}
        if(0 == sqliteApi_query(CANBUS_CONFIG_TABLE,CANBUS_CONFIG_IS_DISABLE_KEY,(idx+1),INTEGER,&sqlResult))
        {
            cfg_table->can_cfg[idx].disable = sqlResult.integer;
            dy_syslog(LOG_DEBUG,"can_cfg[%d].disable=%d\r\n",idx,cfg_table->can_cfg[idx].disable);
        }else{ ret |= -1;}
        if(0 == sqliteApi_query(CANBUS_CONFIG_TABLE,CANBUS_CONFIG_MASTER_CAN_KEY,(idx+1),INTEGER,&sqlResult))
        {
            cfg_table->can_cfg[idx].main_can = sqlResult.integer;
            dy_syslog(LOG_DEBUG,"can_cfg[%d].main_can=%d\r\n",idx,cfg_table->can_cfg[idx].main_can);
        }else{ ret |= -1;}
        if(0 == sqliteApi_query(CANBUS_CONFIG_TABLE,CANBUS_CONFIG_BUS_KEY,(idx+1),STRING,&sqlResult))
        {
            snprintf(cfg_table->can_cfg[idx].name, CAN_BUS_NAME_MAX_LEN + 1, "%s", sqlResult.string);
            dy_syslog(LOG_DEBUG,"can_cfg[%d].name=%s\r\n",idx,cfg_table->can_cfg[idx].name);
        }else{ ret |= -1;}
    }

    return ret;
}