#include "dlt698.h"
#include "dlt698_asn1/generated/dlt698_asn_types.h"

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include <termios.h>
#include <unistd.h>

#include "common.h"
#include "business_log/business_log.h"

static const uint16_t dlt698_fcstab[256] = {
    0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
    0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
    0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
    0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
    0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
    0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
    0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
    0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
    0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
    0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
    0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
    0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
    0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
    0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
    0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
    0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
    0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
    0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
    0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
    0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
    0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
    0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
    0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
    0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
    0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
    0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
    0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
    0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
    0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
    0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
    0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
    0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
};

static char *type_string[] = {
    "unsigned",
    "signed"
};

static void dlt698_sleep_response_timeout(dlt698_hd_t *hd)
{
    struct timespec request;
    struct timespec remaining;

    request.tv_sec = hd->read_time_out / 1000;
    request.tv_nsec = (hd->read_time_out % 1000) * 1000000;
    while (nanosleep(&request, &remaining) == -1 && errno == EINTR) {
        request = remaining;
    }
}

uint16_t dlt698_crc16(const uint8_t *data, uint16_t len)
{
    uint16_t crc = 0xFFFF;
    uint16_t i;

    for (i = 0; i < len; ++i) {
        crc = (uint16_t)((crc >> 8) ^ dlt698_fcstab[(crc ^ data[i]) & 0xFF]);
    }
    return (uint16_t)~crc;
}

dlt698_ext_t dlt698_get_ext_type_by_string(char *typeString)
{
    dlt698_ext_t i;

    for (i = DLT698_EXT_UNSIGNED; i < DLT698_EXT_MAX; i++) {
        if (strcmp(typeString, type_string[i]) == 0) {
            return i;
        }
    }
    BUSINESS_LOG(BLOG_ERR, DAS_ID_DLT698_1, NULL, "dlt698 get extract type error, %s!!!", typeString);
    return DLT698_EXT_MAX;
}

int dlt698_parse_oad_string(const char *oad_str, dlt698_oad_t *oad)
{
    uint32_t packed;

    if (oad_str == NULL || oad == NULL) {
        return -1;
    }

    packed = analysis_addr((char *)oad_str);
    oad->oi = (uint16_t)((packed >> 16) & 0xFFFF);
    oad->attr = (uint8_t)((packed >> 8) & 0xFF);
    oad->index = (uint8_t)(packed & 0xFF);
    return 0;
}

int dlt698_oad_equal(const dlt698_oad_t *a, const dlt698_oad_t *b)
{
    if (a == NULL || b == NULL) {
        return 0;
    }
    return (a->oi == b->oi) && (a->attr == b->attr) && (a->index == b->index);
}

static int dlt698_char_to_nibble(char c, uint8_t *nibble)
{
    if (c >= '0' && c <= '9') {
        *nibble = (uint8_t)(c - '0');
        return 0;
    }
    return -1;
}

static int dlt698_parse_addr_bcd(const char *addr_str, size_t len, dlt698_addr_t *addr)
{
    size_t i;

    addr->len = (uint8_t)(len / 2);
    for (i = 0; i < addr->len; ++i) {
        uint8_t hi;
        uint8_t lo;

        if (dlt698_char_to_nibble(addr_str[i * 2], &hi) != 0
            || dlt698_char_to_nibble(addr_str[i * 2 + 1], &lo) != 0) {
            return -1;
        }
        addr->bytes[i] = (uint8_t)((hi << 4) | lo);
    }
    return 0;
}

static int dlt698_parse_addr_hex(const char *addr_str, size_t len, dlt698_addr_t *addr)
{
    size_t i;

    addr->len = (uint8_t)(len / 2);
    for (i = 0; i < addr->len; ++i) {
        unsigned int byte = 0;
        if (sscanf(addr_str + i * 2, "%2x", &byte) != 1) {
            return -1;
        }
        addr->bytes[i] = (uint8_t)byte;
    }
    return 0;
}

