#include "pp.h"
#include "dy_utils/dy_pp.h"
#include <sys/wait.h>
#include <sys/prctl.h>
static void __free_script_msg_node(pp_script_msg_node_t *node)
{
    switch (node->type)
    {
    case SCRIPT_DECODE:
        break;

    case SCRIPT_ENCODE:
        break;

    default:
        break;
    }

    free(node);
}

static char *get_node_by_sn(char *sn)
{
    char *data = NULL;
    cJSON *root = NULL;
    cJSON *nodes = NULL;
    cJSON *node = NULL;

    int node_cnt = 0, i = 0;
    char sn_tmp[SN_MAX_LEN] = {0};

    char *result_str = NULL;

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

    root = cJSON_Parse(data);
    if (!root)
    {
        dbg_syslog(LOG_ERR, "parse cfg file %s error", NODES_CFG_PATH);
        goto out;
    }
    nodes = cJSON_GetObjectItem(root, "nodes_cfg");
    if (!nodes)
    {
        dbg_syslog(LOG_ERR, "get nodes_cfg failed");
        goto out;
    }

    node_cnt = cJSON_GetArraySize(nodes);

    for (i = 0; i < node_cnt; i++)
    {
        node = cJSON_GetArrayItem(nodes, i);
        if (node)
        {
            GET_JSON_VALUE_STRING(node, "sn", sn_tmp);
            if (strcmp(sn_tmp, sn) == 0)
            {
                result_str = cJSON_PrintUnformatted(node);
                break;
            }
        }
    }

out:
    cJSON_Delete(root);
    if (data != NULL)
    {
        free(data);
    }

    return result_str;
}

static int _do_script(pp_script_thread_t *script_thread, struct list_head *recv_list)
{
    pp_script_msg_node_t *node = NULL;
    pp_script_msg_node_t *tmp = NULL;
    pp_var_t *var = script_thread->var;
    template_cfg_t *ptemplate = script_thread->ptemplate;

    if (list_empty(recv_list))
    {
        return 0;
    }
    list_for_each_entry_safe(node, tmp, recv_list, list)
    {
        switch (node->type)
        {
        case SCRIPT_DECODE:
            pp_parse_data_by_template(var, ptemplate, node->node, node->data.data_decode.rtData, node->data.data_decode.flag, node->data.data_decode.dtu);
            break;

        case SCRIPT_ENCODE:
            pp_regulate_signal_processor(var, ptemplate, node->data.data_encode.data, node->node, node->data.data_encode.rglt_data);
        default:
            break;
        }
        list_del(&node->list);
        __free_script_msg_node(node);
    }
    return 0;
}

static int handle_script_req(pp_script_thread_t *script_thread)
{
    struct list_head tmp_recv_list = LIST_HEAD_INIT(tmp_recv_list);

    pthread_mutex_lock(&script_thread->script_lock);
    while (script_thread->received == 0)
    {
        int ret;

        ret = lnxall_condvar_timedwait(&script_thread->script_cond, &script_thread->script_lock, 5000);
        if (ret && ret != ETIMEDOUT) {
            dbg_syslog(LOG_ERR, "Error, cond_timedwait(...) has failed: %d\n", ret);
        }
    }

    if (script_thread->received)
    {
        // real received packets
        if (!list_empty(&script_thread->data_list))
        {
            list_move_tail_list(&script_thread->data_list, &tmp_recv_list);
        }
        script_thread->received = 0;
        pthread_mutex_unlock(&script_thread->script_lock);

        _do_script(script_thread, &tmp_recv_list);
    }
    else
    {
        pthread_mutex_unlock(&script_thread->script_lock);
    }

    return 0;
}

static void *script_run_loop(void *param)
{
    pp_script_thread_t *script_thread = (pp_script_thread_t *)param;
    pre_load_script(script_thread->ptemplate);

    while (1)
    {
        handle_script_req(script_thread);
    }

    return NULL;
}

