#include "dy_can.h"
#include "controlcan.h"

extern void *handle_frame_loop(void *param);
extern void *can_receive_loop(void *param);

typedef struct Baudrate_table_s
{
    unsigned int baudrate;
    unsigned char timing0;
    unsigned char timing1;
} Baudrate_table_t;

const static Baudrate_table_t Baudrate_table[] =
{
    {10000, 0x31, 0x1C},
    {20000, 0x18, 0x1C},
    {40000, 0x87, 0xFF},
    {50000, 0x09, 0x1C},
    {80000, 0x83, 0xFF},
    {100000, 0x04, 0x1C},
    {125000, 0x03, 0x1C},
    {200000, 0x81, 0xFA},
    {250000, 0x01, 0x1C},
    {400000, 0x80, 0xFA},
    {500000, 0x00, 0x1C},
    {666000, 0x80, 0xB6},
    {800000, 0x00, 0x16},
    {1000000, 0x00, 0x14},
    {33330, 0x09, 0x6F},
    {66660, 0x04, 0x6F},
    {83330, 0x03, 0x6F}
};

static int can_dev_to_port(char *dev)
{
    if (strcmp(dev, "can0") == 0)
    {
        return CANBUS_0;
    }
    if (strcmp(dev, "can1") == 0)
    {
        return CANBUS_1;
    }
    return -1;
}

static int load_can_cfg(can_cfg_t *can_cfg, char *cfg_file, PVCI_INIT_CONFIG pvci_init_config)
{
    char *data = NULL;
    cJSON *root = NULL;
    cJSON *nodes = NULL;
    cJSON *node = NULL;
    int node_cnt = 0;
    int i = 0, ret = 0, m;
    char buff[200];
    int addr[8];
    int size = 0;
    int find = 0;
    char cmd[256];

    if (can_cfg == NULL || cfg_file == NULL)
    {
        dy_syslog(LOG_ERR, "p_can_cfg:%p cfg_file:%p", can_cfg, cfg_file);
        return -1;
    }

    data = read_file_data(cfg_file);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        ret = -1;
        goto out;
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse cfg file %s error", cfg_file);
        ret = -1;
        goto out;
    }
    nodes = cJSON_GetObjectItem(root, "can_cfg");
    if (!nodes)
    {
        dy_syslog(LOG_ERR, "get can_cfg failed");
        ret = -1;
        goto out;
    }

    node_cnt = cJSON_GetArraySize(nodes);
    dy_syslog(LOG_DEBUG, "node_cnt:%d", node_cnt);
    if (node_cnt > MAX_CAN_PORT)
    {
        dy_syslog(LOG_ERR, "node_cnt:%d exceed max count", node_cnt);
        node_cnt = MAX_CAN_PORT;
    }

    for (i = 0; i < node_cnt; i++)
    {
        node = cJSON_GetArrayItem(nodes, i);
        if (node)
        {
            char dev_name[32];
            int idx;

            GET_JSON_VALUE_STRING(node, "bus", dev_name);
            idx = can_port_map(can_dev_to_port(dev_name));

            if (idx >= MAX_CAN_PORT)
            {
                dy_syslog(LOG_ERR, "dev_name:%s invalid", dev_name);
                continue;
            }

            strcpy(can_cfg->can_dev[idx].dev_name, dev_name);
            GET_JSON_VALUE_INT(node, "bitrate", can_cfg->can_dev[idx].bitrate);
            GET_JSON_VALUE_INT(node, "disable", can_cfg->can_dev[idx].disable);
            can_cfg->can_dev[idx].fd = idx;
            can_cfg->can_dev[idx].port = can_dev_to_port(dev_name);
            INIT_LIST_HEAD(&can_cfg->can_dev[idx].rcv_list);
            can_cfg->can_dev[idx].can_cfg = can_cfg;

            if (strlen(can_cfg->can_dev[idx].dev_name) && can_cfg->can_dev[idx].bitrate && can_cfg->can_dev[idx].disable == 0)
            {
                int m;

                find = 0;
                for (m = 0; m < (sizeof(Baudrate_table) / sizeof(Baudrate_table[0])); m++)
                {
                    if (Baudrate_table[m].baudrate == can_cfg->can_dev[idx].bitrate)
                    {
                        pvci_init_config[idx].Timing0 = Baudrate_table[m].timing0;
                        pvci_init_config[idx].Timing1 = Baudrate_table[m].timing1;
                        find = 1;
                        dy_syslog(LOG_INFO, "%s change bitrate %d", can_cfg->can_dev[idx].dev_name, can_cfg->can_dev[idx].bitrate);
                        break;
                    }
                }
                if (find == 0)
                {
                    dy_syslog(LOG_ERR, "%s couldn't sppuort bitrate %d", can_cfg->can_dev[idx].dev_name, can_cfg->can_dev[idx].bitrate);
                    continue; //检查是否支持此速率
                }

                int filter_en = 0;
                GET_JSON_VALUE_INT(node, "filt_en", filter_en);
                if (filter_en == 0)
                {
                    can_cfg->can_dev[idx].filter_cnt = 0;
                }
                else
                {
                    int filter_cnt = 0;
                    struct can_filter *filters = NULL;
                    int j = 0;
                    int can_id, mask;

                    cJSON *filters_node = cJSON_GetObjectItem(node, "filt");
                    if (!filters_node)
                    {
                        dy_syslog(LOG_ERR, "get filter table failed");
                        continue;
                    }

                    filter_cnt = cJSON_GetArraySize(filters_node);
                    filters = malloc(filter_cnt * sizeof(struct can_filter));
                    if (filters == NULL)
                    {
                        dy_syslog(LOG_ERR, "malloc failed");
                        continue;
                    }
                    for (j = 0; j < filter_cnt; j++)
                    {
                        char can_id_str[32];
                        char mask_str[32];

                        cJSON *filter = cJSON_GetArrayItem(filters_node, j);
                        GET_JSON_VALUE_STRING(filter, "can_id", can_id_str);
                        GET_JSON_VALUE_STRING(filter, "mask", mask_str);

                        filters[j].can_id = strtol(can_id_str, NULL, 16);
                        filters[j].can_mask = strtol(mask_str, NULL, 16);
                    }
                    can_cfg->can_dev[idx].filter_cnt = filter_cnt;
                    can_cfg->can_dev[idx].filters = filters;
                }
            }
        }
    }
    can_cfg->node_cnt = node_cnt;