int dlt698_parse_addr_string(const char *addr_str, dlt698_addr_t *addr)
{
    size_t len;
    size_t i;

    if (addr_str == NULL || addr == NULL) {
        return -1;
    }

    len = strlen(addr_str);
    if (len == 0 || (len % 2) != 0 || len > (size_t)DLT698_ADDR_LEN * 2) {
        return -1;
    }

    addr->ca = DLT698_CA_IGNORE;
    for (i = 0; i < len; ++i) {
        if (addr_str[i] < '0' || addr_str[i] > '9') {
            return dlt698_parse_addr_hex(addr_str, len, addr);
        }
    }
    return dlt698_parse_addr_bcd(addr_str, len, addr);
}

static void dlt698_write_u16_le(uint8_t *p, uint16_t v)
{
    p[0] = (uint8_t)(v & 0xFF);
    p[1] = (uint8_t)((v >> 8) & 0xFF);
}

static uint8_t dlt698_sa_len_from_feature(uint8_t addr_feature)
{
    return (uint8_t)((addr_feature & 0x0F) + 1);
}

static uint16_t dlt698_read_u16_le(const uint8_t *p)
{
    return (uint16_t)(p[0] | (p[1] << 8));
}

static int dlt698_addr_match(const uint8_t *frame_sa, uint8_t sa_len, const dlt698_addr_t *addr)
{
    uint8_t i;

    if (sa_len != addr->len) {
        return -1;
    }

    for (i = 0; i < addr->len; ++i) {
        if (frame_sa[i] != addr->bytes[addr->len - 1 - i]) {
            return -1;
        }
    }
    return 0;
}

int dlt698_frame_check(const uint8_t *frame, int len, const dlt698_addr_t *addr)
{
    uint16_t l_field;
    uint16_t hcs;
    uint16_t fcs;
    uint8_t sa_len;
    uint8_t addr_feature;
    int frame_len;
    int hcs_off;

    if (frame == NULL || len < 12 || frame[0] != DLT698_START_CODE) {
        return -1;
    }

    l_field = dlt698_read_u16_le(&frame[1]);
    frame_len = 1 + (int)l_field + 1;
    if (len < frame_len || frame[frame_len - 1] != DLT698_STOP_CODE) {
        return -3;
    }

    addr_feature = frame[4];
    sa_len = dlt698_sa_len_from_feature(addr_feature);
    hcs_off = 5 + sa_len + 1;
    if (hcs_off + 2 > frame_len - 3) {
        return -4;
    }

    hcs = dlt698_read_u16_le(&frame[hcs_off]);
    if (hcs != dlt698_crc16(&frame[1], (uint16_t)(hcs_off - 1))) {
        return -5;
    }

    fcs = dlt698_read_u16_le(&frame[frame_len - 3]);
    if (fcs != dlt698_crc16(&frame[1], (uint16_t)(frame_len - 1 - 3))) {
        return -6;
    }

    if (addr != NULL && dlt698_addr_match(&frame[5], sa_len, addr) != 0) {
        return -7;
    }

    return frame_len;
}

