#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>

#include "dy_modbus_sniffer_session.h"
#include "lnxall_list.h"
#include "dy_pp.h"
#include "dy_common.h"

typedef struct find_data_info_s
{
    struct list_head list;
    int start;
    int end;
} find_data_info_t;

static int data_call_back(modbus_sniffer_session_t *session)
{
    cmd_data_node_t *node = NULL;
    cmd_data_node_t *tmp = NULL;
    int cnt = 0;

    list_for_each_entry_safe(node, tmp, &session->query_list, list)
    {
        dy_syslog_hex(LOG_DEBUG, node->sniffer_data.data, node->sniffer_data.len, "CALLBACK cnt %d len %d", ++cnt, node->sniffer_data.len);
        if (session->handle_recv_msg)
            session->handle_recv_msg(session, node);
        list_del(&node->list);
        free(node->sniffer_data.data);
        free(node);

    }
    return 0;
}

static int add_query_list(modbus_sniffer_session_t *session, find_data_info_t *data_info, cmd_data_node_t *last_node)
{
    cmd_data_node_t *cmd_data = calloc(1, sizeof(cmd_data_node_t));
    if (cmd_data == NULL)
    {
        dy_syslog(LOG_ERR, "malloc failed");
        return -1;
    }
    cmd_data->sniffer_data.userParams = last_node->sniffer_data.userParams;
    cmd_data->sniffer_data.len = data_info->end - data_info->start;
    cmd_data->sniffer_data.data = calloc(1, cmd_data->sniffer_data.len);

    memcpy(cmd_data->sniffer_data.data, &session->store_info.buf[data_info->start], cmd_data->sniffer_data.len);

    // 将数据加入LIST
    list_add_tail(&cmd_data->list, &session->query_list);
    dy_syslog_hex(LOG_DEBUG, cmd_data->sniffer_data.data, cmd_data->sniffer_data.len, "Find data len %d ", cmd_data->sniffer_data.len);

    return 0;
}

static int _do_rcv(modbus_sniffer_session_t *session)
{
    raw_data_node_t *node = NULL;
    raw_data_node_t *tmp = NULL;

    list_for_each_entry_safe(node, tmp, &session->data_list, list)
    {
        dy_syslog_hex(LOG_DEBUG, node->buf, node->len, "%s buf_len %d data_len %d store_flag %d", __FUNCTION__, session->store_info.len, node->len, session->store_info.store_flag);
        if (session->store_info.len - session->store_info.store_flag > node->len)
        {
            memcpy(session->store_info.buf + session->store_info.store_flag, node->buf, node->len);
            session->store_info.store_flag += node->len;
            session->op_code |= SNIFFER_REVIEW_DATA_MASK;
            list_del(&node->list);
            free(node);
        }
        else
            break;
    }
    return 0;
}