out:
    cJSON_Delete(root);
    if (data != NULL)
    {
        free(data);
    }

    return ret;
}

int load_can_cfg_and_init(can_cfg_t *can_cfg, char *cfg_file)
{
    int ret = 0;
    VCI_INIT_CONFIG vci_init_config[MAX_CAN_PORT] = {0};
    int i;
    pthread_t t;

    if (VCI_OpenDevice(VCI_USBCAN2, 0, 0) == 1) //打开设备
    {
        dy_syslog(LOG_DEBUG, ">>open deivce success!\n"); //打开设备成功
    }
    else
    {
        dy_syslog(LOG_DEBUG, ">>open deivce error!\n");
        return -1;
    }

    ret = load_can_cfg(can_cfg, cfg_file, vci_init_config);
    if (ret == -1)
    {
        dy_syslog(LOG_ERR, "can load cfg failed");
        return -1;
    }


    for (i = 0; i < can_cfg->node_cnt; i++)
    {
        if (can_cfg->can_dev[i].disable == 0)
        {
            vci_init_config[i].AccCode = 0;
            vci_init_config[i].AccMask = 0xFFFFFFFF;
            vci_init_config[i].Filter = 1; //接收所有帧
            vci_init_config[i].Mode = 0;   //正常模式

            if (VCI_InitCAN(VCI_USBCAN2, 0, i, &vci_init_config[i]) != 1)
            {
                dy_syslog(LOG_DEBUG, ">>Init CAN1 error\n");
                VCI_CloseDevice(VCI_USBCAN2, 0);
            }
            if (VCI_StartCAN(VCI_USBCAN2, 0, i) != 1)
            {
                dy_syslog(LOG_DEBUG, ">>Start CAN1 error\n");
                VCI_CloseDevice(VCI_USBCAN2, 0);
            }

            pthread_create(&t, NULL, can_receive_loop, (void *)&can_cfg->can_dev[i]);
        }
    }

    return 0;
}

can_cfg_t *dy_can_cfg_init(void *obj, int (*handle_recv_frames)(void *obj, struct list_head *nodes),
                           int (*handle_recv_msg)(void *obj, can_frame_node_t *node))
{
    can_cfg_t *session = calloc(1, sizeof(can_cfg_t));

    if (session == NULL)
    {
        dy_syslog(LOG_ERR, "malloc error");
        return NULL;
    }
    session->handle_recv_frames = NULL; // TODO NOT SUPPORT receive mulit frames callback
    session->handle_recv_msg = handle_recv_msg;
    session->obj = obj;

    return session;
}