#include <errno.h>
#include <unistd.h>
#include <fcntl.h>

#include "stat.h"
#include "fieldstat/fieldstat_easy.h"
#include "fieldstat/fieldstat_exporter.h"

#define IS_FREE 0
#define IS_BUSY 0xf

struct stat_id
{
    // device packet
    int dev_rx_pkts;
    int dev_rx_bytes;

    int dev_tx_pkts;
    int dev_tx_bytes;

    // keep-alive packet
    int keep_alive_pkts;
    int keep_alive_bytes;

    // raw packet
    int raw_rx_pkts;
    int raw_rx_bytes;

    int raw_tx_pkts;
    int raw_tx_bytes;

    // drop packet
    int drop_pkts;
    int drop_bytes;

    // inject packet
    int inject_pkts;
    int inject_bytes;

    // ctrl packet
    int ctrl_rx_pkts;
    int ctrl_rx_bytes;

    int ctrl_tx_pkts;
    int ctrl_tx_bytes;

    // ip reassembly
    int ip4_flow_find;
    int ip4_flow_add;
    int ip4_flow_del;
    int ip4_flow_timeout;

    int ip4_flow_fail_no_space;
    int ip4_flow_fail_overlap;
    int ip4_flow_fail_many_frag;
    int ip4_flow_fail_invalid_length;
    int ip4_flow_bypass_dup_fist_frag;
    int ip4_flow_bypass_dup_last_frag;

    int ip6_flow_find;
    int ip6_flow_add;
    int ip6_flow_del;
    int ip6_flow_timeout;

    int ip6_flow_fail_no_space;
    int ip6_flow_fail_overlap;
    int ip6_flow_fail_many_frag;
    int ip6_flow_fail_invalid_length;
    int ip6_flow_bypass_dup_fist_frag;
    int ip6_flow_bypass_dup_last_frag;

    // TCP session
    int total_nr_tcp_sess_used;
    int curr_nr_tcp_sess_used;
    int curr_nr_tcp_sess_opening;
    int curr_nr_tcp_sess_active;
    int curr_nr_tcp_sess_closing;
    int curr_nr_tcp_sess_discard;
    int curr_nr_tcp_sess_closed;

    // UDP session
    int total_nr_udp_sess_used;
    int curr_nr_udp_sess_used;
    int curr_nr_udp_sess_opening;
    int curr_nr_udp_sess_active;
    int curr_nr_udp_sess_closing;
    int curr_nr_udp_sess_discard;
    int curr_nr_udp_sess_closed;

    // Evicted session
    int nr_tcp_sess_evicted;
    int nr_udp_sess_evicted;

    // Packet
    int nr_udp_pkts_nospace_bypass;
    int nr_tcp_pkts_nospace_bypass;
    int nr_tcp_pkts_nosess_bypass;
    int nr_tcp_pkts_duped_bypass;
    int nr_udp_pkts_duped_bypass;
    int nr_udp_pkts_evctd_bypass;

    // TCP segments
    int nr_tcp_seg_received;
    int nr_tcp_seg_expired;
    int nr_tcp_seg_retransmit;
    int nr_tcp_seg_overlap;
    int nr_tcp_seg_no_space;
    int nr_tcp_seg_inorder;
    int nr_tcp_seg_reorded;
    int nr_tcp_seg_buffered;
    int nr_tcp_seg_released;
};

struct stellar_stat
{
    char output_file[2048];

    // thread stat
    uint16_t nr_thread;
    int flag[MAX_THREAD_NUM]; // IS_FREE or IS_BUSY
    struct io_stat thr_io_stat[MAX_THREAD_NUM];
    struct ip_reassembly_stat thr_ip_stat[MAX_THREAD_NUM];
    struct session_manager_stat thr_sess_stat[MAX_THREAD_NUM];

    // global stat
    struct io_stat io_stat;
    struct ip_reassembly_stat ip_stat;
    struct session_manager_stat sess_stat;

    struct stat_id ids;
    struct fieldstat_easy *fs;
};

