#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 "ble_common.h"
#include "uart.h"

gw_port_e ports[] = {BLE};

static int ds_flush_timer(ble_var_t *var)
{
    time_t now = time(NULL);

    TIMER_CONFIRM(var->node_status_timer);

    if (now >= var->nodes_status_sync_date + 30)
    {
        static time_t lasttime = 0;
        var->nodes_status_sync_date = now;
        char *node_status = ds_print(var->ds);

        dy_syslog(LOG_INFO, "online_status_changed:%d strlen(node_status):%d, (now-lasttime):%d",
                  var->ds->online_status_changed, strlen(node_status), now - lasttime);
        if (node_status && strlen(node_status) > 20 &&
            (var->ds->online_status_changed || now > lasttime + 3600)) //在线状态变化上报，1小时也会上报
        {
            char topic[TOPIC_MAX_LEN];
            int ret = 0;

            snprintf(topic, TOPIC_MAX_LEN, "ipc/%s/%s/%s", var->sn_str, "ble", TOPIC_EVT_NODESSTATUS);

            ret = dy_mqtt_session_publish(var->session, topic, node_status, strlen(node_status));
            if (ret == 0)
            {
                lasttime = now;
                var->ds->online_status_changed = false;
            }
            node_sta_backup(var->ds, BLE_NODE_STATUS_BAK_FILE);
        }
        if (node_status)
        {
            write_file_data(NODES_CACHE "/ble_status.json", node_status, strlen(node_status));
            free(node_status);
        }
    }
}

int ble_send_rglt(ble_var_t *var, pp_regulate_signal_t *regulate_data)
{
    char *data_b64 = NULL;
    char topic[TOPIC_MAX_LEN] = {0};
    cJSON *mqtt_json = NULL;
    int len;
    char *data_tmp = NULL;
    int mi;
    int period;
    int buff_len = regulate_data->len * 2 + 1024;

    data_b64 = (char *)malloc(buff_len);
    if (data_b64 == NULL)
    {
        dy_syslog(LOG_ERR, "malloc fail");
        return -1;
    }

    //store the identifier
    {
        char key_str[64] = {0};
        char *identifier = NULL;
        regulate_cmd_backup_t fields;
        time_t now = time(NULL);

        strcpy(fields.src_identifier, regulate_data->src_identifier);
        strcpy(fields.sn, regulate_data->sn);
        fields.mi = regulate_data->mi;
        fields.ts = now;
        strcpy(fields.requester, regulate_data->requester);
        sprintf(key_str, "%s", regulate_data->sn);
        free(identifier);
        var->identifier_backup.insert(&var->identifier_backup, key_str, &fields, sizeof(fields));
    }

    //将数据加入list,由poll函数去发送
    //send_ble_data(var, regulate_data->data, regulate_data->len, regulate_data->term_addr, 0, UNICAST_NO);

    flush_device_status_by_sn(var->ds, ACTION_SND, regulate_data->sn);
}

static int ble_msg_tag_data_ctrl(ble_var_t *var, mqtt_message_t *mqtt_msg)
{
    pp_regulate_signal_t regulate_data;
    int ret = 0;

    ret = get_rglt_data_from_json(&regulate_data, mqtt_msg->payload);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "parse real data structure failed");
        return -1;
    }
    if (regulate_data.period > 0)
    {
        period_msg_t once;

        dy_syslog(LOG_INFO, "Recevied interval ctrl cmd periad %d", regulate_data.period);
        snprintf(once.key, 128, "%s_%s", regulate_data.sn, regulate_data.src_identifier);
        once.interval = regulate_data.period;
        once.last_poll = 0;
        once.rs = calloc(1, sizeof(pp_regulate_signal_t));
        memcpy(once.rs, &regulate_data, sizeof(pp_regulate_signal_t));
        once.started = 1;
        once.rs->data = calloc(1, regulate_data.len);
        memcpy(once.rs->data, regulate_data.data, regulate_data.len);

        period_msg_add(var->period, &once);
    }
    else
    {
        ret = ble_send_rglt(var, &regulate_data);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
            return -1;
        }
    }

