#include <sys/select.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <termios.h>

#include "ble_common.h"

static int ble_read_msg(int fd, unsigned char *rcv_buf, int max_len)
{
    int len = 0;
    int ret = 0;

    while (1)
    {
        ret = read(fd, rcv_buf + len, max_len - len);
        if (ret > 0)
        {
            len += ret;
        }
        else if (ret == 0)
        {
            break;
        }
        else
        {
            if (errno == EINTR || errno == EAGAIN)
            {
                continue;
            }
            else
            {
                return -1;
            }
        }
    }

    return len;
}

static int ViolentMatch(unsigned char *s, int sLen, unsigned char *p, int pLen)
{
    int i = 0;
    int j = 0;
    while (i < sLen && j < pLen)
    {
        if (s[i] == p[j])
        {
            //①如果当前字符匹配成功（即S[i] == P[j]），则i++，j++
            i++;
            j++;
        }
        else
        {
            //②如果失配（即S[i]! = P[j]），令i = i - (j - 1)，j = 0
            i = i - j + 1;
            j = 0;
        }
    }
    //匹配成功，返回模式串p在文本串s中的位置，否则返回-1
    if (j == pLen)
        return i - j;
    else
        return -1;
}

int ble_get_cur_role(int fd)
{
    char buf[64] = {0};
    char rcv_buf[64] = {0};
    int ret = 0;
    int len;
    int retry_cnt = 0;

    if (fd < 0)
    {
        return -1;
    }

retry:
    sprintf(buf, "AT+ROLE?");
    tcflush(fd, TCIFLUSH);
    len = UART0_Send(fd, buf, strlen(buf));
    if (len <= 0)
    {
        dy_syslog(LOG_ERR, "set buf to BLE failed");
        ret = -1;
    }
    else
    {
        len = UART0_Recv(fd, rcv_buf, sizeof(rcv_buf));
        if (len > 0)
        {
            dy_syslog_hex(LOG_DEBUG, rcv_buf, len, "reply for:%s", buf);
            int pos = ViolentMatch(rcv_buf, len, "+OK=", strlen("+OK="));
            if (pos != -1)
            {
                sscanf(rcv_buf + pos, "+OK=%d", &ret);
                dy_syslog(LOG_DEBUG, "pos:%d ret:%d", pos, ret);
            }
            else
            {
                ret = -1;
                retry_cnt;
                if (retry_cnt < 10)
                {
                    goto retry;
                }
            }
        }
    }

    return ret;
}

int ble_send_data(int fd, char *buf, int len)
{
    char rcv_buf[64] = {0};
    int ret = 0;
    int retry_cnt = 0;

    if (fd < 0)
        return -1;
retry:
    tcflush(fd, TCIFLUSH);
    len = UART0_Send(fd, buf, len);
    if (len <= 0)
    {
        dy_syslog(LOG_ERR, "set buf to BLE failed");
        ret = -1;
    }
    else
    {
        len = UART0_Recv(fd, rcv_buf, sizeof(rcv_buf));
        if (len > 0)
        {
            dy_syslog(LOG_DEBUG, "rcv buf:%s for:%s", rcv_buf, buf);
            if (ViolentMatch(rcv_buf, len, "+OK", strlen("+OK")) != -1)
            {
                ret = 0;
            }
            else
            {
                ret = -1;
                retry_cnt++;
                if (retry_cnt < 10)
                {
                    goto retry;
                }
            }
        }
    }

    return ret;
}