int dlt698_pack_get_request(const dlt698_addr_t *addr, uint8_t piid, const dlt698_oad_t *oad,
                            void *buff, int max_len)
{
    uint8_t apdu[64];
    Client_APDU_t client_apdu;
    GET_Request_t get_req;
    ssize_t apdu_len;
    uint8_t *frame = (uint8_t *)buff;
    uint16_t l_field;
    uint16_t hcs;
    uint16_t fcs;
    int pos;
    uint8_t i;

    if (addr == NULL || oad == NULL || buff == NULL || addr->len == 0 || addr->len > DLT698_ADDR_LEN) {
        return -1;
    }

    memset(&client_apdu, 0, sizeof(client_apdu));
    memset(&get_req, 0, sizeof(get_req));
    client_apdu.present = Client_APDU_PR_getRequest;
    get_req.present = GET_Request_PR_getRequestNormal;
    get_req.choice.getRequestNormal.prioInvID = (uint8_t)(piid & DLT698_PIID_MAX);
    get_req.choice.getRequestNormal.objDescr.objId = oad->oi;
    get_req.choice.getRequestNormal.objDescr.attrIdChara = oad->attr;
    get_req.choice.getRequestNormal.objDescr.attrElemIdx = oad->index;
    client_apdu.choice.getRequest = get_req;
    client_apdu.has_timeTag = 0;

    apdu_len = der_encode_Client_APDU(&client_apdu, apdu, sizeof(apdu));
    if (apdu_len <= 0) {
        return -1;
    }

    /* L = L_self(2) + C(1) + A(1 + addr->len + 1) + HCS(2) + APDU + FCS(2) */
    l_field = (uint16_t)(2 + 1 + 1 + addr->len + 1 + 2 + (uint16_t)apdu_len + 2);
    if (max_len < (int)(1 + l_field + 1)) {
        return -1;
    }

    pos = 0;
    frame[pos++] = DLT698_START_CODE;
    dlt698_write_u16_le(&frame[pos], l_field);
    pos += 2;
    frame[pos++] = DLT698_CTRL_CLIENT_REQ;
    frame[pos++] = (uint8_t)((addr->len - 1) & 0x0F);
    for (i = 0; i < addr->len; ++i) {
        frame[pos++] = addr->bytes[addr->len - 1 - i];
    }
    frame[pos++] = addr->ca;

    hcs = dlt698_crc16(&frame[1], (uint16_t)(pos - 1));
    dlt698_write_u16_le(&frame[pos], hcs);
    pos += 2;

    memcpy(&frame[pos], apdu, (size_t)apdu_len);
    pos += (int)apdu_len;

    fcs = dlt698_crc16(&frame[1], (uint16_t)(pos - 1));
    dlt698_write_u16_le(&frame[pos], fcs);
    pos += 2;
    frame[pos++] = DLT698_STOP_CODE;

    return pos;
}

typedef struct {
    int32_t  sval;
    uint32_t uval;
    uint8_t  is_signed;
    uint8_t  valid;
} dlt698_scalar_t;

static int dlt698_read_u32_be(const uint8_t *p, uint32_t *out)
{
    *out = ((uint32_t)p[0] << 24)
         | ((uint32_t)p[1] << 16)
         | ((uint32_t)p[2] << 8)
         | (uint32_t)p[3];
    return 0;
}

static int dlt698_read_s32_be(const uint8_t *p, int32_t *out)
{
    uint32_t raw;

    if (dlt698_read_u32_be(p, &raw) != 0) {
        return -1;
    }
    *out = (int32_t)raw;
    return 0;
}

static int dlt698_store_scalar(const dlt698_scalar_t *scalar, dlt698_item *ask_it,
                               void *dest_buff, uint32_t idx, uint32_t max_len)
{
    static const double div[6] = {1, 10, 100, 1000, 10000, 100000};

    if (ask_it->pos > 0) {
        double *p_float_dest = (double *)dest_buff;

        if ((idx + 1U) * (uint32_t)sizeof(double) > max_len) {
            return -1;
        }
        if (scalar->is_signed) {
            p_float_dest[idx] = (double)scalar->sval / div[ask_it->pos];
        } else {
            p_float_dest[idx] = (double)scalar->uval / div[ask_it->pos];
        }
    } else {
        int32_t *p_int_dest = (int32_t *)dest_buff;

        if ((idx + 1U) * (uint32_t)sizeof(int32_t) > max_len) {
            return -1;
        }
        p_int_dest[idx] = scalar->is_signed ? scalar->sval : (int32_t)scalar->uval;
    }
    return 0;
}