out:
    free(regulate_data.data);

    return 0;
}

static int period_service_poll(ble_var_t *var)
{
    TIMER_CONFIRM(var->period_poll_timer);
    int ret = 0;
    static int all_started = 0;
    static time_t last_time = 0;
    time_t now = time(NULL);

    period_msg_t *msg = period_msg_next(var->period);
    if (msg && msg->started)
    {
        ret = ble_send_rglt(var, msg->rs);
        if (ret < 0)
        {
            dy_syslog(LOG_ERR, "parse real data structure failed");
        }
    }

    if (now - last_time > 3600)
    {
        period_services_trigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
        last_time = now;
        all_started = 0;
    }

    if (all_started == 0)
    {
        all_started = period_services_retrigger(var->session, var->period, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
    }
    return 0;
}

static void ble_loop(ble_var_t *var)
{
    int ret = -1, maxfd, i;
    fd_set rset;
    struct timeval timeout;

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->node_status_timer);

        timeout.tv_usec = 0;
        timeout.tv_sec = 5;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->node_status_timer > 0 && FD_ISSET(var->node_status_timer, &rset))
            {
                FD_CLR(var->node_status_timer, &rset);
                ds_flush_timer(var);
            }
        }
    }
}

static void ble_subscribe_all(ble_var_t *var)
{
    mqtt_session_t *mqtt_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};
    int i = 0;

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/device/+/data/%s", "ble", TOPIC_SEND_RGLT_SIGNAL_RAW_DATA);
    dy_mqtt_session_subscribe(mqtt_session, topic);
    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/%s/%s", "ble", TOPIC_NOTIFY_UPGRADE);
    dy_mqtt_session_subscribe(mqtt_session, topic);
}

static int ble_mqtt_handle_recv_msg(void *obj, mqtt_message_t *mqtt_msg)
{
    ble_var_t *var = (ble_var_t *)obj;
    dy_syslog(LOG_DEBUG, "received MQTT topic:%s payload length:%d", mqtt_msg->topic, mqtt_msg->payloadLen);
    if (strstr(mqtt_msg->topic, TOPIC_SEND_RGLT_SIGNAL_RAW_DATA))
    {
        //发送下行数据
        ble_msg_tag_data_ctrl(var, mqtt_msg);
    }
}

// 建立与内部broker之间的MQTT连接
static int ble_mqtt_client_init(ble_var_t *var)
{
    char clientId[MAX_CLIENT_ID_LEN] = {0};

    snprintf(clientId, MAX_CLIENT_ID_LEN, "INT_ble_%s", var->sn_str);
    var->session = dy_mqtt_session_new(clientId);
    if (var->session == NULL)
        return -1;

    dy_mqtt_session_set_address(var->session, INTERNAL_BROKER_ADDR, INTERNAL_BROKER_PORT, INTERNAL_BROKER_USER, INTERNAL_BROKER_PASS);
    dy_mqtt_session_set_opts(var->session, DEFAULT_IPC_QOS, KEEP_ALIVE_MAX);
    dy_mqtt_session_set_callbacks(var->session, ble_mqtt_handle_recv_msg, NULL);

    dy_mqtt_session_init(var->session, (void *)var);
    ble_subscribe_all(var);
}

