#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 "entrance_guard_common.h"

static int get_open_door_sql_id_item(long id, char *item, char **reply)
{
    int ret;
    char exec[256];

    sprintf(exec, "%s %ld %s 2>/dev/null | tail -3 | head -1", ENTRANCE_GUARD_INQUIRE_ID, id, item);
    ret = system_reply(exec, reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "system_reply failed\n");
    }

    return ret;
}

static void get_open_door_info(open_door_node_t *open_door_node, long id)
{
    int ret;
    char *reply = NULL;

    open_door_node->ID = id;
    ret = get_open_door_sql_id_item(id, "TokenId", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get TokenId failed\n");
    }
    else
    {
        strncpy(open_door_node->TokenId, reply, sizeof(open_door_node->TokenId));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "TcmId", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get TcmId failed\n");
    }
    else
    {
        strncpy(open_door_node->TcmId, reply, sizeof(open_door_node->TcmId));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "StaffId", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get StaffId failed\n");
    }
    else
    {
        strncpy(open_door_node->StaffId, reply, sizeof(open_door_node->StaffId));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "StaffNo", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get StaffNo failed\n");
    }
    else
    {
        strncpy(open_door_node->StaffNo, reply, sizeof(open_door_node->StaffNo));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "StaffName", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get StaffName failed\n");
    }
    else
    {
        strncpy(open_door_node->StaffName, reply, sizeof(open_door_node->StaffName));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "DevNo", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get DevNo failed\n");
    }
    else
    {
        open_door_node->DevNo = atoll(reply);
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "DoorNo", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get DoorNo failed\n");
    }
    else
    {
        open_door_node->DoorNo = atoll(reply);
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "DevName", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get DevName failed\n");
    }
    else
    {
        strncpy(open_door_node->DevName, reply, sizeof(open_door_node->DevName));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "OpenDate", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get OpenDate failed\n");
    }
    else
    {
        strncpy(open_door_node->OpenDate, reply, sizeof(open_door_node->OpenDate));
        free(reply);
    }

    ret = get_open_door_sql_id_item(id, "OperateDate", &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "get OperateDate failed\n");
    }
    else
    {
        strncpy(open_door_node->OperateDate, reply, sizeof(open_door_node->OperateDate));
        free(reply);
    }
}

static int entrance_guard_inquire(entrance_guard_var_t *var)
{
    int i = 0;
    int count = 0;
    open_door_node_t *open_door_node = NULL;

    TIMER_CONFIRM(var->inquire_timer);

    count = get_new_entrance_guard_data_count(var);

    if (count > 0)
    {
        for (i = 1; i <= count; i++)
        {
            open_door_node = calloc(1, sizeof(open_door_node_t));
            if (open_door_node == NULL)
            {
                dy_syslog(LOG_ERR, "calloc failed\n");
                continue;
            }
            get_open_door_info(open_door_node, var->last_id + i);
            pthread_mutex_lock(&var->open_door_lock);
            list_add_tail(&open_door_node->list, &var->open_door_list);
            var->received++;
            if (i == count)
            {
                var->last_id += count;
            }
            pthread_mutex_unlock(&var->open_door_lock);
            pthread_cond_signal(&var->open_door_cond);
        }
    }

    return 0;
}

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

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

        ret = select(maxfd + 1, &rset, 0, 0, &timeout);
        if (ret < 0)
        {
            dy_syslog(LOG_INFO, "errno %d\n", errno);

            if (errno == EINTR)
            {
                continue;
            }
            else
            {
                break;
            }
        }
        else if (ret > 0)
        {
            if (var->inquire_timer > 0 && FD_ISSET(var->inquire_timer, &rset))
            {
                FD_CLR(var->inquire_timer, &rset);
                entrance_guard_inquire(var);
            }
        }
    }

    return NULL;
}

