#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "mqtt_session.h"
#include "dy_utils/dy_common.h"
#include "cloud_mqtt_common.h"
#include "dy_utils/cJSON.h"
#include "dy_utils/protocol.h"

static int cloud_mqtt_state_change(void *obj, int state)
{
    channel_t *channel = (channel_t *)obj;

    switch (state)
    {
    case MQTT_CONNECTING:
        channel->comm_status = COMM_STATUS_CONNECTING;
        break;
    case MQTT_CONNECTED:
        channel->comm_status = COMM_STATUS_CONNECTED;
        break;
    case MQTT_DISCONNECTED:
        dbg_syslog(LOG_WARNING, "MQTT Disconnect");
        channel->comm_status = COMM_STATUS_IDLE;
        break;
    default:
        dbg_syslog(LOG_ERR, "wrong mqtt state:%d", state);
    }
    return 0;
}

static char *login_print(cloud_mqtt_var_t *var, login_info_t *info, char *gw_sn)
{
    char version[32] = {0};
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        return NULL;
    }
    get_hardware_ver(version);
    cJSON_AddStringToObject(root, "hard_ver", version);
    get_software_ver(version);
    cJSON_AddStringToObject(root, "soft_ver", version);
    cJSON_AddNumberToObject(root, "mi", info->serial_num);
    cJSON_AddNumberToObject(root, "timestamp", info->timestamp);
    cJSON_AddStringToObject(root, "gw_sn", gw_sn);
    cJSON_AddStringToObject(root, "product", var->product_name);
    cJSON_AddStringToObject(root, "data_type", "service");
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);
    return str;
}

static char *heartbeat_print(char *gw_sn, int mi)
{
    cJSON *root = cJSON_CreateObject();
    if (root == NULL)
    {
        return NULL;
    }
    cJSON_AddNumberToObject(root, "mi", mi);
    cJSON_AddStringToObject(root, "gw_sn", gw_sn);
    cJSON_AddStringToObject(root, "data_type", "service");
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);
    return str;
}

static int send_login_msg(cloud_mqtt_var_t *var, channel_t *channel)
{
    login_info_t info;
    char *str = NULL;

    if (channel->login.pub_topic == NULL || strlen(channel->login.pub_topic) == 0)
    {
        return 0;
    }

    info.timestamp = time(NULL);
    info.serial_num = channel->login.mi;
    channel->login.mi++;
    str = login_print(var, &info, var->sn_str);

    mqtt_session_publish(channel->session, channel->login.pub_topic, str, strlen(str));
    free(str);
    return 0;
}

static int send_heartbeat_msg(cloud_mqtt_var_t *var, channel_t *channel)
{
    char *str = NULL;
    int mi = channel->heart_beat.mi++;

    if (channel->heart_beat.pub_topic == NULL || strlen(channel->heart_beat.pub_topic) == 0)
    {
        return 0;
    }

    str = heartbeat_print(var->sn_str, mi);

    mqtt_session_publish(channel->session, channel->heart_beat.pub_topic, str, strlen(str));
    free(str);

    return 0;
}

int status_poll(cloud_mqtt_var_t *var)
{
    int i;
    TIMER_CONFIRM(var->status_poll_timer);
    time_t now = time(NULL);

    for (i = 0; i < var->channel_cnt; i++)
    {
        channel_t *channel = &var->channel[i];
        if (channel->comm_status == COMM_STATUS_CONNECTED)
        {
            if (channel->login.pub_topic == NULL || strlen(channel->login.pub_topic) == 0)
            {
                channel->comm_status = COMM_STATUS_LOGIN;
            }
            else
            {
                if (now > channel->login.last_ts + channel->login.interval)
                {
                    send_login_msg(var, channel);
                    channel->login.last_ts = now;
                }
            }
        }
        else if (channel->comm_status == COMM_STATUS_LOGIN)
        {
            if (now >= (channel->heart_beat.last_ts + channel->heart_beat.interval))
            {
                channel->heart_beat.last_ts = now;
                send_heartbeat_msg(var, channel);
                channel->heart_beat_lost_cnt++;
            }
        }
    }

    return 0;
}

// 建立与broker之间的MQTT连接
int cloud_mqtt_channel_create(cloud_mqtt_var_t *var)
{
    int i = 0, j = 0;
    const char *board_name = NULL;
    board_name = get_board_name();

    for (i = 0; i < var->channel_cnt; i++)
    {
        channel_t *channel = &var->channel[i];

        channel->var = var;
        mqtt_session_set_callbacks(channel->session, recv_cloud_mqtt_msg, cloud_mqtt_state_change);
        channel->comm_status = COMM_STATUS_CONNECTING;
        mqtt_session_start(channel->session);

        if (channel->resume)
        {
			char db_name[32] = {0};
            if (strcmp(board_name, BOARD_WOOLINK_MT7628) == 0)
            {
                sprintf(db_name, "/tmp/MQTT_REPORT_%d.db", i);
            }
            else
            {
                sprintf(db_name, "/app/MQTT_REPORT_%d.db", i);
            }
            dy_db_session_init(&channel->db_session, db_name, (void *)channel);
        }
        for (j = 0; j < channel->topic_maps.downlink_cnt; j++)
        {
            if (strstr(channel->topic_maps.downlink[j].external_topic, "[DEV_SN]"))
            {
                char topic[TOPIC_MAX_LEN] = {0};
                int k = 0;

                for (k = 0; k < var->nodes_cfg_table->node_cnt; k++)
                {
                    replace_sub_str(channel->topic_maps.downlink[j].external_topic, "[DEV_SN]", var->nodes_cfg_table->node[k].sn, topic);
                    dbg_syslog(LOG_INFO, "======subscribe topic:%s", topic);
                    mqtt_session_subscribe(channel->session, topic);
                }

                replace_sub_str(channel->topic_maps.downlink[j].external_topic, "[DEV_SN]", "+", topic);
                strcpy(channel->topic_maps.downlink[j].external_topic, topic);
            }
            else
            {
                mqtt_session_subscribe(channel->session, channel->topic_maps.downlink[j].external_topic);
            }
        }
        if (channel->login.sub_topic != NULL && strlen(channel->login.sub_topic) > 0)
        {
            mqtt_session_subscribe(channel->session, channel->login.sub_topic);
        }

        if (channel->heart_beat.sub_topic != NULL && strlen(channel->heart_beat.sub_topic) > 0)
        {
            mqtt_session_subscribe(channel->session, channel->heart_beat.sub_topic);
        }
    }
    return 0;
}
