#include "mySystem.h"
#include "myComm.h"
#include "myLog.h"
#include "myIcp67.h"

#include <vector>

typedef struct 
{
    uint16_t rptr;
    uint16_t wptr;
    uint16_t size;
    uint16_t cnt;
    uint8_t buf[4096];
}stru_rx;

LOCAL stru_rx g_rx = {
    .rptr = 0,
    .wptr = 0,
    .size = 4096,
    .cnt = 0,
};

uint8_t g_tx[4096];
uint16_t g_tx_len = 0;

LOCAL stru_tcp_para g_tcp_para[ENUM_TCP_MAX] = 
{
    [ENUM_TCP_SERVER_0] = {
    #ifdef RK356x
        // .local_ip = "192.168.80.100",
        .local_ip = "192.168.2.54",
    #else
        .local_ip = "192.168.80.253",
    #endif
        .local_port = 2404,
    },
    [ENUM_TCP_CLIENT_0] = {
        .remote_ip = "192.168.80.102",
        .remote_port = 2404,
    },
    [ENUM_TCP_SERVER_1] = {
    #ifdef RK356x
        .local_ip = "198.121.0.100",
    #else
        .local_ip = "198.120.10.2",
    #endif
        .local_port = 2404,
    }
};

LOCAL stru_uart_para g_uart_para[ENUM_UART_MAX] = 
{
    [ENUM_UART_0] = {
    #ifdef RK356x
        .device = "/dev/ttyS3",
    #else
        .device = "/dev/ttyUSB0",
    #endif
        .baudrate = 115200,
        .data_bits = 8,
        .stop_bits = 1,
        .parity = 'N',
    }
};

LOCAL struct 
{
    CommType type;
    CommDebugShow debug_show;
    void *p_para;
    int id;
    int socket_fd;
    pthread_t thread_id;
    void* (*start_cb)(void *arg);
}g_channel_para[ENUM_COMM_MAX] = 
{
    [ENUM_COMM_TCP_S_0] = {
        .type = CommType::tcp_server,
        .debug_show = CommDebugShow::off,   
        .p_para = &g_tcp_para[ENUM_TCP_SERVER_0],
        .id = -1,
        .socket_fd = -1,
        // .thread_id = -1,
        // .start_cb = comm_channel_tcp_server_0_start,
    },
    [ENUM_COMM_TCP_C_0] = {
        .type = CommType::tcp_client,
        .debug_show = CommDebugShow::off,
        .p_para = &g_tcp_para[ENUM_TCP_CLIENT_0],
        .id = -1,    
        .socket_fd = -1,
        // .thread_id = -1,
        // .start_cb = comm_channel_tcp_client_0_start,
    },
    [ENUM_COMM_UART_0] = {
        .type = CommType::uart,
        .debug_show = CommDebugShow::off,
        .p_para = &g_uart_para[ENUM_UART_0],
        .id = -1,    
        .socket_fd = -1,
    },
    [ENUM_COMM_TCP_S_1] = {
        .type = CommType::tcp_server,
        .debug_show = CommDebugShow::off,
        .p_para = &g_tcp_para[ENUM_TCP_SERVER_1],
        .id = -1,    
        .socket_fd = -1,
    }
};

uint32_t com_channel_interface_get(uint32_t comm_id)
{
    if(comm_id >= ENUM_COMM_MAX)
    {
        return 0;
    }

    uint32_t itfs = ((g_channel_para[comm_id].id & 0xFFFF) << 16) | (g_channel_para[comm_id].socket_fd & 0xFFFF);

    // LOG_I("com_channel_interface_get comm_id %d, itfs %d, id %d, socket_fd %d", comm_id, itfs, g_channel_para[comm_id].id, g_channel_para[comm_id].socket_fd);

    return itfs;
}

LOCAL uint8_t crc_check_sum(uint8_t *p_data, uint16_t len)
{
    uint8_t crc = 0;
    uint16_t i;
    for(i = 0; i < len; i++)
    {
        crc += p_data[i];
    }
    return crc;
}

int icp66_search_frame(uint8_t *p_rx, uint16_t rx_len, uint16_t *p_pos, uint16_t *p_len)
{
    *p_pos = 0;
    *p_len = 0;

    for(uint16_t i = 0; i < rx_len; i++)
    {
        stru_head *p_head = (stru_head *)&p_rx[i];

        if(i + 2 + sizeof(stru_head) > g_rx.cnt)
        {
            break;
        }

        if(0x66 != p_head->head1 || 0x66 != p_head->head2)
        {
            continue;
        }

        if(p_head->len + 6 > g_rx.cnt - i)
        {
            break;
        }

        if(p_rx[i + p_head->len + 4] != crc_check_sum(&p_rx[i + 4], p_head->len))
        {
            continue;
        }

        if(p_rx[i + p_head->len + 5] != 0x16)
        {
            continue;
        }

        *p_pos = i;
        *p_len = p_head->len + 6;
        return 0;
    }

    return -1;
}

