#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 "upgrade.h"
#include "upgrade_rel_packages.h"
#include "mqtt_session.h"

#define IPK_BLACK_LIST_CONF "/app/config/ipk_blacklist_conf.json"


int check_package_install(char *name, char *cmd)
{
    int ret = -1;
    char *string = NULL;

    ret = system_reply(cmd, &string);
    if (ret == 0)
    {
        if (!string)return 0;
		//strcpy(wan_ip_addr, string);
		if(strlen(string) && strstr(string, name))
		{
			free(string);
			return 1;
		}
        free(string);
		return 0;
    }
    return -1;
}

static int app_manager_check_installed(upgrade_var_t *var)
{
	app_manager_config_t *app_manager_cfg = NULL;
	list_for_each_entry(app_manager_cfg, &var->config_list, list)
	{
		if(app_manager_cfg->app_cfg.installed == 1)
			continue;
		
		//dy_syslog(LOG_DEBUG, "name %s cmd %s", app_manager_cfg->app_cfg.name,app_manager_cfg->app_cfg.cmd);
		if(strlen(app_manager_cfg->app_cfg.check_cmd))
			app_manager_cfg->app_cfg.installed = check_package_install(app_manager_cfg->app_cfg.name, app_manager_cfg->app_cfg.check_cmd);
		else
		{
			char buff[128] = {0};
			snprintf(buff, sizeof(buff), "opkg list-installed | grep \"%s\"", app_manager_cfg->app_cfg.name);
			app_manager_cfg->app_cfg.installed = check_package_install(app_manager_cfg->app_cfg.name, buff);
		}
		//dyog(LOG_DEBUG, "name %s installed %d",app_manager_cfg->app_cfg.name,app_manager_cfg->app_cfg.installed);
		if(app_manager_cfg->app_cfg.installed != 1)
		{
			app_manager_install(var, app_manager_cfg);
		}
	}
	return 0;
}

static int app_manager_check_timer(upgrade_var_t *var)
{
	TIMER_CONFIRM(var->check_timer);
	
	// app_manager_check_installed(var);
    return 0;
}

static int upgrade_handle_mqtt_data(upgrade_var_t *var, ipc_msg_t *mqtt_msg)
{
    char topic[TOPIC_MAX_LEN] = {0};

    sscanf(mqtt_msg->topic, "M/%*[^/]/%*[^/]/device/%*[^/]/service/%[^/]", topic);
    if (strlen(topic) == 0)
    {
        sscanf(mqtt_msg->topic, "ipc/%*[^/]/%*[^/]/%"STR(TOPIC_MAX_LEN)"s", topic);
    }

    if (strcmp(topic, TOPIC_SET_RELIPKUPDATE) == 0)
    {
        install_rel_ipks(var, mqtt_msg);
    }
    if (strcmp(topic, TOPIC_SET_GATEWAYUPGRADECMD) == 0)
    {
        msg_upgrade_gw(var, mqtt_msg);
    }
    else if (strcmp(topic, TOPIC_GET_APPSLIST) == 0)
    {
        msg_upgrade_requst_apps_list(var, mqtt_msg);
    }
    else if (strcmp(topic, TOPIC_SET_APPREMOVE) == 0)
    {
        msg_upgrade_app_remove(var, mqtt_msg);
    }
    else if (strcmp(topic, TOPIC_SET_APPINSTALL) == 0)
    {
        msg_upgrade_app_install(var, mqtt_msg);
    }
	else if (strcmp(topic, TOPIC_SET_DEPENDAPPINSTALL) == 0)
    {
        msg_upgrade_depend_app_install(var, mqtt_msg);
    }
	else if (strcmp(topic, TOPIC_SET_DEPENDAPPREMOVE) == 0)
    {
        msg_upgrade_depend_app_remove(var, mqtt_msg);
    }
    else if (strcmp(topic, TOPIC_SET_NODESUPGRADECMD) == 0)
    {
        // not USED
        dy_syslog(LOG_ERR, "OLD TOPIC, don't support any more");
    }
    else if (strcmp(topic, TOPIC_UPGRADE_NODE) == 0)
    {
        msg_upgrade_node(var, mqtt_msg);
    }
    return 0;
}

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

    while (1)
    {
        SELECT_INIT();
        SELECT_ADD_FD(var->check_timer);
        timeout.tv_usec = 0;
        timeout.tv_sec = 10;

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            int error = errno;
            printf("errno %d\n", error);
            if (error == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret == 0)
        {
        }
        else
        {
            if (var->check_timer > 0 && FD_ISSET(var->check_timer, &rset))
            {
                FD_CLR(var->check_timer, &rset);
                app_manager_check_timer(var);
            }
        }
    }
}