static int dlt698_parse_primitive(const uint8_t *data, size_t data_len, dlt698_scalar_t *scalar)
{
    uint8_t tag;

    if (data == NULL || data_len < 1 || scalar == NULL) {
        return -1;
    }

    memset(scalar, 0, sizeof(*scalar));
    scalar->valid = 1;
    tag = data[0];

    switch (tag) {
    case 0x06: /* double-long-unsigned */
        if (data_len < 5) {
            return -1;
        }
        if (dlt698_read_u32_be(&data[1], &scalar->uval) != 0) {
            return -1;
        }
        return 5;

    case 0x05: /* double-long */
        if (data_len < 5) {
            return -1;
        }
        scalar->is_signed = 1;
        if (dlt698_read_s32_be(&data[1], &scalar->sval) != 0) {

            return -1;
        }
        return 5;

    case 0x12: /* long-unsigned */
        if (data_len < 3) {
            return -1;
        }
        scalar->uval = ((uint32_t)data[1] << 8) | (uint32_t)data[2];
        return 3;

    case 0x10: /* long */
        if (data_len < 3) {
            return -1;
        }
        scalar->is_signed = 1;
        scalar->sval = (int16_t)(((uint16_t)data[1] << 8) | (uint16_t)data[2]);
        return 3;

    case 0x11: /* unsigned */
        if (data_len < 2) {
            return -1;
        }
        scalar->uval = data[1];
        return 2;

    case 0x0F: /* integer */
        if (data_len < 2) {
            return -1;
        }
        scalar->is_signed = 1;
        scalar->sval = (int8_t)data[1];
        return 2;

    default:
        return -1;
    }
}

static int dlt698_collect_scalars(const uint8_t *data, size_t data_len, dlt698_item *ask_it,
                                  void *dest_buff, uint32_t max_len)
{
    dlt698_scalar_t scalar;
    uint8_t tag;
    uint8_t count;
    size_t off = 0;
    uint32_t idx = 0;
    uint32_t max_elems;
    int elem_len;
    uint8_t i;

    if (data == NULL || data_len < 1 || ask_it == NULL || dest_buff == NULL) {
        return -1;
    }
    if (ask_it->pos < 0) {
        return -1;
    }

    max_elems = ask_it->data_len > 0 ? ask_it->data_len
                                     : (uint32_t)(max_len / (ask_it->pos > 0 ? sizeof(double) : sizeof(int32_t)));
    if (max_elems == 0) {
        max_elems = 1;
    }

    tag = data[0];
    if (tag == 0x01 || tag == 0x02) {
        if (data_len < 2) {
            return -2;
        }
        count = data[1];
        off = 2;
        for (i = 0; i < count && idx < max_elems; ++i) {
            elem_len = dlt698_parse_primitive(data + off, data_len - off, &scalar);
            if (elem_len <= 0) {
                break;
            }
            if (dlt698_store_scalar(&scalar, ask_it, dest_buff, idx, max_len) != 0) {
                break;
            }
            off += (size_t)elem_len;
            idx++;
        }
        return (int)idx;
    }

    elem_len = dlt698_parse_primitive(data, data_len, &scalar);
    if (elem_len <= 0) {
        return -4;
    }
    if (dlt698_store_scalar(&scalar, ask_it, dest_buff, 0, max_len) != 0) {
        return -3;
    }
    return 1;
}

static int dlt698_extract_data_value(const uint8_t *data, size_t data_len, dlt698_item *ask_it,
                                     void *dest_buff, uint32_t max_len)
{
    if (data == NULL || data_len < 1 || ask_it == NULL || dest_buff == NULL) {
        return -1;
    }
    

    return dlt698_collect_scalars(data, data_len, ask_it, dest_buff, max_len);
}