void icp66_to_icp67(uint8_t *p_data)
{
    stru_head *p_head = (stru_head *)p_data;

    p_head->head1 = 0x67; 
    p_head->head2 = 0x67; 
    p_head->dst = 1;      
    p_head->dev_addr = 1; 

    p_data[4 + p_head->len] = crc_check_sum(&p_data[4], p_head->len);
}

LOCAL void icp67_show_frame(const uint8_t *p_data, uint16_t len)
{
    if(NULL == p_data || 0 == len)
    {
        return;
    }

    stru_head *p_head = (stru_head *)p_data;

    const char *str[5] = {"维护软件", "主板", "采样板", "LCD", "XTU"};

    printf("%s -> %s, len = %d, dev_addr %d\n", str[p_head->src-1], str[p_head->dst-1], len, p_head->dev_addr);
    for(uint16_t i = 0; i < len; i++)
    {
        printf("%02X ", p_data[i]);
    }
    printf("\n");
}

LOCAL void put_rx_data(stru_rx *p_slef_rx, const uint8_t *p_rx, uint16_t len)
{
    if(NULL == p_slef_rx || NULL == p_rx || 0 == len)
    {
        LOG_E("put_rx_data p_slef_rx NULL or p_rx NULL or len 0");
        return;
    }

    for(uint16_t i = 0; i < len; i++)
    {
        p_slef_rx->buf[p_slef_rx->wptr] = p_rx[i];
        p_slef_rx->wptr = (p_slef_rx->wptr + 1) % p_slef_rx->size;
        p_slef_rx->cnt++;

        if(p_slef_rx->cnt > p_slef_rx->size)
        {
            p_slef_rx->cnt--;
            p_slef_rx->rptr = (p_slef_rx->rptr + 1) % p_slef_rx->size;

            LOG_E("put_rx_data cnt %d, rptr %d, wptr %d", p_slef_rx->cnt, p_slef_rx->rptr, p_slef_rx->wptr);
        }
    }
}

LOCAL void get_rx_data(stru_rx *p_slef_rx, uint8_t *p_data, uint16_t *p_data_len)
{
    if(NULL == p_slef_rx || NULL == p_data || NULL == p_data_len)
    {
        LOG_E("get_rx_data p_slef_rx NULL or p_data NULL or p_data_len NULL");
        return;
    }

    uint16_t pos = p_slef_rx->rptr;

    for(uint16_t i = 0; i < p_slef_rx->cnt; i++)
    {
        p_data[i] = p_slef_rx->buf[pos];
        pos = (pos + 1) % p_slef_rx->size;
    }
    
    (*p_data_len) = p_slef_rx->cnt;
}

LOCAL void icp66_search_first_head(uint8_t *p_data, uint16_t len, uint16_t *p_pos)
{
    *p_pos = 0;

    for(uint16_t i = 0; i < len; i++)
    {
        if(p_data[i] == 0x66)
        {
            (*p_pos) = i;
            return;
        }
    }

    (*p_pos) = len;   
}

void icp66_frame_decode(uint32_t itfs, const uint8_t *p_rx, uint16_t rx_len)
{
    put_rx_data(&g_rx, p_rx, rx_len);

    uint16_t pos = 0;
    uint16_t valid_len = 0;
    int ret = -1;
    uint8_t temp_buf[2048] = {0};
    do
    {
        pos = 0;
        valid_len = 0;

        get_rx_data(&g_rx, g_tx, &g_tx_len);
        ret = icp66_search_frame(g_tx, g_tx_len, &pos, &valid_len);

        if(ret == 0)
        {
            g_rx.rptr += (pos + valid_len);
            g_rx.cnt -= (pos + valid_len);
            g_rx.rptr %= g_rx.size;

            // icp67_show_frame(g_tx + pos, valid_len);
            memcpy(temp_buf, &g_tx[pos], valid_len);
            icp66_to_icp67(temp_buf);

            com_recv_data(itfs, temp_buf, valid_len);
        }
    }while(ret == 0);

    get_rx_data(&g_rx, g_tx, &g_tx_len);
    icp66_search_first_head(g_tx, g_tx_len, &pos);
    g_rx.rptr += pos;
    g_rx.cnt -= pos;
    g_rx.rptr %= g_rx.size;
}



