#include "resend_protl.h"

void resend_init(resend_device_t *resend_device, int (*send)(void* data, uint16_t len, int timeout), int (*recv)(void* data, uint16_t len, int timeout), int (*get_length )(void), void (*resend_user_parse)(void *resend_device))
{
    resend_device->send = send;
    resend_device->recv = recv;
    resend_device->get_length = get_length;
    resend_device->resend_user_parse = resend_user_parse;

    resend_device->status.c_state = RESEND_IDLE;

    resend_device->tx_frame.header_0 = RESEND_HEADER_0;
    resend_device->tx_frame.header_1 = RESEND_HEADER_1;
    resend_device->tx_frame.seq = 0;
    resend_device->tx_frame.cmd = 0;
    resend_device->tx_frame.retry_cnt = 0;
    resend_device->tx_frame.len = 0;
    resend_device->tx_frame.crc = 0;
}
void resend_set(resend_device_t *resend_device)
{
    resend_device->tx_frame.seq = resend_device->rx_frame.seq;
}
int resend_encode(resend_device_t *resend_device, void *out, uint8_t cmd, void* data, uint16_t len)
{
    if(!out ) return -1;
    uint8_t* c  = (uint8_t*)out;
    // header
    c[0] = resend_device->tx_frame.header_0;
    c[1] = resend_device->tx_frame.header_1;
    // seq
    if(cmd != RESEND_CMD_DATA && cmd != RESEND_CMD_ACK && resend_device->tx_frame.retry_cnt == 0)
        resend_device->tx_frame.seq++;

    c[2] = resend_device->tx_frame.seq;
    // cmd
    c[3] = resend_device->tx_frame.cmd = cmd;
    // retry_cnt
    c[4] = resend_device->tx_frame.retry_cnt;
    // len big endian
    c[5] = (uint8_t)((len) & 0xFF);
    c[6] = (uint8_t)((len >> 8) & 0xFF);
    // payload
    if (len > 0 && data) memcpy(&c[7], data, len);
    // compute crc over seq..payload (i.e. bytes p+1 .. p+5+len)
    uint16_t crc = fmav_crc_calculate(&c[2], 5 + len);
    c[7 + len] = (uint8_t)(crc & 0xFF);
    c[7 + len + 1] = (uint8_t)((crc >> 8) & 0xFF);

    return (int)(len + RESEND_MAX_PAYLOAD);
}

int resend_decode(resend_device_t *resend_device, uint8_t c)
{
    static uint16_t frame_crc = 0;
    switch(resend_device->status.c_state)
    {
        case RESEND_IDLE:
            if (c == RESEND_HEADER_0) {
                resend_device->status.c_state = RESEND_HDR;
            } 
            else{
                resend_device->status.c_state = RESEND_IDLE;
            }
            break;
        case RESEND_HDR:
            if (c == RESEND_HEADER_1) {
                resend_device->status.c_state = RESEND_SEQ;
                fmav_crc_init(&frame_crc);
            }else{
                resend_device->status.c_state = RESEND_IDLE;
            }
            break;
        case RESEND_SEQ:
            resend_device->rx_frame.seq = c;
            fmav_crc_accumulate(&frame_crc, c);
            resend_device->status.c_state = RESEND_CMD;
            break;
        case RESEND_CMD:
            resend_device->rx_frame.cmd = c;
            fmav_crc_accumulate(&frame_crc, c);
            resend_device->status.c_state = RESEND_RETRY_CNT;
            break;
        case RESEND_RETRY_CNT:
            resend_device->rx_frame.retry_cnt = c;
            //超过最大允许重传次数
            if(resend_device->rx_frame.retry_cnt > RESEND_MAX_RETRY_CNT)
                resend_device->status.c_state = RESEND_IDLE;
            fmav_crc_accumulate(&frame_crc, c);
            resend_device->status.c_state = RESEND_LEN_1;
            break;
        case RESEND_LEN_1:
            resend_device->rx_frame.len = 0;
            resend_device->rx_frame.len |= c; 
            fmav_crc_accumulate(&frame_crc, c);
            resend_device->status.c_state = RESEND_LEN_2;
            break;
        case RESEND_LEN_2:
            resend_device->rx_frame.len |= (uint16_t)(c << 8);;
            fmav_crc_accumulate(&frame_crc, c);
            if(resend_device->rx_frame.len > 0)
            {
                resend_device->status.data_offset = 0;
                resend_device->status.c_state = RESEND_DATA;
                //超过允许的字节数
                if(resend_device->rx_frame.len > RESEND_DATA_SIZE)
                    resend_device->status.c_state = RESEND_IDLE;
            }
            else
                resend_device->status.c_state = RESEND_CRC_1;
            break;
        case RESEND_DATA:
            resend_device->rx_frame.payload[resend_device->status.data_offset ++] = c;
            fmav_crc_accumulate(&frame_crc, c);
            if(resend_device->rx_frame.len == resend_device->status.data_offset){
                resend_device->status.data_offset = 0;
                resend_device->status.c_state = RESEND_CRC_1;
            }
            break;
        case RESEND_CRC_1:
            if((uint8_t)(frame_crc) == c){
                resend_device->status.c_state = RESEND_CRC_2;
            }else{
                resend_device->status.c_state = RESEND_IDLE;
            }
            break;
        case RESEND_CRC_2:
            if((uint8_t)(frame_crc >> 8 & 0xFF) == c){
                resend_device->status.c_state = RESEND_IDLE;
                resend_device->rx_frame.crc = frame_crc;
                if(resend_device->rx_frame.cmd != RESEND_CMD_DATA && resend_device->rx_frame.cmd != RESEND_CMD_ACK && resend_device->rx_frame.retry_cnt == 0)
                    resend_device->tx_frame.seq++;
                return 0;
            }else{
                resend_device->status.c_state = RESEND_IDLE;
            }
            break;
        default:    
            break;
    }
    return 1;
}

