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

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

RUN_MODE_E get_gateway_run_mode()
{
	int mode = 0;
    FILE *fp = NULL;
    char buf[128];

    if (access(GATEWAY_RUN_MODE_CFG_FILE, F_OK) == -1)
    {
        // file not exist or cannot access
        // consider as unregistered
        mode = RUN_MODE_UNREGISTERED;
        goto out;
    }

    fp = fopen(GATEWAY_RUN_MODE_CFG_FILE, "r");
    if (fp == NULL)
    {
        /* The file could not be opened */
        // consider as unregistered
        mode = RUN_MODE_UNREGISTERED;
        goto out;
    }
    fgets(buf, sizeof(buf), fp);

    if (sscanf(buf, "%d", &mode) == 0)
		mode = 0;

out:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return (RUN_MODE_E) mode;
}

static int set_gateway_run_mode(RUN_MODE_E mode)
{
    FILE *fp = NULL;
    int ret = 0;

    fp = fopen(GATEWAY_RUN_MODE_CFG_FILE, "w+");
    if (fp == NULL)
    {
        /* The file could not be opened */
        // consider as unregistered
        ret = -1;
        dy_syslog(LOG_ERR, "write run mode file failed");
        goto out;
    }
    fprintf(fp, "%d", mode);
    fflush(fp);

out:
    if (fp != NULL)
    {
        fclose(fp);
    }
    return ret;
}

static int login_prase(login_info_t *info, const char *str)
{
    if (!info || !str)
    {
        return -1;
    }
    cJSON *root = cJSON_Parse(str);
    if (!root)
    {
        return -1;
    }
    GET_JSON_VALUE_INT(root, "mi", info->serial_num);
    GET_JSON_VALUE_INT(root, "timestamp", info->timestamp);
    GET_JSON_VALUE_INT(root, "error_code", info->error_code);
    cJSON_Delete(root);
    return 0;
}

static void msg_login_result(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
    login_info_t info = {0};
    int ret = login_prase(&info, mqtt_msg->payload);

    if (!ret)
    {
        if (info.error_code == LOGIN_PASS)
        {
            var->comm_status = COMM_STATUS_LOGIN;
        }
        else if (info.error_code == LOGIN_REFUSE)
        {
            dy_syslog(LOG_ERR, "gateway login failed");
            if (var->using_default_server == 0)
            {
                refuse_master_server(var);
            }
        }
    }
}

static void msg_remote_reboot(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
    int ret = ERR_CODE_NONE;
    int msgid = 0;

    cJSON *root = cJSON_Parse(mqtt_msg->payload);
    if (root == NULL)
    {
        ret = ERR_CODE_JSON_FORMAT;
        goto out;
    }
    GET_JSON_VALUE_INT(root, "mi", msgid);
    gw_common_result(var->session, msgid, ret, NULL);
    cJSON_Delete(root);

    system("reboot -d 5&");
    dy_syslog(LOG_EMERG, "************* [ Wait 5S system will be reboot ] *************");
out:
    dy_syslog(LOG_ERR, "remote reboot, parse json failed");
}

static void msg_confirm_connect(cloud_conn_var_t *var)
{
    var->heart_beat_lost_cnt = 0;
}

void set_gw_rtc_msg(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
    dy_syslog(LOG_WARNING, "Not support any more");
}

void set_gw_run_mode_msg(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
    int ret = ERR_CODE_NONE;
    int msgid = 0;
    RUN_MODE_E mode = 0;

    cJSON *root = cJSON_Parse(mqtt_msg->payload);
    if (root == NULL)
    {
        ret = ERR_CODE_JSON_FORMAT;
        goto out;
    }
    GET_JSON_VALUE_INT(root, "mi", msgid);
    GET_JSON_VALUE_INT(root, "run_mode", mode);
    cJSON_Delete(root);

    set_gateway_run_mode(mode);
    gateway_login_msg(var);

out:
    gw_common_result(var->session, msgid, ret, NULL);
}

void get_gw_run_mode_msg(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
    int msgid = 0;
    char topic[TOPIC_MAX_LEN];

    cJSON *root = cJSON_Parse(mqtt_msg->payload);
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "MQTT msg is not json format");
        return;
    }
    GET_JSON_VALUE_INT(root, "mi", msgid);
    cJSON_Delete(root);

    root = cJSON_CreateObject();
    if (root == NULL)
    {
        dy_syslog(LOG_ERR, "cJSON create failed");
        return;
    }

    cJSON_AddNumberToObject(root, "mi", msgid);
    cJSON_AddNumberToObject(root, "run_mode", get_gateway_run_mode());
    char *str = (cJSON_Print(root));
    cJSON_Delete(root);

    snprintf(topic, TOPIC_MAX_LEN, "G/%s/%s", var->sn_str, TOPIC_RSP_RUNMODECFG);
    mqtt_session_publish(var->session_cloud, topic, str, strlen(str));
    free(str);
}