int pp_send_data_to_script(pp_var_t *var, template_cfg_t *ptemplate, script_data_t *script_data, node_cfg_t *node, script_type_e type)
{
    pp_script_msg_node_t *script_node = NULL;
    pp_script_thread_t *script_thread = NULL;
    static int full_time = 0;

    script_thread = ptemplate->script_thread;
    if (script_thread == NULL)
    {
        dbg_syslog(LOG_ERR, "script thread is NULL");
        return -1;
    }

    if (script_thread->received > 100)
    {
        dbg_syslog(LOG_ERR, "queue is full");
        full_time++;
        if (full_time > 100)
        {
            system("/etc/init.d/protocol_parser restart&");
        }
        return -1;
    }

    full_time = 0;

    script_node = calloc(1, sizeof(pp_script_msg_node_t));
    if (script_node == NULL)
    {
        dbg_syslog(LOG_ERR, "malloc failed");
        return -1;
    }

    script_node->node = node;
    script_node->type = type;
    memcpy(&script_node->data, script_data, sizeof(script_data_t));

    pthread_mutex_lock(&script_thread->script_lock);
    list_add_tail(&script_node->list, &script_thread->data_list);
    script_thread->received++;
    pthread_mutex_unlock(&script_thread->script_lock);
    pthread_cond_signal(&script_thread->script_cond);
    return 0;
}

static char *get_nodes_by_template_id(char *template_id)
{
    char *data = NULL;
    cJSON *root = NULL;
    cJSON *nodes = NULL;
    cJSON *node = NULL;
    cJSON *result = NULL;
    int node_cnt = 0, i = 0;
    char template_id_tmp[16] = {0};

    char *result_str = NULL;

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

    root = cJSON_Parse(data);
    if (!root)
    {
        dbg_syslog(LOG_ERR, "parse cfg file %s error", NODES_CFG_PATH);
        goto out;
    }
    nodes = cJSON_GetObjectItem(root, "nodes_cfg");
    if (!nodes)
    {
        dbg_syslog(LOG_ERR, "get nodes_cfg failed");
        goto out;
    }

    node_cnt = cJSON_GetArraySize(nodes);

    result = cJSON_CreateArray();

    for (i = 0; i < node_cnt; i++)
    {
        node = cJSON_GetArrayItem(nodes, i);
        if (node)
        {
            GET_JSON_VALUE_STRING(node, "template_id", template_id_tmp);
            if (strcmp(template_id_tmp, template_id) == 0)
            {
                cJSON *new = cJSON_Duplicate(node, 1);
                cJSON_AddItemToArray(result, new);
            }
        }
    }
    result_str = cJSON_PrintUnformatted(result);

out:
    cJSON_Delete(root);
    if (data != NULL)
    {
        free(data);
    }
    cJSON_Delete(result);

    return result_str;
}

static void *call_scripte_entry_one_node(void *param)
{
    node_cfg_t *node = (node_cfg_t *)param;
    template_cfg_t *template = node->template;
    char *file_name = template->parser_script;
    char file[256] = {0};

    sprintf(file, "%s%s", PARSE_SCRIPT_DIR, file_name);

    if (strstr(file_name, LUA_EXTEND))
    {

        pid_t pid;
        int status;
restart:
        pid = fork();
        if (pid < 0)
        {
            perror("script fork failed");
            exit(1);
        }
        else if (pid > 0)
        { // This is in parent progress
            pid_t w = 0;
            do
            {
                w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                if (w == -1)
                {
                    perror("waitpid");
                    exit(EXIT_FAILURE);
                }

                if (WIFEXITED(status))
                {
                    printf("exited, status=%d\n", WEXITSTATUS(status));
                }
                else if (WIFSIGNALED(status))
                {
                    printf("killed by signal %d\n", WTERMSIG(status));
                }
                else if (WIFSTOPPED(status))
                {
                    printf("stopped by signal %d\n", WSTOPSIG(status));
                }
                else if (WIFCONTINUED(status))
                {
                    printf("continued\n");
                }
            } while (!WIFEXITED(status) && !WIFSIGNALED(status));
            dbg_syslog(LOG_WARNING, "process child exit :%s wait pid %d !!!", file_name, w);
            goto restart;
        }
        else // This is in child progress
        {
            prctl(PR_SET_PDEATHSIG, SIGHUP);
            char *data_str = get_node_by_sn(node->sn);
            if (execlp("lua", "lua", file, data_str, NULL) < 0)
                dbg_syslog(LOG_WARNING, "script :%s exec error !!!", file_name);
        }
    }
    return NULL;
}

