#include "dy_can.h"

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

const static int bitrate_tab[] = {1000000, 500000, 250000, 125000, 100000, 50000};

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)
{
    char *data = NULL;
    cJSON *root = NULL;
    cJSON *nodes = NULL;
    cJSON *node = NULL;
    int node_cnt = 0;
    int i = 0, ret = 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].port = can_dev_to_port(dev_name);
            INIT_LIST_HEAD(&can_cfg->can_dev[idx].rcv_list);

            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(bitrate_tab) / sizeof(bitrate_tab[0])); m++)
                {
                    if (bitrate_tab[m] == can_cfg->can_dev[idx].bitrate)
                    {
                        find = 1;
                        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; //检查是否支持此速率
                }
                dy_syslog(LOG_INFO, "%s change bitrate %d", can_cfg->can_dev[idx].dev_name, can_cfg->can_dev[idx].bitrate);
                sprintf(cmd, "ifconfig %s down;ip link set %s type can bitrate %d triple-sampling on;ifconfig %s up;", can_cfg->can_dev[idx].dev_name, can_cfg->can_dev[idx].dev_name, can_cfg->can_dev[idx].bitrate, can_cfg->can_dev[idx].dev_name);
                system(cmd);

                // filter
                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;

                    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;
}

static int can_init(can_cfg_t *can_cfg)
{
    int i;
    struct epoll_event ev;

    pthread_mutex_init(&can_cfg->recv_lock, NULL);
    lnxall_condvar_init(&can_cfg->recv_cond);

    if (can_cfg->node_cnt <= 0)
    {
        dy_syslog(LOG_INFO, "no can node avalible");
        return 0;
    }

    can_cfg->events = malloc(MAXEPOLLSIZE * sizeof(struct epoll_event));
    if (can_cfg->events == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return -1;
    }

    can_cfg->epollfd = epoll_create(MAXEPOLLSIZE);
    if (can_cfg->epollfd <= 0)
    {
        dy_syslog(LOG_ERR, "Epoll create error");
        return -1;
    }

    for (i = 0; i < can_cfg->node_cnt; i++)
    {
        if (can_cfg->can_dev[i].disable == 0)
        {
            can_cfg->can_dev[i].fd = can_open(can_cfg->can_dev[i].dev_name);
            if (can_cfg->can_dev[i].fd <= 0)
            {
                dy_syslog(LOG_ERR, "dev:%s open failed", can_cfg->can_dev[i].dev_name);
                continue;
            }

            if (can_cfg->can_dev[i].filter_cnt > 0)
            {
                setsockopt(can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, can_cfg->can_dev[i].filters, can_cfg->can_dev[i].filter_cnt * sizeof(struct can_filter));
            }
            else
            {
                // setsockopt(can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_FILTER, NULL, 0);
            }

            /*关闭回环模式*/
            int loopback = 0; /* 0 = disabled, 1 = enabled (default) */
            setsockopt(can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_LOOPBACK, &loopback, sizeof(loopback));

            /*关闭自收自发*/
            int recv_own_msgs = 0; /* 0 = disabled (default), 1 = enabled */
            setsockopt(can_cfg->can_dev[i].fd, SOL_CAN_RAW, CAN_RAW_RECV_OWN_MSGS, &recv_own_msgs, sizeof(recv_own_msgs));

            ev.events = EPOLLIN | EPOLLET;
            ev.data.fd = can_cfg->can_dev[i].fd;
            if (epoll_ctl(can_cfg->epollfd, EPOLL_CTL_ADD, can_cfg->can_dev[i].fd, &ev) < 0)
            {
                dy_syslog(LOG_ERR, "epoll set insertion error: fd=%d", can_cfg->can_dev[i].fd);
                continue;
            }
        }
        else
        {
            can_cfg->can_dev[i].fd = -1;
        }
    }
    return 0;
}

int load_can_cfg_and_init(can_cfg_t *can_cfg, char *cfg_file)
{
    int ret = 0;
    pthread_t can_thread;
    pthread_t frame_thread;

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

    ret = can_init(can_cfg);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "epoll init failed");
        return -1;
    }

    pthread_create(&can_thread, NULL, can_receive_loop, (void *)can_cfg);
    pthread_create(&frame_thread, NULL, handle_frame_loop, (void *)can_cfg);

    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 = handle_recv_frames;
    session->handle_recv_msg = handle_recv_msg;
    session->obj = obj;

    return session;
}