LOCAL void com_channel_recv_cb(int id, int socket_fd, const uint8_t *p_rx, uint16_t rx_len)
{

    int send_id = -1;
    int send_socket_fd = -1;

    if(id == g_channel_para[ENUM_COMM_TCP_S_0].id)
    {
        send_id = g_channel_para[ENUM_COMM_TCP_C_0].id;
        send_socket_fd = g_channel_para[ENUM_COMM_TCP_C_0].socket_fd;

        comm_send(send_id, send_socket_fd, p_rx, rx_len);
    }
    else if(id == g_channel_para[ENUM_COMM_UART_0].id)
    {
        // uint32_t itfs = com_channel_interface_get(ENUM_COMM_TCP_C_0);

        uint32_t itfs = ((id << 16) | (socket_fd));
        icp66_frame_decode(itfs, p_rx, rx_len);
    }
    else
    {
        uint32_t itfs = ((id << 16) | (socket_fd));
        com_recv_data(itfs, p_rx, rx_len);
    }
}

LOCAL void com_channel_state_cb(int id, int socket_fd, CommState state)
{
    LOG_I("comm_channel_state_cb id %d, socket_fd %d, state %d", id, socket_fd, state);

    for(int i = 0; i < ENUM_COMM_MAX; i++)
    {
        if(g_channel_para[i].id == id)
        {
            if(state == CommState::connected)
            {
                g_channel_para[i].socket_fd = socket_fd;
            }
            else if(state == CommState::disconnected)
            {
                g_channel_para[i].socket_fd = -1;
            }
            break;
        }
    }

    if(id == g_channel_para[ENUM_COMM_TCP_C_0].id)
    {
        uint32_t itfs = com_channel_interface_get(ENUM_COMM_TCP_C_0);
        self_ptl_set_interface(itfs);
    }
}

LOCAL int com_channel_create()
{
    for(int i = 0; i < ENUM_COMM_MAX; i++)
    {
        if((g_channel_para[i].id = comm_create(g_channel_para[i].type, g_channel_para[i].debug_show, g_channel_para[i].p_para)) < 0)
        {
            LOG_E("comm_create failed, channel id %d", i);
            return -1;
        }

        if(0 != comm_recv_register(g_channel_para[i].id, com_channel_recv_cb))
        {
            LOG_E("comm_recv_register failed, channel id %d", i);
            return -1;
        }

        if(0 != comm_state_register(g_channel_para[i].id, com_channel_state_cb))
        {
            LOG_E("comm_state_register failed, channel id %d", i);
            return -1;
        }
    }

    return 0;
}

LOCAL void *com_start(void *arg)
{
    if(NULL == arg)
    {
        LOG_E("arg is NULL");
        return NULL;
    }

    int id = *(int *)arg;

    if(0 != comm_connect(id))
    {
        LOG_E("comm_connect failed, channel id %d", id);
        return NULL;
    }

    return NULL;
}

LOCAL void com_channel_start()
{
    for(int i = 0; i < ENUM_COMM_MAX; i++)
    {
        if(g_channel_para[i].id >= 0)
        {
            if(0 != pthread_create(&g_channel_para[i].thread_id, NULL, /*g_channel_para[i].start_cb*/com_start, &g_channel_para[i].id))
            {
                LOG_E("pthread_create failed, channel id %d", i);
                return;
            }
        }
    }
}


int app_com_channel_init1(void *arg)
{
    if(0 != com_channel_create())
    {
        LOG_E("comm_channel_create failed");
        return -1;
    }
    com_channel_start();

    return 0;
}

int app_com_channel_init2(void *arg)
{

    return 0;
}

void *app_com_channel(void *arg)
{
    if(NULL == arg)
    {
        LOG_E("app_com_scan arg null");
        return NULL;
    }

    stru_app *p_app = (stru_app *)arg;

    uint32_t event;



    while(1)
    {
        task_event_recv(p_app->p_event, 
            EV_TIMER1 | EV_TIMER2 | EV_TIMER3, 
            TASK_EVENT_FLAG_OR | TASK_EVENT_FLAG_CLEAR, 
            TASK_EVENT_WAIT_FOREVER, 
            &event);

        if(event & EV_TIMER1)
        {
            ;
        }

        if(event & EV_TIMER2)
        {
            ;
        }

        if(event & EV_TIMER3)
        {
            p_app->run_cnt++;
        }
    }


    return NULL;
}