int resend_recv_data(resend_device_t *resend_device, int timeout)
{
    uint8_t data[RESEND_FRAME_SIZE];
    int ret = resend_device->recv(data, RESEND_FRAME_SIZE, timeout);
    if(ret <= 0)
        return -1;
    int ret_later = ret;
    static uint8_t seq_lat = 0;
    while(ret_later --)
    {
        if(!resend_decode(resend_device, data[ret - ret_later - 1]))
        {
            // printf("tx_req %d rx_req %d retry_cnt %d cmd %d\r\n", resend_device->tx_frame.seq, resend_device->rx_frame.seq, resend_device->rx_frame.retry_cnt, resend_device->rx_frame.cmd);

            if(seq_lat == resend_device->rx_frame.seq){
                resend_device->status.resend_flag = 1;
            } else {
                resend_device->status.resend_flag = 0;
            }
            
            resend_device->resend_user_parse(resend_device);
            resend_parse_data(resend_device);

            seq_lat = resend_device->rx_frame.seq;
        }
    }
    return ret;
}

int resend_send_cmd(resend_device_t *resend_device, uint8_t cmd, int timeout)
{
    uint8_t out[RESEND_MAX_PAYLOAD];
    int ret = resend_encode(resend_device, out, cmd, NULL, 0);
    if (ret <= 0) 
        return -1;
    return resend_device->send(out, ret, timeout);
}
int resend_send_data(resend_device_t *resend_device, uint8_t cmd, void* data, uint16_t len, int timeout)
{   
    uint8_t out[RESEND_MAX_PAYLOAD + len];
    memcpy(resend_device->tx_frame.payload, data, len);
    
    resend_device->tx_frame.retry_cnt = 0;

    int ret = resend_encode(resend_device, out, cmd, data, len);
    if (ret <= 0) 
        return -1;
    // 初始化ack应答标志位，需等待应答
    resend_device->status.ack_flag = 1;

    while(resend_device->status.ack_flag)
    {
        if(resend_device->tx_frame.retry_cnt)
        {
            //进行重发 ， 重新编码
            ret = resend_encode(resend_device, out, cmd, data, len);
            if (ret <= 0) 
                return -1;
            printf("data retry cnt %d\n", resend_device->tx_frame.retry_cnt);
        }
            
        ret = resend_device->send(out, ret, 0);
        if(ret < RESEND_MAX_PAYLOAD)
            return ret;
        
        // 特殊命令以及应答信号不需要等待
        if(cmd == RESEND_CMD_DATA || cmd == RESEND_CMD_ACK)
            resend_device->status.ack_flag = 0;

        resend_device->tx_frame.retry_cnt ++;

        // 接收数据
        uint16_t timeout_lat = timeout + 1;
        while(timeout_lat --)
        {
            if(resend_recv_data(resend_device, 0) > 0)
                break;
            os_thread_sleep(1);
        }
        // 超过允许的等待次数
        if(resend_device->tx_frame.retry_cnt > RESEND_MAX_RETRY_CNT && resend_device->status.ack_flag)
        {
            printf("timeout cnt max\n");
            resend_device->tx_frame.retry_cnt = 0;
            resend_device->status.ack_flag = 0;
            return -1;
        } else if(!resend_device->status.ack_flag){
            return ret;
        }
    }
    
    return ret;
    // return resend_device->send(out, resend_frame_encode(resend_device, out, data, len), timeout);
} 

int resend_parse_data(resend_device_t *resend_device)
{
    switch(resend_device->rx_frame.cmd){
        case RESEND_CMD_DATA:
            break;
        case RESEND_CMD_ACK:
            printf("ACK\n");
            resend_device->status.ack_flag = 0;
            break;
        case RESEND_CMD_PARAM:
            resend_set(resend_device);
            resend_send_cmd(resend_device, RESEND_CMD_ACK, 0);
            break;
        case RESEND_CMD_GET_PARAM:
            resend_send_cmd(resend_device, RESEND_CMD_ACK, 0);
            resend_device->handle_flag = RESEND_CMD_GET_PARAM;
            break;
        case RESEND_CMD_SET_PARAM:
            break;
        case RESEND_CMD_DATA_ACK:
            resend_send_cmd(resend_device, RESEND_CMD_ACK, 0);
            break;
        case RESEND_CMD_OTA_GET_PARAM:
            //针对OTA的应答
            resend_device->status.ack_flag = 0;
            break;
        case RESEND_CMD_OTA_START:
            resend_device->handle_flag = RESEND_CMD_OTA_GET_PARAM;
            break;
        case RESEND_CMD_OTA_DATA:
            break;
        case RESEND_CMD_OTA_END:
            break;
        case RESEND_CMD_FC_ISP_GET_PARAM:
            //针对ISP的应答
            resend_device->status.ack_flag = 0;
            break;
        case RESEND_CMD_FC_ISP_START:
            resend_device->handle_flag = RESEND_CMD_FC_ISP_GET_PARAM;
            break;
        case RESEND_CMD_FC_ISP_DATA:
            break;
        case RESEND_CMD_FC_ISP_END:
            break;
        default:
            break;
    }
    return 0;
}