int dlt698_analysis(dlt698_item *it, void *ack_buf, int len, const dlt698_addr_t *addr,
                    void *dist_buff, unsigned int max_len)
{
    Server_APDU_t server_apdu;
    GetResponseNormal_t *resp_normal;
    Get_Result_t *result;
    dlt698_oad_t resp_oad;
    int frame_len;
    const uint8_t *frame = (const uint8_t *)ack_buf;
    const uint8_t *apdu;
    uint8_t sa_len;
    int apdu_len;
    int hcs_off;

    frame_len = dlt698_frame_check(frame, len, addr);
    if (frame_len < 0) {
        return -1;
    }

    sa_len = dlt698_sa_len_from_feature(frame[4]);
    hcs_off = 5 + sa_len + 1;
    apdu = &frame[hcs_off + 2];
    apdu_len = frame_len - (hcs_off + 2 + 2 + 1);

    if (ber_decode_Server_APDU(apdu, (size_t)apdu_len, &server_apdu) < 0) {
        return -2;
    }

    if (server_apdu.present != Server_APDU_PR_getResponse
        || server_apdu.choice.getResponse.present != GET_Response_PR_getResponseNormal) {
        return -3;
    }

    resp_normal = &server_apdu.choice.getResponse.choice.getResponseNormal;
    resp_oad.oi = resp_normal->a_ResultNormal.objDescr.objId;
    resp_oad.attr = resp_normal->a_ResultNormal.objDescr.attrIdChara;
    resp_oad.index = resp_normal->a_ResultNormal.objDescr.attrElemIdx;
    if (!dlt698_oad_equal(&it->oad, &resp_oad)) {
        return -3;
    }

    result = &resp_normal->a_ResultNormal.getResult;
    if (result->present == Get_Result_PR_dar) {
        return -4;
    }

    if (result->present != Get_Result_PR_data) {
        return -5;
    }
    
    return dlt698_extract_data_value(result->choice.data.buf, result->choice.data.size,
                                     it, dist_buff, max_len);
}

int dlt698_connect(dlt698_hd_t *hd)
{
    uart_attr *ctx_uart = (uart_attr *)hd->data;

    hd->fd = open_uart(ctx_uart);
    if (hd->fd > -1) {
        return 0;
    }
    return -1;
}

static int dlt698_tx(dlt698_hd_t *hd, void *buff, uint32_t len)
{
    frame_wgap_delay(hd->lastread, hd->frame_gap);
    return (int)write(hd->fd, buff, len);
}

static int dlt698_rx(dlt698_hd_t *hd, void *buff, uint32_t to_read)
{
    uint8_t *dest_buff = (uint8_t *)buff;
    int s_rc;
    int rx_len = 0;
    struct timeval tv;

    tv.tv_sec = hd->read_time_out / 1000;
    tv.tv_usec = (hd->read_time_out % 1000) * 1000;
    fd_set rset;
    FD_ZERO(&rset);
    FD_SET(hd->fd, &rset);

    do {
        ssize_t rval;
        while ((s_rc = select(hd->fd + 1, &rset, NULL, NULL, &tv)) == -1) {
            if (errno == EINTR) {
                FD_ZERO(&rset);
                FD_SET(hd->fd, &rset);
            } else {
                return -1;
            }
        }

        if (s_rc == 0) {
            errno = ETIMEDOUT;
            break;
        }

        rval = read(hd->fd, dest_buff + rx_len, to_read - (uint32_t)rx_len);
        if (rval > 0) {
            hd->lastread = sysuptime();
            rx_len += (int)rval;
        } else {
            break;
        }
        tv.tv_sec = (hd->interval > 1000) ? hd->interval / 1000 : 0;
        tv.tv_usec = (hd->interval % 1000) * 1000;
    } while ((uint32_t)rx_len < to_read);

    return rx_len;
}

int dlt698_dis_connect(dlt698_hd_t *hd)
{
    uart_attr *ctx_uart = hd->data;
    int re;

    tcsetattr(hd->fd, TCSANOW, &ctx_uart->old_tios);
    re = close(hd->fd);
    if (re == 0) {
        hd->fd = -1;
    }
    return re;
}