void run_diag_info(cloud_conn_var_t *var, mqtt_message_t *mqtt_msg)
{
#define OUPPUT_STR_LEN (4096 * 8)
    DIR *dir;
    struct dirent *ptr;
    char output_str[OUPPUT_STR_LEN];
    int cur = 0;
    char filename[sizeof("/proc/%u/task/%u/cmdline") + sizeof(int) * 3 * 2];
    char topic[TOPIC_MAX_LEN];
    int i;

    if ((dir = opendir("/proc")) == NULL)
    {
        perror("Open dir error...");
        exit(1);
    }

    while ((ptr = readdir(dir)) != NULL)
    {
        if (strcmp(ptr->d_name, ".") == 0 || strcmp(ptr->d_name, "..") == 0) /// current dir OR parrent dir
            continue;

        if (ptr->d_type == 4) /// dir
        {
            if (string_is_number(ptr->d_name))
            {
                int pid = strtoul(ptr->d_name, NULL, 10);
                DIR *dir_t;
                struct dirent *ptr_t;

                printf("pid:%d", pid);

                cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "%d:\n", pid);
                cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\tTASKS:\n");
                sprintf(filename, "/proc/%u/task", pid);
                if ((dir_t = opendir(filename)) == NULL)
                {
                    perror("Open dir error...");
                    continue;
                }

                while ((ptr_t = readdir(dir_t)) != NULL)
                {
                    if (strcmp(ptr_t->d_name, ".") == 0 || strcmp(ptr_t->d_name, "..") == 0) /// current dir OR parrent dir
                        continue;

                    cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\t\t%s\n", ptr_t->d_name);
                }
                closedir(dir_t);

                cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\tFDS:\n");
                sprintf(filename, "/proc/%u/fd", pid);
                if ((dir_t = opendir(filename)) == NULL)
                {
                    perror("Open dir error...");
                    continue;
                }

                while ((ptr_t = readdir(dir_t)) != NULL)
                {
                    char buf[PATH_MAX];
                    char fname[PATH_MAX + 32];
                    if (strcmp(ptr_t->d_name, ".") == 0 || strcmp(ptr_t->d_name, "..") == 0) /// current dir OR parrent dir
                        continue;

                    memset(buf, 0, sizeof(buf));
                    sprintf(fname, "/proc/%u/fd/%s", pid, ptr_t->d_name);
                    readlink(fname, buf, PATH_MAX);
                    cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\t\t%s\n", buf);
                }
                closedir(dir_t);

                sprintf(filename, "/proc/%u/cmdline", pid);
                cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\tcmdline:");
                cur += open_read_close(filename, output_str + cur, OUPPUT_STR_LEN - cur);
                cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "\n");
            }
        }
    }
    closedir(dir);

    sprintf(filename, "/proc/meminfo");
    cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "meminfo:\n");
    cur += open_read_close(filename, output_str + cur, OUPPUT_STR_LEN - cur);

    sprintf(filename, "/proc/uptime");
    cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "uptime:\n");
    cur += open_read_close(filename, output_str + cur, OUPPUT_STR_LEN - cur);

    sprintf(filename, "/proc/stat");
    cur += snprintf(output_str + cur, OUPPUT_STR_LEN - cur, "state:\n");
    cur += open_read_close(filename, output_str + cur, OUPPUT_STR_LEN - cur);
    output_str[cur] = '\0';

    for (i = 0; i < cur; i++)
    {
        if (output_str[i] == 0)
            output_str[i] = ' ';
    }
    snprintf(topic, TOPIC_MAX_LEN, "G/%s/%s", var->sn_str, TOPIC_COLLECT_DIAG_INFO);
    mqtt_session_publish(var->session_cloud, topic, output_str, strlen(output_str));
}

static char *get_last_topic(char *buf, char *sep)
{
    char *tok;
    char *last = NULL;

    tok = strtok(buf, sep);
    last = tok;
    while (tok)
    {
        last = tok;
        tok = strtok(0, sep);
    }

    return last;
}

int recv_host_msg(void *obj, mqtt_message_t *mqtt_msg)
{
    cloud_conn_var_t *var = (cloud_conn_var_t *)obj;
    char *topic = NULL;
    char *buf = NULL;
    char port[TOPIC_MAX_LEN] = {0};

    post_net_led(LED_TWINKLE_ONCE);
    dy_syslog(LOG_DEBUG, "External MQTT client received MQTT topic:%s payload:%s",
              mqtt_msg->topic, mqtt_msg->payload);

    buf = strdup(mqtt_msg->topic);
    topic = get_last_topic(buf, "/");

    if (strcmp(topic, TOPIC_COLLECT_DIAG_INFO) == 0)
    {
        run_diag_info(var, mqtt_msg);
    }

    // 网关配置通道
    // M/[GW SN]/gateway/device/[GW SN]/service/Set_ServerRedirect
    sscanf(mqtt_msg->topic, "M/%*[^/]/%[^/]/device/%*[^/]/service/%*[^/]", port);

    if (strcmp(port, TOPIC_GATEWAY_CONFIG) == 0)
    {
        // 配置网关
        if (strcmp(topic, TOPIC_GW_REBOOT) == 0)
        {
            msg_remote_reboot(var, mqtt_msg);
        }
        else if (strcmp(topic, TOPIC_RSP_GETRTC) == 0)
        {
            set_gw_rtc_msg(var, mqtt_msg);
        }
        else if (strcmp(topic, TOPIC_SET_RUNMODECFG) == 0)
        {
            set_gw_run_mode_msg(var, mqtt_msg);
        }
        else if (strcmp(topic, TOPIC_GET_RUNMODECFG) == 0)
        {
            get_gw_run_mode_msg(var, mqtt_msg);
        }
        else
        {
            // 发布到内部broker交由其它模块处理
            int i = 0;

            while (i < 5)
            {
                int r = 0;
                r = ipc_session_publish(var->session, mqtt_msg->topic, mqtt_msg->payload, strlen(mqtt_msg->payload));
                if (r == 0)
                    break;
                else
                {
                    i++;
                }
            }
        }
    }
    else
    {
        if (strcmp(topic, TOPIC_RSP_HEARTBEAT) == 0)
        {
            msg_confirm_connect(var);
        }
        else if (strcmp(topic, TOPIC_RSP_LOGIN) == 0)
        {
            msg_login_result(var, mqtt_msg);
        }
        else if (strcmp(topic, TOPIC_GW_LOGOUT) == 0)
        {
            var->comm_status = COMM_STATUS_CONNECTED;
        }
    }

    free(buf);
    return 0;
}