static void upgrade_subscribe_all(upgrade_var_t *var)
{
    ipc_session_t *ipc_session = var->session;
    char topic[TOPIC_MAX_LEN] = {0};
    int i = 0;
    char *internal_topics[] =
    {
        TOPIC_SET_GATEWAYUPGRADECMD,
        TOPIC_SET_NODESUPGRADECMD,
        TOPIC_GET_APPSLIST,
        TOPIC_SET_APPINSTALL,
        TOPIC_SET_APPREMOVE,
        TOPIC_UPGRADE_NODE,
        TOPIC_SET_DEPENDAPPINSTALL,
        TOPIC_SET_DEPENDAPPREMOVE,
        TOPIC_SET_RELIPKUPDATE,
    };

    for (i = 0; i < ARRAY_SIZE(internal_topics); i++)
    {
        snprintf(topic, TOPIC_MAX_LEN, "M/+/%s/device/%s/service/%s", TOPIC_GATEWAY_CONFIG, var->sn_str, internal_topics[i]);
        ipc_session_subscribe(ipc_session, topic);
    }

    snprintf(topic, TOPIC_MAX_LEN, "ipc/+/+/%s", TOPIC_UPGRADE_NODE);
    ipc_session_subscribe(ipc_session, topic);
}

static int upgrade_state_change(void *obj, int state)
{
    switch (state)
    {
    case MQTT_CONNECTED:
        dy_syslog(LOG_INFO, "upgrade mqtt connected\n");
        upgrade_var_t *var = (upgrade_var_t *)obj;
        if (send_ipk_update_req(var->session, 1) != 0) {
            dy_syslog(LOG_ERR, "send ipk update request failed!\n");
        }
        break;
    case MQTT_DISCONNECTED:
        dy_syslog(LOG_ERR, "upgrade mqtt disconnected!\n");
        break;
    default:
        dy_syslog(LOG_ERR, "upgrade mqtt unknown state\n");
    }

    return 0;
}

static int upgrade_mqtt_handle_recv_msg(void *obj, ipc_msg_t *mqtt_msg)
{
    upgrade_var_t *var = (upgrade_var_t *)obj;

    dy_syslog(LOG_DEBUG, " MQTT client: received MQTT topic:%s payload:%s length:%d",
              mqtt_msg->topic, mqtt_msg->payload, mqtt_msg->payloadLen);
    upgrade_handle_mqtt_data(var, mqtt_msg);
    return 0;
}

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

    snprintf(clientId, sizeof(clientId), "INT_upgrade_%s", var->sn_str);
    var->session = ipc_session_new(clientId, (void*)var, IPC_DEFAULT);
    if (var->session == NULL) return -1;

    ipc_session_set_callbacks(var->session, upgrade_mqtt_handle_recv_msg, upgrade_state_change);
    upgrade_subscribe_all(var);
    ipc_session_start(var->session);
    return 0;
}

static int load_ipk_blacklist_cfg_to_hash(struct hash_intptr **ipk_blacklist_hash, const char *file)
{
    char *json_str = NULL;

    json_str = read_file_data(IPK_BLACK_LIST_CONF);
    if (!json_str)
    {
        dy_syslog(LOG_ERR, "json_str: NULL");
        return -1;
    }

    cJSON *root = cJSON_Parse(json_str);
    if (root == NULL) {
        dy_syslog(LOG_ERR, "cJSON_Parse failed!!!");
        return -1;
    }

    cJSON *name = NULL;
    cJSON_ArrayForEach(name, root) {
        if (name == NULL) {
            dy_syslog(LOG_ERR, "get item of json array failed!");
            return -1;
        }
        dy_syslog(LOG_INFO, "ipk_name: %s", name->valuestring);

        if (hash_intptr_addint(ipk_blacklist_hash, name->valuestring, strlen(name->valuestring), 1) < 0) {
            dy_syslog(LOG_ERR, "hash_intptr_addint, key: %s failed!",  name->valuestring);
            return -1;
        }
    }

    cJSON_Delete(root);

    return 0;
}

static int upgrade_init(upgrade_var_t *var)
{
	INIT_LIST_HEAD(&var->config_list);
    get_board_sn(var->sn_str);
    get_process_name(var->proc_name);

    upgrade_mqtt_client_init(var);

    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");
    }
    if (load_ipk_blacklist_cfg_to_hash(&var->ipk_blacklist_hash, IPK_BLACK_LIST_CONF) == -1)
    {
        dy_syslog(LOG_ERR, "load ipk blacklist cfg fail");
    }

	//加载配置
	app_manager_load_config(var, APP_MANAGER_CFG);
	//检查包安装情况
	app_manager_check_installed(var);

	var->check_timer = my_timer_create();
    if (var->check_timer > 0)
    {
    	my_timer_set(var->check_timer, 3, 60000);
    }
	
    dy_syslog(LOG_INFO, "init done, board SN:%s", var->sn_str);

    return 0;
}

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

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

    upgrade_init(&var);
    upgrade_loop(&var);

    return 0;
}