static int report_entrance_guard_data(entrance_guard_var_t *var, open_door_node_t *node)
{
    pp_real_time_data_t real_data = {0};
    cJSON *root = cJSON_CreateObject();
    char *data_tmp = NULL;
    int i;

    cJSON_AddNumberToObject(root, "ID", node->ID);
    cJSON_AddStringToObject(root, "TokenId", node->TokenId);
    cJSON_AddStringToObject(root, "TcmId", node->TcmId);
    cJSON_AddStringToObject(root, "StaffId", node->StaffId);
    cJSON_AddStringToObject(root, "StaffNo", node->StaffNo);
    cJSON_AddStringToObject(root, "StaffName", node->StaffName);
    cJSON_AddNumberToObject(root, "DevNo", node->DevNo);
    cJSON_AddNumberToObject(root, "DoorNo", node->DoorNo);
    cJSON_AddStringToObject(root, "DevName", node->DevName);
    cJSON_AddStringToObject(root, "OpenDate", node->OpenDate);
    cJSON_AddStringToObject(root, "OperateDate", node->OperateDate);
    data_tmp = cJSON_Print(root);

    real_data.len = strlen(data_tmp);
    real_data.data = malloc(real_data.len);
    memcpy(real_data.data, data_tmp, real_data.len);
    for (i = 0; i < var->nodes_cfg_table->node_cnt; i++)
    {
        if (strcmp(var->nodes_cfg_table->node[i].app_key, ENTRANCE_GUARD_APP_KEY) == 0)
        {
            strcpy(real_data.sn, var->nodes_cfg_table->node[i].sn);
            break;
        }
    }
    send_to_proto_parser(var->session, &real_data);

    free(real_data.data);
    free(data_tmp);
    cJSON_Delete(root);

    return 0;
}

static int do_entrance_guard_data(entrance_guard_var_t *var, struct list_head *recv_list)
{
    open_door_node_t *node = NULL;
    open_door_node_t *tmp = NULL;

    if (list_empty(recv_list))
    {
        return 0;
    }
    list_for_each_entry_safe(node, tmp, recv_list, list)
    {
        report_entrance_guard_data(var, node);
        list_del(&node->list);
        free(node);
    }

    return 0;
}

static int handle_entrance_guard_data(entrance_guard_var_t *var)
{
    struct list_head tmp_recv_list = LIST_HEAD_INIT(tmp_recv_list);

    pthread_mutex_lock(&var->open_door_lock);
    while (var->received == 0)
    {
        int ret;

        ret = lnxall_condvar_timedwait(&var->open_door_cond, &var->open_door_lock, 5000);
    }

    if (var->received)
    {
        if (!list_empty(&var->open_door_list))
        {
            list_move_tail_list(&var->open_door_list, &tmp_recv_list);
        }
        var->received = 0;
        pthread_mutex_unlock(&var->open_door_lock);

        do_entrance_guard_data(var, &tmp_recv_list);
    }
    else
    {
        pthread_mutex_unlock(&var->open_door_lock);
    }

    return 0;
}

static void *report_thread_loop(void *param)
{
    entrance_guard_var_t *var= (entrance_guard_var_t *)param;

    while (1)
    {
        handle_entrance_guard_data(var);
    }

    return NULL;
}

int get_new_entrance_guard_data_count(entrance_guard_var_t *var)
{
    int ret;
    char exec[256];
    char *reply = NULL;
    int count = 0;
    
    sprintf(exec, "%s %ld 2>/dev/null | tail -3 | head -1", ENTRANCE_GUARD_COUNT_ID, var->last_id);
    ret = system_reply(exec, &reply);
    if (ret < 0)
    {
        dy_syslog(LOG_ERR, "system_reply failed\n");
        return -1;
    }
    count = atoi(reply);
    free(reply);

    return count;
}

int entrance_guard_start_thread(entrance_guard_var_t *var)
{
    pthread_t thread_inquire;
    pthread_t thread_report;

    pthread_create(&thread_inquire, NULL, inquire_thread_loop, (void *)var);
    pthread_create(&thread_report, NULL, report_thread_loop, (void *)var);

    return 0;
}