static int find_cmd_by_data(modbus_sniffer_session_t *session)
{
    int i;
    int pair_start = 0 /*, pair_end = 0 */ ;
    int cmd_cnt = 0, cmd_null_cnt = 0, null_index = 0;
    find_data_info_t data_info[64];
    cmd_data_node_t *last_node = NULL;

    memset(data_info, 0, 64 * sizeof(find_data_info_t));
    dy_syslog_hex(LOG_DEBUG, session->store_info.buf, session->store_info.store_flag , "buf_len %d store_flag %d", session->store_info.len, session->store_info.store_flag);

    //遍历数据缓存，根据命令找数据区
    for (i = 0; i < session->store_info.store_flag; i++)
    {
        cmd_data_node_t *node = NULL;
        cmd_data_node_t *tmp = NULL;

        list_for_each_entry_safe(node, tmp, &session->cmd_list, list)
        {
            //根据数据找命令, 找到相应命令，标记。直到数据找完
            if (memcmp(&session->store_info.buf[i], node->sniffer_data.data, node->sniffer_data.len) == 0)
            {
                //dy_syslog_hex(LOG_DEBUG, session->store_info.buf, session->store_info.store_flag,"--44 11 all DATA-- %s %d store_flag %d i %d", __FUNCTION__,__LINE__,session->store_info.store_flag,i);
                //dy_syslog_hex(LOG_DEBUG, node->sniffer_data.data, node->sniffer_data.len ,"--44 11 FIND CMD-- %s %d find data len %d pair_start %d cmd_cnt %d", __FUNCTION__,__LINE__,node->sniffer_data.len,pair_start,cmd_cnt);
                //找第1个命令的开始
                if (pair_start == 0)
                {
                    i = i + node->sniffer_data.len;
                    data_info[cmd_cnt].start =  i;
                    last_node = node;
                    i -= 1;
                    pair_start = 1;
                    dy_syslog(LOG_DEBUG, "First cmd index %d", i);
                }
                //找下一个命令的开始
                else if (i - data_info[cmd_cnt].start > 1)
                {
                    //匹配到一个完整命令
                    data_info[cmd_cnt].end =  i;
                    //加入数据列表
                    add_query_list(session, &data_info[cmd_cnt], last_node);
                    cmd_cnt ++;
                    last_node = node;

                    //标记下一命令的开始
                    i = i + node->sniffer_data.len;
                    data_info[cmd_cnt].start =  i;
                    i -= 1;
                }
                //找到无效命令，跳过。即无数据段
                else
                {
                    null_index = i;
                    i = i + node->sniffer_data.len;
                    data_info[cmd_cnt].start =  i;
                    i -= 1;
                    cmd_null_cnt++;
                    last_node = node;
                    dy_syslog(LOG_DEBUG, "Find null cmd");
                }
                break;
            }
        }
    }

    //MODBUS数据优化
    if (session->protocol == GW_PROTC_MODBUS && pair_start == 1 && session->store_info.store_flag - data_info[cmd_cnt].start > 3)
    {
        unsigned short crc_calculated = modbus_data_check_crc((unsigned char *) &session->store_info.buf[data_info[cmd_cnt].start],
            session->store_info.store_flag - data_info[cmd_cnt].start);
        if (crc_calculated == 0)
        {
            //匹配到一个完整命令
            data_info[cmd_cnt].end =  session->store_info.store_flag;
            //加入数据列表
            add_query_list(session, &data_info[cmd_cnt], last_node);
            cmd_cnt ++;

            //标记下一命令的开始
            data_info[cmd_cnt].start =  session->store_info.store_flag;
        }
    }
    //非MODBUS,重置最后数据位置
    else if (cmd_cnt >= 1)
    {
        data_info[cmd_cnt].start = data_info[cmd_cnt].start - last_node->sniffer_data.len;
    }

    //缓存维护
    if (cmd_cnt >= 1)
    {
        int lstart = data_info[cmd_cnt].start;
        if (last_node && lstart >= last_node->sniffer_data.len)
            lstart -= last_node->sniffer_data.len;
        memmove(&session->store_info.buf[0], &session->store_info.buf[lstart], session->store_info.len - lstart);
        session->store_info.store_flag = session->store_info.store_flag - lstart;
        dy_syslog(LOG_DEBUG, "1 reset buf cmd_cnt %d cmd_null_cnt %d start %d store_flag %d", cmd_cnt, cmd_null_cnt, lstart, session->store_info.store_flag);
    }
    //无效命令缓存处理
    else if (data_info[cmd_cnt].start > 0 && cmd_null_cnt > 1)
    {
        memmove(&session->store_info.buf[0], &session->store_info.buf[null_index], session->store_info.store_flag - null_index);
        session->store_info.store_flag -= null_index;
        dy_syslog(LOG_DEBUG, "2 reset buf cmd_cnt %d cmd_null_cnt %d start %d store_flag %d", cmd_cnt, cmd_null_cnt, data_info[cmd_cnt].start, session->store_info.store_flag);
    }

    //通知用户
    data_call_back(session);
    return 0;
}

static int _do_review_data(modbus_sniffer_session_t *session)
{
    cmd_data_node_t *node = NULL;
    cmd_data_node_t *tmp = NULL;

    if (list_empty(&session->cmd_list))
    {
        return 0;
    }
    list_for_each_entry_safe(node, tmp, &session->cmd_list, list)
    {
        //根据数据找命令, 找到相应命令，标记。直到数据找完
        find_cmd_by_data(session);
        //截取命令间数据，回调用户注册函数

        //更新数据缓存和标记
    }
    return 0;
}

static int modbus_sniffer_to_loop(modbus_sniffer_session_t *session)
{
    dy_syslog(LOG_DEBUG, "session->op_code %d", session->op_code);
    pthread_mutex_lock(&session->sniffer_lock);
    while (session->op_code == 0)
    {
        lnxall_condvar_timedwait(&session->sniffer_cond, &session->sniffer_lock, 5000);
    }

    dy_syslog(LOG_DEBUG, "session->op_code %d", session->op_code);
    //缓存接收到的数据
    if (session->op_code & SNIFFER_ADD_DATA_MASK)
    {
        if (!list_empty(&session->data_list))
        {
            _do_rcv(session);
        }
    }

    //缓存接收到的数据
    if (session->op_code & SNIFFER_REVIEW_DATA_MASK)
    {
        dy_syslog(LOG_DEBUG, "session->op_code %d", session->op_code);
        _do_review_data(session);
    }

    session->op_code = 0;
    pthread_mutex_unlock(&session->sniffer_lock);

    return 0;

}


void *modbus_sniffer_loop(void *param)
{
    modbus_sniffer_session_t *session = (modbus_sniffer_session_t *)param;
    while (1)
    {
        modbus_sniffer_to_loop(session);
    }
}