static void *call_script_entry(void *param)
{
    pp_script_thread_t *script_thread = (pp_script_thread_t *)param;
    pp_var_t *var = script_thread->var;
    template_cfg_t *template = script_thread->ptemplate;
    char *file_name = template->parser_script;
    char file[256] = {0};
    if (file_name == NULL)
    {
        return NULL;
    }
    sprintf(file, "%s%s", PARSE_SCRIPT_DIR, file_name);
    char *data_str = get_nodes_by_template_id(template->template_id);
    if (strstr(file_name, LUA_EXTEND))
    {
    restart:
        sprintf(file, "%s%s", PARSE_SCRIPT_DIR, file_name);
        pid_t pid;
        int status;
        pid = fork();
        if (pid < 0)
        {
            perror("script fork failed");
            exit(1);
        }
        else if (pid > 0)
        { // This is in parent progress
            pid_t w = 0;
            do
            {
                w = waitpid(pid, &status, WUNTRACED | WCONTINUED);
                if (w == -1)
                {
                    perror("waitpid");
                    exit(EXIT_FAILURE);
                }

                if (WIFEXITED(status))
                {
                    printf("exited, status=%d\n", WEXITSTATUS(status));
                }
                else if (WIFSIGNALED(status))
                {
                    printf("killed by signal %d\n", WTERMSIG(status));
                }
                else if (WIFSTOPPED(status))
                {
                    printf("stopped by signal %d\n", WSTOPSIG(status));
                }
                else if (WIFCONTINUED(status))
                {
                    printf("continued\n");
                }
            } while (!WIFEXITED(status) && !WIFSIGNALED(status));
            dbg_syslog(LOG_WARNING, "process child exit :%s wait pid %d !!!", file_name, w);
            sleep(10);
            goto restart;
        }
        else // This is in child progress
        {
            prctl(PR_SET_PDEATHSIG, SIGHUP);
            if (execlp("lua", "lua", file, data_str, var->sn_str, NULL) < 0)
            {
                dbg_syslog(LOG_WARNING, "script :%s exec error !!!", file_name);
            }
        }
    }
    else if (strstr(file_name, PYTHON_EXTEND))
    {
    }
    else if (strstr(file_name, JAVASCRIPT_EXTEND))
    {
    }
    else
    {
        dbg_syslog(LOG_WARNING, "script file_name:%s error !!!", file_name);
    }
    free(data_str);
    free(script_thread);
    template->script_thread = NULL;
    return NULL;
}

int pp_start_script_thread(pp_var_t *var, template_cfg_t *template)
{
    pthread_t thread_script;
    pp_script_thread_t *script_thread = NULL;

    script_thread = calloc(1, sizeof(pp_script_thread_t));
    if (script_thread == NULL)
    {
        dbg_syslog(LOG_ERR, "Malloc failed");
        return -1;
    }

    INIT_LIST_HEAD(&script_thread->data_list);
    pthread_mutex_init(&script_thread->script_lock, NULL);
    lnxall_condvar_init(&script_thread->script_cond);
    script_thread->ptemplate = template;
    script_thread->var = var;
    template->script_thread = script_thread;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setguardsize(&attr, 1024);
    if (template->use_parser_url == SCRIPT_PARSER)
    {
        pthread_create(&thread_script, &attr, script_run_loop, (void *)script_thread);
    }
    else if (template->use_parser_url == SCRIPT_DEAMON)
    {
        if (template->script_scope == SCRIPT_TEMPLATE)
        {
            pthread_create(&thread_script, &attr, call_script_entry, (void *)script_thread);
            var->deamon_script_cnt++;
        }
        else if (template->script_scope == SCRIPT_NODE)
        {
            int i = 0;

            for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
            {
                if (strcmp(var->nodes_cfg_table->node[i].template_id, template->template_id) == 0)
                {
                    var->nodes_cfg_table->node[i].template = template;
                    pthread_create(&thread_script, &attr, call_scripte_entry_one_node, (void *)&var->nodes_cfg_table->node[i]);
                    var->deamon_script_cnt++;
                }
            }
        }
    }
    return 0;
}