// python3 -m pip install prettytable
// python3 -m pip install jinja2
// /opt/MESA/bin/fieldstat_exporter.py local -j log/stellar_fs4.json -e -l --clear-screen
struct stellar_stat *stellar_stat_new(uint16_t nr_thread)
{
    char cwd[1024] = {0};
    struct stellar_stat *stat = (struct stellar_stat *)calloc(1, sizeof(struct stellar_stat));
    if (stat == NULL)
    {
        return NULL;
    }

    if (getcwd(cwd, sizeof(cwd)) == NULL)
    {
        STAT_LOG_ERROR("failed to get current working directory: %s", strerror(errno));
        goto error_out;
    }
    snprintf(stat->output_file, sizeof(stat->output_file), "%s/log/stellar_fs4.json", cwd);

    stat->fs = fieldstat_easy_new(1, "stellar", NULL, 0);
    if (stat->fs == NULL)
    {
        STAT_LOG_ERROR("failed to create fieldstat_easy");
        goto error_out;
    }

    stat->nr_thread = nr_thread;
    for (int i = 0; i < MAX_THREAD_NUM; i++)
    {
        stat->flag[i] = IS_FREE;
    }

    // device packet
    stat->ids.dev_rx_pkts = fieldstat_easy_register_counter(stat->fs, "dev_rx_pkts");
    stat->ids.dev_rx_bytes = fieldstat_easy_register_counter(stat->fs, "dev_rx_bytes");
    stat->ids.dev_tx_pkts = fieldstat_easy_register_counter(stat->fs, "dev_tx_pkts");
    stat->ids.dev_tx_bytes = fieldstat_easy_register_counter(stat->fs, "dev_tx_bytes");
    // keep-alive packet
    stat->ids.keep_alive_pkts = fieldstat_easy_register_counter(stat->fs, "keep_alive_pkts");
    stat->ids.keep_alive_bytes = fieldstat_easy_register_counter(stat->fs, "keep_alive_bytes");
    // raw packet
    stat->ids.raw_rx_pkts = fieldstat_easy_register_counter(stat->fs, "raw_rx_pkts");
    stat->ids.raw_rx_bytes = fieldstat_easy_register_counter(stat->fs, "raw_rx_bytes");
    stat->ids.raw_tx_pkts = fieldstat_easy_register_counter(stat->fs, "raw_tx_pkts");
    stat->ids.raw_tx_bytes = fieldstat_easy_register_counter(stat->fs, "raw_tx_bytes");
    // drop packet
    stat->ids.drop_pkts = fieldstat_easy_register_counter(stat->fs, "drop_pkts");
    stat->ids.drop_bytes = fieldstat_easy_register_counter(stat->fs, "drop_bytes");
    // inject packet
    stat->ids.inject_pkts = fieldstat_easy_register_counter(stat->fs, "inject_pkts");
    stat->ids.inject_bytes = fieldstat_easy_register_counter(stat->fs, "inject_bytes");
    // ctrl packet
    stat->ids.ctrl_rx_pkts = fieldstat_easy_register_counter(stat->fs, "ctrl_rx_pkts");
    stat->ids.ctrl_rx_bytes = fieldstat_easy_register_counter(stat->fs, "ctrl_rx_bytes");
    stat->ids.ctrl_tx_pkts = fieldstat_easy_register_counter(stat->fs, "ctrl_tx_pkts");
    stat->ids.ctrl_tx_bytes = fieldstat_easy_register_counter(stat->fs, "ctrl_tx_bytes");
    // ip reassembly
    stat->ids.ip4_flow_find = fieldstat_easy_register_counter(stat->fs, "ip4_flow_find");
    stat->ids.ip4_flow_add = fieldstat_easy_register_counter(stat->fs, "ip4_flow_add");
    stat->ids.ip4_flow_del = fieldstat_easy_register_counter(stat->fs, "ip4_flow_del");
    stat->ids.ip4_flow_timeout = fieldstat_easy_register_counter(stat->fs, "ip4_flow_timeout");
    stat->ids.ip4_flow_fail_no_space = fieldstat_easy_register_counter(stat->fs, "ip4_flow_fail_no_space");
    stat->ids.ip4_flow_fail_overlap = fieldstat_easy_register_counter(stat->fs, "ip4_flow_fail_overlap");
    stat->ids.ip4_flow_fail_many_frag = fieldstat_easy_register_counter(stat->fs, "ip4_flow_fail_many_frag");
    stat->ids.ip4_flow_fail_invalid_length = fieldstat_easy_register_counter(stat->fs, "ip4_flow_fail_invalid_length");
    stat->ids.ip4_flow_bypass_dup_fist_frag = fieldstat_easy_register_counter(stat->fs, "ip4_flow_bypass_dup_fist_frag");
    stat->ids.ip4_flow_bypass_dup_last_frag = fieldstat_easy_register_counter(stat->fs, "ip4_flow_bypass_dup_last_frag");
    stat->ids.ip6_flow_find = fieldstat_easy_register_counter(stat->fs, "ip6_flow_find");
    stat->ids.ip6_flow_add = fieldstat_easy_register_counter(stat->fs, "ip6_flow_add");
    stat->ids.ip6_flow_del = fieldstat_easy_register_counter(stat->fs, "ip6_flow_del");
    stat->ids.ip6_flow_timeout = fieldstat_easy_register_counter(stat->fs, "ip6_flow_timeout");
    stat->ids.ip6_flow_fail_no_space = fieldstat_easy_register_counter(stat->fs, "ip6_flow_fail_no_space");
    stat->ids.ip6_flow_fail_overlap = fieldstat_easy_register_counter(stat->fs, "ip6_flow_fail_overlap");
    stat->ids.ip6_flow_fail_many_frag = fieldstat_easy_register_counter(stat->fs, "ip6_flow_fail_many_frag");
    stat->ids.ip6_flow_fail_invalid_length = fieldstat_easy_register_counter(stat->fs, "ip6_flow_fail_invalid_length");
    stat->ids.ip6_flow_bypass_dup_fist_frag = fieldstat_easy_register_counter(stat->fs, "ip6_flow_bypass_dup_fist_frag");
    stat->ids.ip6_flow_bypass_dup_last_frag = fieldstat_easy_register_counter(stat->fs, "ip6_flow_bypass_dup_last_frag");
    // TCP session
    stat->ids.total_nr_tcp_sess_used = fieldstat_easy_register_counter(stat->fs, "total_nr_tcp_sess_used");
    stat->ids.curr_nr_tcp_sess_used = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_used");
    stat->ids.curr_nr_tcp_sess_opening = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_opening");
    stat->ids.curr_nr_tcp_sess_active = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_active");
    stat->ids.curr_nr_tcp_sess_closing = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_closing");
    stat->ids.curr_nr_tcp_sess_discard = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_discard");
    stat->ids.curr_nr_tcp_sess_closed = fieldstat_easy_register_counter(stat->fs, "curr_tcp_sess_closed");
    // UDP session
    stat->ids.total_nr_udp_sess_used = fieldstat_easy_register_counter(stat->fs, "total_nr_udp_sess_used");
    stat->ids.curr_nr_udp_sess_used = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_used");
    stat->ids.curr_nr_udp_sess_opening = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_opening");
    stat->ids.curr_nr_udp_sess_active = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_active");
    stat->ids.curr_nr_udp_sess_closing = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_closing");
    stat->ids.curr_nr_udp_sess_discard = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_discard");
    stat->ids.curr_nr_udp_sess_closed = fieldstat_easy_register_counter(stat->fs, "curr_udp_sess_closed");
    // Evicted session
    stat->ids.nr_tcp_sess_evicted = fieldstat_easy_register_counter(stat->fs, "tcp_sess_evicted");
    stat->ids.nr_udp_sess_evicted = fieldstat_easy_register_counter(stat->fs, "udp_sess_evicted");
    // Packet
    stat->ids.nr_udp_pkts_nospace_bypass = fieldstat_easy_register_counter(stat->fs, "udp_pkts_nospace_bypass");
    stat->ids.nr_tcp_pkts_nospace_bypass = fieldstat_easy_register_counter(stat->fs, "tcp_pkts_nospace_bypass");
    stat->ids.nr_tcp_pkts_nosess_bypass = fieldstat_easy_register_counter(stat->fs, "tcp_pkts_nosess_bypass");
    stat->ids.nr_tcp_pkts_duped_bypass = fieldstat_easy_register_counter(stat->fs, "tcp_pkts_duped_bypass");
    stat->ids.nr_udp_pkts_duped_bypass = fieldstat_easy_register_counter(stat->fs, "udp_pkts_duped_bypass");
    stat->ids.nr_udp_pkts_evctd_bypass = fieldstat_easy_register_counter(stat->fs, "udp_pkts_evctd_bypass");
    // TCP segments
    stat->ids.nr_tcp_seg_received = fieldstat_easy_register_counter(stat->fs, "tcp_seg_received");
    stat->ids.nr_tcp_seg_expired = fieldstat_easy_register_counter(stat->fs, "tcp_seg_expired");
    stat->ids.nr_tcp_seg_retransmit = fieldstat_easy_register_counter(stat->fs, "tcp_seg_retransmit");
    stat->ids.nr_tcp_seg_overlap = fieldstat_easy_register_counter(stat->fs, "tcp_seg_overlap");
    stat->ids.nr_tcp_seg_no_space = fieldstat_easy_register_counter(stat->fs, "tcp_seg_no_space");
    stat->ids.nr_tcp_seg_inorder = fieldstat_easy_register_counter(stat->fs, "tcp_seg_inorder");
    stat->ids.nr_tcp_seg_reorded = fieldstat_easy_register_counter(stat->fs, "tcp_seg_reorded");
    stat->ids.nr_tcp_seg_buffered = fieldstat_easy_register_counter(stat->fs, "tcp_seg_buffered");
    stat->ids.nr_tcp_seg_released = fieldstat_easy_register_counter(stat->fs, "tcp_seg_released");

