#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <linux/can.h>
#include <linux/can/raw.h>

#include "can_base.h"

typedef struct
{
    unsigned int canObjId;
    int socket;
    int bitrate;
    char * canDev;
}CAN_BASE_INFO;

#define CHECK_CAN_OBJ_ID(objId) \
    if(objId >= MAX_CAN_PORT)                \
    {                    \
        dy_syslog(LOG_ERR, "can obj Id not support,only support 0 or 1.");   \
        return -1;      \
    }                   \

CAN_BASE_INFO g_baseInfo[MAX_CAN_PORT] = 
{
    {0,-1,500000,DEV_CAN0},
    {0,-1,500000,DEV_CAN1},
    {0,-1,500000,DEV_CAN2},
};

int can_set_bitrate(unsigned int canObjId, int bitrate)
{
    char cmd[128];
    char *dev = g_baseInfo[canObjId].canDev;

    //TODO:后面需要实现按照采样点来配置波特率
    CHECK_CAN_OBJ_ID(canObjId)
    dy_syslog(LOG_DEBUG, "dev:%s, bitrate = %d", dev, bitrate);

    g_baseInfo[canObjId].bitrate = bitrate;
    memset(cmd, 0, sizeof(cmd));
    //FIXME: 虽然设置了自动重启的延迟时间，但实际不会生效
    sprintf(cmd, "ifconfig %s down;ip link set %s type can bitrate %d restart-ms %d;ifconfig %s up;",\
            dev, dev, bitrate, RESTART_TIME, dev);
    return system(cmd);
}

int can_setFilter(unsigned int canObjId, CanFilter *canFilter,unsigned int filterNum)
{
    unsigned int i = 0;
    struct can_filter rfilter[CAN_RAW_FILTER_MAX];

    CHECK_CAN_OBJ_ID(canObjId)

    for(i =0; i < MIN(filterNum,CAN_RAW_FILTER_MAX); i++)
    {
        rfilter[i].can_id = canFilter[i].canId;
        if(canFilter[i].ide == CAN_ID_EXT)
        {
            rfilter[i].can_id |= CAN_EFF_FLAG;
        }
        rfilter[i].can_mask = canFilter[i].canMask;
    }
    return setsockopt(g_baseInfo[canObjId].socket, SOL_CAN_RAW, CAN_RAW_FILTER, &rfilter, (sizeof(struct can_filter)*MIN(filterNum,CAN_RAW_FILTER_MAX)));
}

int can_send(unsigned int canObjId, CanMsg *txMsg)
{
    int ret = 0;
    int retry_cnt = 0;
    int socket = g_baseInfo[canObjId].socket;
    struct can_frame frame;

    // TODO:发送保序待实现
    CHECK_CAN_OBJ_ID(canObjId)

    frame.can_dlc = txMsg->DLC;
    frame.can_id = txMsg->CanId;
    if(txMsg->IDE == CAN_ID_EXT)
    {
        frame.can_id |= CAN_EFF_FLAG;
    }
    memcpy(frame.data,txMsg->Data,CAN_MAX_DLEN);
retry:
    ret = write(socket, &frame, sizeof(struct can_frame));
    if (ret < 0)
    {
        // dy_syslog(LOG_DEBUG, "ret = %d, retry_cnt = %d, errno = 0x%x", ret, retry_cnt, errno);
        if (errno == ENOBUFS)
        {
            retry_cnt++;
            if (retry_cnt < 100)
            {
                usleep(200);
                goto retry;
            }
        }
    }
    // dy_syslog(LOG_DEBUG, "ret = %d, retry_cnt = %d", ret, retry_cnt);
    
    return ret;
}

int can_rcv(unsigned int canObjId, CanMsg *rxMsg)
{
    int socket = g_baseInfo[canObjId].socket;
    struct can_frame frame;

    CHECK_CAN_OBJ_ID(canObjId)
    if (sizeof(struct can_frame) != read(socket, &frame, sizeof(struct can_frame)))
    {
        //dy_syslog(LOG_ERR, "can read failed");
        return -1;
    }
    // dy_syslog(LOG_DEBUG, "recv canId = 0x%x", frame.can_id);

    if (frame.can_id & CAN_ERR_FLAG) {
        // 错误帧，直接返回 -1
        dy_syslog(LOG_ERR, "recv err canId = 0x%x", frame.can_id);
        return -1;
    }

    rxMsg->CanId = frame.can_id;
    if(frame.can_id & CAN_EFF_FLAG)
    {
        rxMsg->CanId &= CAN_EFF_MASK;
        rxMsg->IDE = CAN_ID_EXT;
    }
    else
    {
        rxMsg->CanId &= CAN_SFF_MASK;
    }
    rxMsg->DLC = frame.can_dlc;
    memcpy(rxMsg->Data,frame.data,frame.can_dlc);
    return 0;
}

/*
setnonblocking - 设置句柄为非阻塞方式
*/
int setnonblocking(unsigned int canObjId)
{
    int fd = g_baseInfo[canObjId].socket;

    CHECK_CAN_OBJ_ID(canObjId)
    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK) == -1)
    {
        return -1;
    }
    return 0;
}


static int can_open(char *dev)
{
    int socked = -1;
    struct sockaddr_can can_addr;
    struct ifreq ifr;

    if ((socked = socket(PF_CAN, SOCK_RAW, CAN_RAW)) < 0)
    {
        dy_syslog(LOG_ERR, "open %s socket error.", dev);
        return -1;
    }

    memset(&ifr, 0, sizeof(ifr));
    strcpy(ifr.ifr_name, dev);
    if (0 != ioctl(socked, SIOCGIFINDEX, &ifr))
    {
        dy_syslog(LOG_ERR, "%s:ioctl SIOCGIFINDEX failed.", dev);
        close(socked);
        return -1;
    }

    //setnonblocking(socked);
    memset(&can_addr, 0, sizeof(can_addr));
    can_addr.can_family  = AF_CAN;
    can_addr.can_ifindex = ifr.ifr_ifindex;

    if (bind(socked, (struct sockaddr *)&can_addr, sizeof(can_addr)) < 0)
    {
        dy_syslog(LOG_ERR, "bind can socket failed.");
        close(socked);
        return -1;
    }
    dy_syslog(LOG_DEBUG, "can open success, socked = %d.", socked);
    
    return socked;
}

static void can_close(unsigned int canObjId)
{
    int socket = g_baseInfo[canObjId].socket;

    dy_syslog(LOG_DEBUG, "can exit");
    close(socket);
    g_baseInfo[canObjId].socket = -1;
}

int can_DeInit(unsigned int canObjId)
{
    CHECK_CAN_OBJ_ID(canObjId)
    can_close(canObjId);

    return 0;
}

int can_init(unsigned int canObjId)
{  
    CHECK_CAN_OBJ_ID(canObjId)

    if(g_baseInfo[canObjId].socket > 0)
    {
        can_DeInit(canObjId);
    }
    g_baseInfo[canObjId].socket = can_open(g_baseInfo[canObjId].canDev);

    if(g_baseInfo[canObjId].socket > 0){return 0;}
    else{return -1;}
}

/* 重启can */
int can_reopen(unsigned int canObjId)
{
    char cmd[128];
    char *dev = g_baseInfo[canObjId].canDev;

    memset(cmd, 0, sizeof(cmd));
    sprintf(cmd, "ifconfig %s down; ifconfig %s up;", dev, dev);
    return system(cmd);
}