int dlt698_free(dlt698_hd_t *hd)
{
    if (hd == NULL) {
        return -1;
    }

    if (hd->fd > -1) {
        dlt698_dis_connect(hd);
    }

    if (hd->data != NULL) {
        free(hd->data);
    }

    free(hd);
    return 0;
}

static int dlt698_flush(dlt698_hd_t *hd)
{
    return tcflush(hd->fd, TCIOFLUSH);
}

dlt698_hd_t *new_dlt698_uart(char *dev, int baud, uint8_t data_bit, uint8_t stop_bit, char parity)
{
    dlt698_hd_t *hd = calloc(1, sizeof(dlt698_hd_t));
    uart_attr *uart_data;

    if (hd == NULL) {
        return NULL;
    }

    uart_data = calloc(1, sizeof(uart_attr));
    if (uart_data == NULL) {
        free(hd);
        return NULL;
    }

    uart_data->baud = baud;
    uart_data->data_bit = data_bit;
    uart_data->stop_bit = stop_bit;
    uart_data->parity = parity;
    snprintf(uart_data->dev_f, sizeof(uart_data->dev_f), "%s", dev);

    hd->data = uart_data;
    hd->piid = 1;
    snprintf(hd->name, sizeof(hd->name), "%s", dev);
    hd->frame_gap = -1;
    hd->lastread = sysuptime() - 10000;
    hd->fd = -1;
    return hd;
}

int dlt698_set_time(dlt698_hd_t *hd, uint32_t timeout_ms, uint32_t interval)
{
    hd->read_time_out = timeout_ms;
    hd->interval = interval;
    return 0;
}

int dlt698_set_name(dlt698_hd_t *hd, char *new_name)
{
    return snprintf(hd->name, sizeof(hd->name), "%s", new_name);
}

int dlt698_set_piid(dlt698_hd_t *hd, uint8_t piid)
{
    if (hd == NULL) {
        return -1;
    }
    hd->piid = (uint8_t)(piid & DLT698_PIID_MAX);
    return 0;
}

static int dlt698_get_ack(dlt698_hd_t *hd, void *buff, uint32_t max_len, int *valid_start)
{
    uint8_t *tmp_buff = (uint8_t *)buff;
    int rx_len = 0;
    int pre_sum = 0;
    uint16_t l_field;
    int need_len;
    int re;

    if (valid_start != NULL) {
        *valid_start = 0;
    }

    rx_len = dlt698_rx(hd, tmp_buff, 1);
    if (rx_len < 1) {
        return rx_len;
    }

    while (pre_sum < DLT698_PREAMBLE_CNT && tmp_buff[0] == DLT698_PREAMBLE) {
        pre_sum++;
        if (dlt698_rx(hd, tmp_buff, 1) < 1) {
            return pre_sum > 0 ? pre_sum : 0;
        }
    }

    if (tmp_buff[0] != DLT698_START_CODE) {
        return pre_sum > 0 ? pre_sum : rx_len;
    }

    rx_len = 1;
    re = dlt698_rx(hd, tmp_buff + rx_len, 2);
    if (re < 2) {
        return rx_len + (re > 0 ? re : 0);
    }
    rx_len += 2;

    l_field = dlt698_read_u16_le(&tmp_buff[1]);
    need_len = 1 +  (int)l_field + 1;
    if ((uint32_t)need_len > max_len) {
        return rx_len;
    }

    while (rx_len < need_len) {
        re = dlt698_rx(hd, tmp_buff + rx_len, (uint32_t)(need_len - rx_len));
        if (re <= 0) {
            break;
        }
        rx_len += re;
    }

    if (rx_len < need_len) {
        return rx_len;
    }

    if (valid_start != NULL) {
        *valid_start = pre_sum;
    }
    return need_len;
}