static int pub_ble_data(ble_var_t *var, unsigned char *mac, unsigned int rssi, unsigned char *buf, int len)
{
    tag_table_t ptag = {0};
    int i, j, k;
    time_t now = time(NULL);
    cJSON *advdata = NULL;
    char buf_b64[256];

    ptag.data_type = DATA_TYPE_SERVICE;
    strcpy(ptag.identifier, "BLE_raw_data");
    ptag.report_period = 0;
    ptag.time = now;

    advdata = cJSON_CreateObject();

    if (advdata != NULL)
    {
        char *data_tmp = NULL;

        char mac_str[32];
        sprintf(mac_str, "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
        cJSON_AddStringToObject(advdata, "MAC", mac_str);
        b64_encode(buf, len, buf_b64, sizeof(buf_b64));
        cJSON_AddStringToObject(advdata, "Advdata", buf_b64);
        cJSON_AddNumberToObject(advdata, "RSSI", rssi);
        sprintf(ptag.sn, "%s_BLE", var->sn_str);
        data_tmp = cJSON_Print(advdata);
        if (strlen(data_tmp) > 2)
        {
            ptag.tag_node = data_tmp;
            pp_send_tag_data(var->session, &ptag);
        }
        cJSON_Delete(advdata);
        advdata = NULL;
        free(data_tmp);
    }

    return 0;
}

int handle_ble_msg(ble_var_t *var, unsigned char *buf, int tail)
{
    static int pos = 0;
    int data_len;
    unsigned char mac[6];
    int rssi;
    unsigned char *advdata = NULL;
    int len = 0;
    int count = 0;

process_more:
    dy_syslog(LOG_DEBUG, "count:%d pos:%d tail:%d", count, pos, tail);
    if (tail < pos)
    {
        len = (BLE_DATA_BUF_SIZE - pos) + tail;
    }
    else if (tail > pos)
    {
        len = tail - pos;
    }
    else
    {
        return 0;
    }

    dy_syslog(LOG_DEBUG, "len:%d", len);

    if (len < 8)
    {
        dy_syslog(LOG_DEBUG, "data is not complete, len:%d", len);
        return 0;
    }

    data_len = buf[pos];
    dy_syslog(LOG_DEBUG, "data_len:%d", data_len);
    if (data_len == 0 || data_len > 39)
    {
        dy_syslog(LOG_ERR, "Data is wrong, restart the program!!!!");
        system("/etc/init.d/e104bt52 restart&");
    }
    if (data_len > len)
    {
        dy_syslog(LOG_DEBUG, "data is not complete, len:%d", len);
        return 0;
    }
    if (pos + data_len > BLE_DATA_BUF_SIZE)
    {
        // roll back
        int part = BLE_DATA_BUF_SIZE - pos;
        dy_syslog(LOG_DEBUG, "roll back, part:%d", part);
        unsigned char *tmp_buf = malloc(data_len);
        memcpy(tmp_buf, buf + pos, part);
        memcpy(tmp_buf + part, buf, data_len - part);
        memcpy(mac, tmp_buf + 1, 6);
        rssi = tmp_buf[7];
        advdata = tmp_buf + 8;
        dy_syslog_hex(LOG_DEBUG, tmp_buf, data_len, "====len:%d BLE AD data len:%d mac:%02X:%02X:%02X:%02X:%02X:%02X rssi:%d", len, data_len, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], rssi);
        pub_ble_data(var, mac, rssi, advdata, data_len - 8);
        free(tmp_buf);
        pos = data_len - part;
    }
    else
    {
        memcpy(mac, buf + pos + 1, 6);
        rssi = buf[pos + 7];
        advdata = buf + pos + 8;
        dy_syslog_hex(LOG_DEBUG, buf + pos, data_len, "====len:%d BLE AD data len:%d mac:%02X:%02X:%02X:%02X:%02X:%02X rssi:%d", len, data_len, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5], rssi);
        pub_ble_data(var, mac, rssi, advdata, data_len - 8);
        pos = pos + data_len;
        if (pos == BLE_DATA_BUF_SIZE)
        {
            pos = 0;
        }
    }

    count++;
    goto process_more;

    return 0;
}

void *recv_loop(void *param)
{
    ble_var_t *var = (ble_var_t *)param;
    int fd = var->ble_cfg.tty_fd;
    int ret = -1, maxfd, i;
    fd_set rset;
    struct timeval timeout;
    unsigned char *ring_buf = NULL;
    int len = 0;
    int count = 0;
    int pos = 0;
    char buf[64] = {0};

    ring_buf = calloc(1, BLE_DATA_BUF_SIZE);
    if (ring_buf == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return NULL;
    }

    sprintf(buf, "AT+RESET");
    ble_send_data(fd, buf, strlen(buf));
    tcflush(fd, TCIOFLUSH);
    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(fd);

        timeout.tv_usec = 0;
        timeout.tv_sec = 10;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            break;
        }
        else if (ret > 0)
        {
            if (fd > 0 && FD_ISSET(fd, &rset))
            {
                FD_CLR(fd, &rset);
                // read data from tty, max time once read is 1024
                len = ble_read_msg(fd, ring_buf + pos, BLE_DATA_BUF_SIZE - pos > 1024 ? 1024 : BLE_DATA_BUF_SIZE - pos);
                if (len > 0)
                {
                    pos = pos + len;
                    if (pos >= BLE_DATA_BUF_SIZE)
                    {
                        pos = 0;
                    }
                    handle_ble_msg(var, ring_buf, pos);
                }
            }
        }
    }

    free(ring_buf);

    return NULL;
}