    return stat;

error_out:
    stellar_stat_free(stat);
    return NULL;
}

void stellar_stat_free(struct stellar_stat *stat)
{
    if (stat)
    {
        if (stat->fs)
        {
            fieldstat_easy_free(stat->fs);
            stat->fs = NULL;
        }
        free(stat);
        stat = NULL;
    }
}

void stellar_stat_output(struct stellar_stat *stat)
{
    // merge thread stat to global stat
    for (uint16_t i = 0; i < stat->nr_thread; i++)
    {
        if (ATOMIC_READ(&(stat->flag[i])) == IS_BUSY)
        {
            // packet io stat
            stat->io_stat.dev_rx_pkts += stat->thr_io_stat[i].dev_rx_pkts;
            stat->io_stat.dev_rx_bytes += stat->thr_io_stat[i].dev_rx_bytes;

            stat->io_stat.dev_tx_pkts += stat->thr_io_stat[i].dev_tx_pkts;
            stat->io_stat.dev_tx_bytes += stat->thr_io_stat[i].dev_tx_bytes;

            stat->io_stat.keep_alive_pkts += stat->thr_io_stat[i].keep_alive_pkts;
            stat->io_stat.keep_alive_bytes += stat->thr_io_stat[i].keep_alive_bytes;

            stat->io_stat.raw_rx_pkts += stat->thr_io_stat[i].raw_rx_pkts;
            stat->io_stat.raw_rx_bytes += stat->thr_io_stat[i].raw_rx_bytes;

            stat->io_stat.raw_tx_pkts += stat->thr_io_stat[i].raw_tx_pkts;
            stat->io_stat.raw_tx_bytes += stat->thr_io_stat[i].raw_tx_bytes;

            stat->io_stat.drop_pkts += stat->thr_io_stat[i].drop_pkts;
            stat->io_stat.drop_bytes += stat->thr_io_stat[i].drop_bytes;

            stat->io_stat.inject_pkts += stat->thr_io_stat[i].inject_pkts;
            stat->io_stat.inject_bytes += stat->thr_io_stat[i].inject_bytes;

            stat->io_stat.ctrl_rx_pkts += stat->thr_io_stat[i].ctrl_rx_pkts;
            stat->io_stat.ctrl_rx_bytes += stat->thr_io_stat[i].ctrl_rx_bytes;

            stat->io_stat.ctrl_tx_pkts += stat->thr_io_stat[i].ctrl_tx_pkts;
            stat->io_stat.ctrl_tx_bytes += stat->thr_io_stat[i].ctrl_tx_bytes;

            // ip reassembly stat
            stat->ip_stat.ip4_flow_find += stat->thr_ip_stat[i].ip4_flow_find;
            stat->ip_stat.ip4_flow_add += stat->thr_ip_stat[i].ip4_flow_add;
            stat->ip_stat.ip4_flow_del += stat->thr_ip_stat[i].ip4_flow_del;
            stat->ip_stat.ip4_flow_timeout += stat->thr_ip_stat[i].ip4_flow_timeout;

            stat->ip_stat.ip4_flow_fail_no_space += stat->thr_ip_stat[i].ip4_flow_fail_no_space;
            stat->ip_stat.ip4_flow_fail_overlap += stat->thr_ip_stat[i].ip4_flow_fail_overlap;
            stat->ip_stat.ip4_flow_fail_many_frag += stat->thr_ip_stat[i].ip4_flow_fail_many_frag;
            stat->ip_stat.ip4_flow_fail_invalid_length += stat->thr_ip_stat[i].ip4_flow_fail_invalid_length;
            stat->ip_stat.ip4_flow_bypass_dup_fist_frag += stat->thr_ip_stat[i].ip4_flow_bypass_dup_fist_frag;
            stat->ip_stat.ip4_flow_bypass_dup_last_frag += stat->thr_ip_stat[i].ip4_flow_bypass_dup_last_frag;

            stat->ip_stat.ip6_flow_find += stat->thr_ip_stat[i].ip6_flow_find;
            stat->ip_stat.ip6_flow_add += stat->thr_ip_stat[i].ip6_flow_add;
            stat->ip_stat.ip6_flow_del += stat->thr_ip_stat[i].ip6_flow_del;
            stat->ip_stat.ip6_flow_timeout += stat->thr_ip_stat[i].ip6_flow_timeout;

            stat->ip_stat.ip6_flow_fail_no_space += stat->thr_ip_stat[i].ip6_flow_fail_no_space;
            stat->ip_stat.ip6_flow_fail_overlap += stat->thr_ip_stat[i].ip6_flow_fail_overlap;
            stat->ip_stat.ip6_flow_fail_many_frag += stat->thr_ip_stat[i].ip6_flow_fail_many_frag;
            stat->ip_stat.ip6_flow_fail_invalid_length += stat->thr_ip_stat[i].ip6_flow_fail_invalid_length;
            stat->ip_stat.ip6_flow_bypass_dup_fist_frag += stat->thr_ip_stat[i].ip6_flow_bypass_dup_fist_frag;
            stat->ip_stat.ip6_flow_bypass_dup_last_frag += stat->thr_ip_stat[i].ip6_flow_bypass_dup_last_frag;

            memset(&stat->thr_ip_stat[i], 0, sizeof(struct ip_reassembly_stat));

            // session manager stat
            stat->sess_stat.total_nr_tcp_sess_used += stat->thr_sess_stat[i].total_nr_tcp_sess_used;
            stat->sess_stat.curr_nr_tcp_sess_used += stat->thr_sess_stat[i].curr_nr_tcp_sess_used;
            stat->sess_stat.curr_nr_tcp_sess_opening += stat->thr_sess_stat[i].curr_nr_tcp_sess_opening;
            stat->sess_stat.curr_nr_tcp_sess_active += stat->thr_sess_stat[i].curr_nr_tcp_sess_active;
            stat->sess_stat.curr_nr_tcp_sess_closing += stat->thr_sess_stat[i].curr_nr_tcp_sess_closing;
            stat->sess_stat.curr_nr_tcp_sess_discard += stat->thr_sess_stat[i].curr_nr_tcp_sess_discard;
            stat->sess_stat.curr_nr_tcp_sess_closed += stat->thr_sess_stat[i].curr_nr_tcp_sess_closed;

            stat->sess_stat.total_nr_udp_sess_used += stat->thr_sess_stat[i].total_nr_udp_sess_used;
            stat->sess_stat.curr_nr_udp_sess_used += stat->thr_sess_stat[i].curr_nr_udp_sess_used;
            stat->sess_stat.curr_nr_udp_sess_opening += stat->thr_sess_stat[i].curr_nr_udp_sess_opening;
            stat->sess_stat.curr_nr_udp_sess_active += stat->thr_sess_stat[i].curr_nr_udp_sess_active;
            stat->sess_stat.curr_nr_udp_sess_closing += stat->thr_sess_stat[i].curr_nr_udp_sess_closing;
            stat->sess_stat.curr_nr_udp_sess_discard += stat->thr_sess_stat[i].curr_nr_udp_sess_discard;
            stat->sess_stat.curr_nr_udp_sess_closed += stat->thr_sess_stat[i].curr_nr_udp_sess_closed;

            stat->sess_stat.nr_tcp_sess_evicted += stat->thr_sess_stat[i].nr_tcp_sess_evicted;
            stat->sess_stat.nr_udp_sess_evicted += stat->thr_sess_stat[i].nr_udp_sess_evicted;

            stat->sess_stat.nr_udp_pkts_nospace_bypass += stat->thr_sess_stat[i].nr_udp_pkts_nospace_bypass;
            stat->sess_stat.nr_tcp_pkts_nospace_bypass += stat->thr_sess_stat[i].nr_tcp_pkts_nospace_bypass;
            stat->sess_stat.nr_tcp_pkts_nosess_bypass += stat->thr_sess_stat[i].nr_tcp_pkts_nosess_bypass;
            stat->sess_stat.nr_tcp_pkts_duped_bypass += stat->thr_sess_stat[i].nr_tcp_pkts_duped_bypass;
            stat->sess_stat.nr_udp_pkts_duped_bypass += stat->thr_sess_stat[i].nr_udp_pkts_duped_bypass;
            stat->sess_stat.nr_udp_pkts_evctd_bypass += stat->thr_sess_stat[i].nr_udp_pkts_evctd_bypass;

            stat->sess_stat.nr_tcp_seg_received += stat->thr_sess_stat[i].nr_tcp_seg_received;
            stat->sess_stat.nr_tcp_seg_expired += stat->thr_sess_stat[i].nr_tcp_seg_expired;
            stat->sess_stat.nr_tcp_seg_retransmit += stat->thr_sess_stat[i].nr_tcp_seg_retransmit;
            stat->sess_stat.nr_tcp_seg_overlap += stat->thr_sess_stat[i].nr_tcp_seg_overlap;
            stat->sess_stat.nr_tcp_seg_no_space += stat->thr_sess_stat[i].nr_tcp_seg_no_space;
            stat->sess_stat.nr_tcp_seg_inorder += stat->thr_sess_stat[i].nr_tcp_seg_inorder;
            stat->sess_stat.nr_tcp_seg_reorded += stat->thr_sess_stat[i].nr_tcp_seg_reorded;
            stat->sess_stat.nr_tcp_seg_buffered += stat->thr_sess_stat[i].nr_tcp_seg_buffered;
            stat->sess_stat.nr_tcp_seg_released += stat->thr_sess_stat[i].nr_tcp_seg_released;

            memset(&stat->thr_sess_stat[i], 0, sizeof(struct session_manager_stat));

            ATOMIC_SET(&(stat->flag[i]), IS_FREE);
        }
    }

    // device packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.dev_rx_pkts, NULL, 0, stat->io_stat.dev_rx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.dev_rx_bytes, NULL, 0, stat->io_stat.dev_rx_bytes);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.dev_tx_pkts, NULL, 0, stat->io_stat.dev_tx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.dev_tx_bytes, NULL, 0, stat->io_stat.dev_tx_bytes);
    // keep-alive packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.keep_alive_pkts, NULL, 0, stat->io_stat.keep_alive_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.keep_alive_bytes, NULL, 0, stat->io_stat.keep_alive_bytes);
    // raw packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.raw_rx_pkts, NULL, 0, stat->io_stat.raw_rx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.raw_rx_bytes, NULL, 0, stat->io_stat.raw_rx_bytes);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.raw_tx_pkts, NULL, 0, stat->io_stat.raw_tx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.raw_tx_bytes, NULL, 0, stat->io_stat.raw_tx_bytes);
    // drop packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.drop_pkts, NULL, 0, stat->io_stat.drop_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.drop_bytes, NULL, 0, stat->io_stat.drop_bytes);
    // inject packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.inject_pkts, NULL, 0, stat->io_stat.inject_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.inject_bytes, NULL, 0, stat->io_stat.inject_bytes);
    // ctrl packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ctrl_rx_pkts, NULL, 0, stat->io_stat.ctrl_rx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ctrl_rx_bytes, NULL, 0, stat->io_stat.ctrl_rx_bytes);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ctrl_tx_pkts, NULL, 0, stat->io_stat.ctrl_tx_pkts);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ctrl_tx_bytes, NULL, 0, stat->io_stat.ctrl_tx_bytes);
    // ip reassembly
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_find, NULL, 0, stat->ip_stat.ip4_flow_find);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_add, NULL, 0, stat->ip_stat.ip4_flow_add);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_del, NULL, 0, stat->ip_stat.ip4_flow_del);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_timeout, NULL, 0, stat->ip_stat.ip4_flow_timeout);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_fail_no_space, NULL, 0, stat->ip_stat.ip4_flow_fail_no_space);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_fail_overlap, NULL, 0, stat->ip_stat.ip4_flow_fail_overlap);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_fail_many_frag, NULL, 0, stat->ip_stat.ip4_flow_fail_many_frag);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_fail_invalid_length, NULL, 0, stat->ip_stat.ip4_flow_fail_invalid_length);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_bypass_dup_fist_frag, NULL, 0, stat->ip_stat.ip4_flow_bypass_dup_fist_frag);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip4_flow_bypass_dup_last_frag, NULL, 0, stat->ip_stat.ip4_flow_bypass_dup_last_frag);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_find, NULL, 0, stat->ip_stat.ip6_flow_find);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_add, NULL, 0, stat->ip_stat.ip6_flow_add);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_del, NULL, 0, stat->ip_stat.ip6_flow_del);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_timeout, NULL, 0, stat->ip_stat.ip6_flow_timeout);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_fail_no_space, NULL, 0, stat->ip_stat.ip6_flow_fail_no_space);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_fail_overlap, NULL, 0, stat->ip_stat.ip6_flow_fail_overlap);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_fail_many_frag, NULL, 0, stat->ip_stat.ip6_flow_fail_many_frag);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_fail_invalid_length, NULL, 0, stat->ip_stat.ip6_flow_fail_invalid_length);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_bypass_dup_fist_frag, NULL, 0, stat->ip_stat.ip6_flow_bypass_dup_fist_frag);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.ip6_flow_bypass_dup_last_frag, NULL, 0, stat->ip_stat.ip6_flow_bypass_dup_last_frag);
    //  TCP session
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.total_nr_tcp_sess_used, NULL, 0, stat->sess_stat.total_nr_tcp_sess_used);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_used, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_used);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_opening, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_opening);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_active, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_active);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_closing, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_closing);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_discard, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_discard);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_tcp_sess_closed, NULL, 0, stat->sess_stat.curr_nr_tcp_sess_closed);
    // UDP session
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.total_nr_udp_sess_used, NULL, 0, stat->sess_stat.total_nr_udp_sess_used);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_used, NULL, 0, stat->sess_stat.curr_nr_udp_sess_used);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_opening, NULL, 0, stat->sess_stat.curr_nr_udp_sess_opening);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_active, NULL, 0, stat->sess_stat.curr_nr_udp_sess_active);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_closing, NULL, 0, stat->sess_stat.curr_nr_udp_sess_closing);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_discard, NULL, 0, stat->sess_stat.curr_nr_udp_sess_discard);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.curr_nr_udp_sess_closed, NULL, 0, stat->sess_stat.curr_nr_udp_sess_closed);
    // Evicted session
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_sess_evicted, NULL, 0, stat->sess_stat.nr_tcp_sess_evicted);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_udp_sess_evicted, NULL, 0, stat->sess_stat.nr_udp_sess_evicted);
    // Packet
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_udp_pkts_nospace_bypass, NULL, 0, stat->sess_stat.nr_udp_pkts_nospace_bypass);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_pkts_nospace_bypass, NULL, 0, stat->sess_stat.nr_tcp_pkts_nospace_bypass);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_pkts_nosess_bypass, NULL, 0, stat->sess_stat.nr_tcp_pkts_nosess_bypass);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_pkts_duped_bypass, NULL, 0, stat->sess_stat.nr_tcp_pkts_duped_bypass);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_udp_pkts_duped_bypass, NULL, 0, stat->sess_stat.nr_udp_pkts_duped_bypass);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_udp_pkts_evctd_bypass, NULL, 0, stat->sess_stat.nr_udp_pkts_evctd_bypass);
    // TCP segments
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_received, NULL, 0, stat->sess_stat.nr_tcp_seg_received);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_expired, NULL, 0, stat->sess_stat.nr_tcp_seg_expired);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_retransmit, NULL, 0, stat->sess_stat.nr_tcp_seg_retransmit);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_overlap, NULL, 0, stat->sess_stat.nr_tcp_seg_overlap);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_no_space, NULL, 0, stat->sess_stat.nr_tcp_seg_no_space);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_inorder, NULL, 0, stat->sess_stat.nr_tcp_seg_inorder);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_reorded, NULL, 0, stat->sess_stat.nr_tcp_seg_reorded);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_buffered, NULL, 0, stat->sess_stat.nr_tcp_seg_buffered);
    fieldstat_easy_counter_set(stat->fs, 0, stat->ids.nr_tcp_seg_released, NULL, 0, stat->sess_stat.nr_tcp_seg_released);

    char *buff;
    size_t len;
    fieldstat_easy_output(stat->fs, &buff, &len);
    if (buff)
    {
        FILE *fp = fopen(stat->output_file, "w+");
        if (fp == NULL)
        {
            STAT_LOG_ERROR("failed to open file: %s, %s", stat->output_file, strerror(errno));
        }
        else
        {
            fwrite(buff, len, 1, fp);
            fflush(fp);
            fclose(fp);
        }
        free(buff);
    }

    memset(&stat->io_stat, 0, sizeof(struct io_stat));
    memset(&stat->ip_stat, 0, sizeof(struct ip_reassembly_stat));
    memset(&stat->sess_stat, 0, sizeof(struct session_manager_stat));
}

void stellar_stat_merge(struct stellar_stat *stat, const struct thread_stat *thr_stat, uint16_t thr_idx)
{
    if (ATOMIC_READ(&(stat->flag[thr_idx])) == IS_FREE)
    {
        memcpy(&stat->thr_io_stat[thr_idx], thr_stat->io, sizeof(struct io_stat));
        memcpy(&stat->thr_ip_stat[thr_idx], thr_stat->ip_rea, sizeof(struct ip_reassembly_stat));
        memcpy(&stat->thr_sess_stat[thr_idx], thr_stat->sess_mgr, sizeof(struct session_manager_stat));

        ATOMIC_SET(&(stat->flag[thr_idx]), IS_BUSY);
    }
}