int dlt698_read_data(dlt698_hd_t *hd, const dlt698_addr_t *addr,
                     int (*get_raw_data)(char *data, int len, void *contex), void *contex,
                     dlt698_item *it, void *buff, uint32_t max_len)
{
    uint8_t tx_buff[DLT698_WR_LEN];
    uint8_t tmp_buff[DLT698_RX_BUFF_MAX];
    char tmp[64];
    int pack_len;
    int rx_len;
    int re = -99;

    if (hd == NULL || addr == NULL || it == NULL) {
        return -1;
    }
    pack_len = dlt698_pack_get_request(addr, hd->piid, &it->oad, tx_buff, sizeof(tx_buff));
    if (pack_len < 0) {
        return -1;
    }

    dlt698_flush(hd);
    {
        const uint8_t *send_ptr = tx_buff;
        int send_len = pack_len;
#if DLT698_PREAMBLE_ENABLE
        uint8_t wire_buff[DLT698_PREAMBLE_CNT + DLT698_WR_LEN];
        int i;

        send_len = pack_len + DLT698_PREAMBLE_CNT;
        if (send_len > (int)sizeof(wire_buff)) {
            return -1;
        }
        for (i = 0; i < DLT698_PREAMBLE_CNT; ++i) {
            wire_buff[i] = DLT698_PREAMBLE;
        }
        memcpy(wire_buff + DLT698_PREAMBLE_CNT, tx_buff, (size_t)pack_len);
        send_ptr = wire_buff;
#endif

        if (hd->raw_send_cb != NULL) {
            hd->raw_send_cb(hd, send_ptr, send_len, hd->raw_send_channel);
        }

        if (dlt698_tx(hd, (void *)send_ptr, (uint32_t)send_len) < 0) {
            return -1;
        }
    }
    rx_len = dlt698_get_ack(hd, tmp_buff, DLT698_RX_BUFF_MAX, NULL);
    memset(tmp, 0, sizeof(tmp));

    if (rx_len > 0 && hd->raw_receive_cb != NULL) {
        hd->raw_receive_cb(hd, tmp_buff, rx_len, hd->raw_receive_channel);
    }

    if (rx_len > 12) {
        if (dlt698_frame_check(tmp_buff, rx_len, addr) > 0) {
            if (get_raw_data != NULL) {
                get_raw_data((char *)tmp_buff, rx_len, contex);
            }
            re = dlt698_analysis(it, tmp_buff, rx_len, addr, buff, max_len);
            if (re <= 0) {
                BUSINESS_LOG(BLOG_ERR, DAS_ID_DLT698_1, NULL, "dlt698 extract data error");
                re = (errno == ETIMEDOUT) ? 0 : -7;
            }
        } else {
            BUSINESS_LOG(BLOG_ERR, DAS_ID_DLT698_1, NULL, "dlt698 recv check error");
            re = (errno == ETIMEDOUT) ? 0 : -8;
            dlt698_sleep_response_timeout(hd);
            dlt698_flush(hd);
        }
    } 
    else {
        BUSINESS_LOG(BLOG_ERR, DAS_ID_DLT698_1, NULL, "dlt698 rx data error");
        re = (errno == ETIMEDOUT) ? 0 : -3;
    }

    hd->piid++;
    if (hd->piid > DLT698_PIID_MAX) {
        hd->piid = 1;
    }

    return re;
}

void dlt698_set_raw_send_callback(dlt698_hd_t *hd, dlt698_raw_send_cb_t cb, void *channel)
{
    if (hd == NULL) {
        return;
    }
    hd->raw_send_cb = cb;
    hd->raw_send_channel = channel;
}

void dlt698_set_raw_receive_callback(dlt698_hd_t *hd, dlt698_raw_receive_cb_t cb, void *channel)
{
    if (hd == NULL) {
        return;
    }
    hd->raw_receive_cb = cb;
    hd->raw_receive_channel = channel;
}