static int ble_load_config(ble_var_t *var, char *cfg_file)
{
    char *data = NULL;
    cJSON *root = NULL;
    int ret = -1;
    char buf[64] = {0};

    strcpy(var->ble_cfg.dev, DEFAULT_TTY_DEV);
    var->ble_cfg.baudrate = DEFAULT_BAUD_RATE;
    var->ble_cfg.mode = SNIFFER;
    var->ble_cfg.scan_intv = DEFAULT_SCAN_INTV;
    var->ble_cfg.scan_wind = DEFAULT_SCAN_WIND;

    if (cfg_file == NULL)
    {
        dy_syslog(LOG_ERR, "cfg_file:%s not exist", cfg_file);
        goto out;
    }

    data = read_file_data(cfg_file);
    if (data == NULL)
    {
        dy_syslog(LOG_ERR, "read cfg file %s error", cfg_file);
        goto out;
    }

    root = cJSON_Parse(data);
    if (!root)
    {
        dy_syslog(LOG_ERR, "parse cfg file %s error", cfg_file);
        goto out;
    }

    GET_JSON_VALUE_STRING(root, "dev", var->ble_cfg.dev);
    GET_JSON_VALUE_INT(root, "role", var->ble_cfg.mode);
    GET_JSON_VALUE_INT(root, "baudrate", var->ble_cfg.baudrate);
    GET_JSON_VALUE_INT(root, "scan_intv", var->ble_cfg.scan_intv);
    GET_JSON_VALUE_INT(root, "scan_wind", var->ble_cfg.scan_wind);

    cJSON_Delete(root);
    ret = 0;

out:
    if (data)
    {
        free(data);
    }

    var->ble_cfg.tty_fd = uart_init(var->ble_cfg.dev, var->ble_cfg.baudrate, 1, 0, 8, 1);
    if (var->ble_cfg.tty_fd > 0)
    {
        sleep(1);
    }
    else
    {
        dy_syslog(LOG_ERR, "dev:%s open failed\n", var->ble_cfg.dev);
        return -1;
    }

    int role = ble_get_cur_role(var->ble_cfg.tty_fd);

    if (role != var->ble_cfg.mode)
    {
        dy_syslog(LOG_DEBUG, "currnt role is :%d, switch to %d", role, var->ble_cfg.mode);
        sprintf(buf, "AT+ROLE=%d", var->ble_cfg.mode);
        ble_send_data(var->ble_cfg.tty_fd, buf, strlen(buf));
        sprintf(buf, "AT+SCANINTV=%d", var->ble_cfg.scan_intv);
        ble_send_data(var->ble_cfg.tty_fd, buf, strlen(buf));
        sprintf(buf, "AT+SCANWND=%d", var->ble_cfg.scan_wind);
        ble_send_data(var->ble_cfg.tty_fd, buf, strlen(buf));
        sprintf(buf, "AT+RESET");
        ble_send_data(var->ble_cfg.tty_fd, buf, strlen(buf));
    }
    return ret;
}

int ble_init(ble_var_t *var)
{
    int i, j;
    pthread_t thread_rcv;

    get_board_sn(var->sn_str);
    var->board_name = get_board_name();

    if (load_nodes_cfg(&var->nodes_cfg_table, NODES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load nodes cfg fail");
    }
    if (load_templates_cfg(&var->template_table, TEMPLATES_CFG_PATH) == -1)
    {
        dy_syslog(LOG_ERR, "load template cfg fail");
    }
    dev_status_init(&var->ds, var->nodes_cfg_table, var->template_table, ports, ARRAY_SIZE(ports));
    node_sta_recovery(var->ds, BLE_NODE_STATUS_BAK_FILE);

    ble_load_config(var, BLE_CFG);

    period_msg_init(&var->period, 128);
    kv_array_init(&var->identifier_backup, 32);

    ble_mqtt_client_init(var);

    pthread_create(&thread_rcv, NULL, recv_loop, (void *)var);

    var->node_status_timer = my_timer_create();
    if (var->node_status_timer > 0)
    {
        my_timer_set(var->node_status_timer, 1, 5000);
    }

    var->period_poll_timer = my_timer_create();
    if (var->period_poll_timer > 0)
    {
        my_timer_set(var->period_poll_timer, 1, 1000);
    }

    return 0;
}

int main(int argc, char *argv[])
{
    ble_var_t var = {0};

    if ((access("/tmp/e104bt52_started", F_OK)) != -1)
    {
        dy_syslog(LOG_DEBUG, "NOT first start");
    }
    else
    {
        sleep(30);
        system("touch /tmp/e104bt52_started");
    }

    openlog("ble", LOG_PID, LOG_DAEMON);

    memset(&var, 0, sizeof(var));

    ble_init(&var);
    ble_loop(&var);

    return 0;
}
