//
// Created by HuangXin on 2023/4/26.
//
#include <ctype.h>
#include "main.h"
#include "zlog_module.h"
#include "dhcp_network.h"
#include "sds/sds.h"
#include "dhcp_options.h"

static GtkWidget *g_detailWnd = NULL;

#if 0
static void dhcp_details_store_create(GtkBuilder *builder) {
    int           i, j;
    PDHCP_INFO    pInfo, pTemp;
    GtkTreeIter   iter, iter_child;
    GtkWidget    *view  = GTK_WIDGET(gtk_builder_get_object(builder, "tvDiscDhcp"));
    GtkTreeStore *store = GTK_TREE_STORE(gtk_builder_get_object(builder, "lsDhcpInfo"));

    gtk_tree_view_set_model(GTK_TREE_VIEW(view), NULL);
    gtk_tree_store_clear(store);

    // 创建根节点
    gtk_tree_store_append(store, &iter, NULL);
    // clang-format off
	gtk_tree_store_set(store,
		   &iter,
		   COL_DHCP_ITEM,           "name",
		   COL_DHCP_VALUE,          "",
		   COL_ITEM_ATTR_VISIABLE,  TRUE,
		   COL_VALUE_ATTR_VISIABLE, FALSE,
		   -1);
    // clang-format on
    gtk_tree_store_append(store, &iter_child, &iter);

    // clang-format off
	// 创建子节点
	gtk_tree_store_set(store,
		   &iter_child,
		    COL_DHCP_VALUE,          "",
            COL_ITEM_ATTR_VISIABLE,  FALSE,
            COL_VALUE_ATTR_VISIABLE, TRUE,
		    -1);
    // clang-format on
    gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
}


void details_wnd_create(void *b) {
    GtkBuilder *builder = gtk_builder_new();
    gtk_builder_add_from_file(builder, "./res/detail.glade", NULL);

    GtkWidget *deatilWnd = GTK_WIDGET(gtk_builder_get_object(builder, "wndDetails"));
    GtkWidget *panDisc   = GTK_WIDGET(gtk_builder_get_object(builder, "hbDisc"));
    GtkWidget *tab       = GTK_WIDGET(gtk_builder_get_object(builder, "nbDetails"));
    GtkWidget *labDisc   = GTK_WIDGET(gtk_builder_get_object(builder, "labDiscover"));
    GtkWidget *labAck    = GTK_WIDGET(gtk_builder_get_object(builder, "labAck"));
    GtkWidget *scAck     = GTK_WIDGET(gtk_builder_get_object(builder, "scAck"));

    gtk_container_add(GTK_CONTAINER(deatilWnd), tab);    // 笔记本放进窗口
    gtk_notebook_append_page(GTK_NOTEBOOK(tab), panDisc, labDisc);
    gtk_notebook_append_page(GTK_NOTEBOOK(tab), scAck, labAck);

    g_signal_connect(G_OBJECT(deatilWnd), "delete_event", G_CALLBACK(delete_event), builder);
    g_signal_connect(G_OBJECT(deatilWnd), "show", G_CALLBACK(show), builder);

    gtk_builder_connect_signals(builder, NULL);
    //gtk_widget_show(deatilWnd);
}
#endif

typedef struct {
    U8 op_code;
    U8 op_size;
    U8 op_value[256];
} STR_OPT;

static char* g_mess_info[] = { "NONE", "DISCOVER", "OFFER", "REQUEST", "DECLINE", "ACK", "NAK", "RELEASE", "INFORM"};

static GtkTextBuffer  *g_pTxtBuf[4];
static GtkWidget      *g_ptvDHcp[4];
static GtkWidget      *g_ptvHex[4];

static gboolean delete_event(GtkWidget *widget, GdkEventAny *event) {
    /* 如果你的 "delete_event" 信号处理函数返回 FALSE,GTK 会发出 "destroy" 信号。
    * 返回 TRUE,你不希望关闭窗口。
    * 当你想弹出“你确定要退出吗?”对话框时它很有用。*/
    //gtk_widget_hide_all(window_test);
    gtk_widget_hide(widget);
    return TRUE;    //注意必须为TRUE,否则子窗口点击关闭按钮后,就摧毁了,而不是隐藏了。
}

static void add_dhcp_tree_colums(GtkWidget *treeView) {
    GtkCellRenderer   *renderer = gtk_cell_renderer_text_new();
    GtkTreeViewColumn *column   = gtk_tree_view_column_new_with_attributes("协议", renderer, "text", 0, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);

    renderer = gtk_cell_renderer_text_new();
    column   = gtk_tree_view_column_new_with_attributes("值", renderer, "text", 1, NULL);
    gtk_tree_view_append_column(GTK_TREE_VIEW(treeView), column);
}

#define ADD_SUB_STRING(name, s)                                     \
    do {                                                            \
        gtk_tree_store_append(store, &iterSub, &iter);              \
        gtk_tree_store_set(store, &iterSub, 0, (name), 1, (s), -1); \
    } while (0)

#define ADD_SUB_INT(name, fmt, v)                                 \
    do {                                                          \
        gtk_tree_store_append(store, &iterSub, &iter);            \
        s = sdsempty();                                           \
        sprintf(s, (fmt), (v));                                   \
        gtk_tree_store_set(store, &iterSub, 0, (name), 1, s, -1); \
        sdsfree(s);                                               \
    } while (0)

#define ADD_SUB_OPTION(code, len, val)                                                      \
    do {                                                                                    \
        int count_flag = 0;                                                                 \
        char *p;                                                                            \
        gtk_tree_store_append(store, &iterOpt, &iterSub);                                   \
        s = sdsempty();                                                                     \
        sprintf(s, "%u", (len));                                                            \
        gtk_tree_store_set(store, &iterOpt, 0, "Length", 1, s, -1);                         \
        sdsfree(s);                                                                         \
        \
        switch(dhcp_get_opType(code)) {                                                  \
            case 1:                                                                         \
                gtk_tree_store_append(store, &iterOpt, &iterSub);                           \
                gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, (val), -1);\
                break;                                                                      \
            case 2:                                                                         \
                do {                                                                        \
                    gtk_tree_store_append(store, &iterOpt, &iterSub);                       \
                    s = sdsempty();                                                         \
                    sprintf(s, "(%u) %s", (val)[count_flag], dhcp_get_opName((val)[count_flag]));\
                    gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, s, -1);\
                    sdsfree(s);                                                             \
                    count_flag++;                                                           \
                } while(count_flag != (len));                                               \
                break;                                                                      \
            case 3:                                                                         \
                do {                                                                        \
                    gtk_tree_store_append(store, &iterOpt, &iterSub);                       \
                    s = sdsempty();                                                         \
                    sprintf(s, "%u.%u.%u.%u", (val)[count_flag], (val)[count_flag+1], (val)[count_flag+2], (val)[count_flag+3]);\
                    gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, s, -1); \
                    sdsfree(s);                                                             \
                    count_flag += 4;                                                        \
                } while(count_flag != (len));                                               \
                break;                                                                      \
            case 4:                                                                         \
                gtk_tree_store_append(store, &iterOpt, &iterSub);                           \
                for(int i = 0; i<(len); i++)                                               \
                    count_flag = count_flag*256 + (val)[i];                                 \
                s = sdsempty();                                                             \
                sprintf(s, "%d(s)", count_flag);                                             \
                gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, s, -1);    \
                sdsfree(s);                                                                 \
                break;                                                                      \
            case 6:                                                                         \
                gtk_tree_store_append(store, &iterOpt, &iterSub);                           \
                s = sdsempty();                                                             \
                if((code) == OPT_MESSAGETYPE) {                                             \
                    gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, g_mess_info[(val)[0]], -1);\
                }                    \
                sdsfree(s);                                                                 \
                break;                                                                      \
            default:                                                                        \
                gtk_tree_store_append(store, &iterOpt, &iterSub);                            \
                gtk_tree_store_set(store, &iterOpt, 0, dhcp_get_opName(code), 1, "", -1);\
        }                                                                                   \
    } while (0)

static void create_dhcp_tree_mode(PDHCP_PACKAGE p, GtkWidget *treeView) {
    // 填充右上侧 TreeView 协议数据
    sds           s;
    GtkTreeIter   iter, iterSub, iterOpt;
    GtkTreeStore *store       = gtk_tree_store_new(2, G_TYPE_STRING, G_TYPE_STRING);
    const char   *itemTitle[] = {"Ethernet II", "802.1Q Virtual LAN", "IP Version 4", "UDP", "DHCP"};

    gtk_tree_store_append(store, &iter, NULL);
    gtk_tree_store_set(store, &iter, 0, itemTitle[0], 1, "", -1);

    // 添加 Ethernet II 头相关内容

    s = sdsempty();
    MAC_TO_STR(p->hdr.eth.h_dest, s);
    ADD_SUB_STRING("Destination", s);
    sdsfree(s);

    s = sdsempty();
    MAC_TO_STR(p->hdr.eth.h_source, s);
    ADD_SUB_STRING("Source", s);
    sdsfree(s);

#if VLAN_SUPPORT
    // 添加 VLan 头相关内容
    gtk_tree_store_append(store, &iter, NULL);
    gtk_tree_store_set(store, &iter, 0, itemTitle[1], 1, "", -1);
    ADD_SUB_INT("ID", "%u", VLAN_VNI_ID(p->hdr.vlan.id));
    ADD_SUB_INT("Type", "0x%04X", ntohs(p->hdr.vlan.type));
#endif

    // 添加 IP 头
    gtk_tree_store_append(store, &iter, NULL);
    gtk_tree_store_set(store, &iter, 0, itemTitle[2], 1, "", -1);
    ADD_SUB_INT("Version", "%u", p->hdr.ip.version);

    s = sdsempty();
    sprintf(s, "%u %s (%u)", p->hdr.ip.ihl*4, "bytes", p->hdr.ip.ihl);
    ADD_SUB_STRING("Header Length", s);
    sdsfree(s);

    ADD_SUB_INT("Differentiated Services Field", "0x%02X", p->hdr.ip.tos);
    ADD_SUB_INT("Total Length", "%u", ntohs(p->hdr.ip.tot_len));
    ADD_SUB_INT("Identification", "0x%04X", ntohs(p->hdr.ip.id));
    ADD_SUB_INT("Flags", "0x%04X", ntohs(p->hdr.ip.frag_off));
    ADD_SUB_INT("Time to live", "%u", p->hdr.ip.ttl);
    ADD_SUB_STRING("Protocol", "UDP");
    ADD_SUB_INT("Header Checksum", "0x%04X", ntohs(p->hdr.ip.check));
    ADD_SUB_STRING("Source", inet_ntoa(*(struct in_addr*)&p->hdr.ip.saddr));
    ADD_SUB_STRING("Destination", inet_ntoa(*(struct in_addr*)&p->hdr.ip.daddr));

    // 添加 UDP 头
    gtk_tree_store_append(store, &iter, NULL);
    gtk_tree_store_set(store, &iter, 0, itemTitle[3], 1, "", -1);
    ADD_SUB_INT("Source Port", "%u", ntohs(p->hdr.udp.source));
    ADD_SUB_INT("Destination Port", "%u", ntohs(p->hdr.udp.dest));
    ADD_SUB_INT("Length", "%u", ntohs(p->hdr.udp.len));
    ADD_SUB_INT("Checksum", "0x%04X", ntohs(p->hdr.udp.check));

    // 添加 DHCP 内容
    gtk_tree_store_append(store, &iter, NULL);
    gtk_tree_store_set(store, &iter, 0, itemTitle[4], 1, "", -1);
    switch(p->dhcp.op) {
        case 1:
            ADD_SUB_STRING("Message Type", "Boot Request");
            break;
        case 2:
            ADD_SUB_STRING("Message Type", "Boot Reply");
            break;
        default:
            ADD_SUB_STRING("Message Type", "Unknown");
    }
    switch(p->dhcp.htype) {
        case 1:
            ADD_SUB_STRING("Hardware Type", "Ethernet");
            break;
        default:
            ADD_SUB_STRING("Hardware Type", "Unknown");
    }
    ADD_SUB_INT("Hardware address length", "%u", p->dhcp.hlen);
    ADD_SUB_INT("Hops", "%u", p->dhcp.hops);
    ADD_SUB_INT("Transaction ID", "0x%08X", p->dhcp.xid);
    ADD_SUB_INT("Seconds elapsed", "%u", p->dhcp.secs);
    ADD_SUB_INT("Bootp flags", "0x%04X", p->dhcp.flags);
    ADD_SUB_STRING("Client IP address", inet_ntoa(*(struct in_addr*)&p->dhcp.ciaddr));
    ADD_SUB_STRING("Your(client) IP address", inet_ntoa(*(struct in_addr*)&p->dhcp.yiaddr));
    ADD_SUB_STRING("Next server IP address", inet_ntoa(*(struct in_addr*)&p->dhcp.siaddr));
    ADD_SUB_STRING("Relay agent IP address", inet_ntoa(*(struct in_addr*)&p->dhcp.giaddr));
    s = sdsempty();
    MAC_TO_STR(p->dhcp.chaddr, s);
    ADD_SUB_STRING("Destination", s);
    sdsfree(s);

    if(strlen(p->dhcp.sname) != 0)
        ADD_SUB_STRING("Server host name", p->dhcp.sname);
    else
        ADD_SUB_STRING("Server host name", "not given");

    if(strlen(p->dhcp.file) != 0)
        ADD_SUB_STRING("Boot file name", p->dhcp.file);
    else
        ADD_SUB_STRING("Boot file name", "not given");

    ADD_SUB_STRING("Magic cookie", "DHCP");

    U8 *opPointer;
    opPointer = p->dhcp.options;
    while (*opPointer && *opPointer != OPT_END) {
        STR_OPT opTmp;
        opTmp.op_code = *opPointer;
        opPointer++;
        opTmp.op_size = *opPointer;
        opPointer++;
        memcpy(opTmp.op_value, opPointer, opTmp.op_size);
        opPointer += opTmp.op_size;

        s = sdsempty();
        sprintf(s, "(%u) %s", opTmp.op_code, dhcp_get_opName(opTmp.op_code));
        ADD_SUB_STRING("Option", s);
        sdsfree(s);

        ADD_SUB_OPTION(opTmp.op_code, opTmp.op_size, opTmp.op_value);
    }

    if(*opPointer == OPT_END) {
        ADD_SUB_STRING("Option", "(255) End");
    }

    gtk_tree_view_set_model(GTK_TREE_VIEW(treeView), GTK_TREE_MODEL(store));
    g_object_unref(GTK_TREE_MODEL(store));
}

void details_wnd_show(PDHCP_INFO pInfo) {
    PBUF_INFO hexBuf[4];

    if (pInfo) {
        hexBuf[0] = &pInfo->pDiscBuf;
        hexBuf[1] = &pInfo->pOfferBuf;
        hexBuf[2] = &pInfo->pReqBuf;
        hexBuf[3] = &pInfo->pAckBuf;
        // 填充HEX 窗口数据
        for (int i = 0; i < 4; i++) {
            char        buf[128] = {0};
            U32         offset   = 0;
            GtkTextIter iter, iter1;

            if (hexBuf[i]->buf_size == 0) {
                continue;
            }

            gtk_text_buffer_get_start_iter(g_pTxtBuf[i], &iter);
            gtk_text_buffer_get_end_iter(g_pTxtBuf[i], &iter1);

            if (strlen(gtk_text_buffer_get_text(g_pTxtBuf[i], &iter, &iter1, TRUE)) > 0) {
                continue;
            }

            gtk_text_buffer_get_iter_at_offset(g_pTxtBuf[i], &iter, 0);
            sprintf(buf, " Offset(h) 00 01 02 03 04 05 06 07  08 09 0A 0B 0C 0D 0E 0F  ASCII Tables\n");
            gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, buf, -1, "hex_head", NULL);

            int row = hexBuf[i]->buf_size / 16 + ((hexBuf[i]->buf_size % 16) > 0 ? 1 : 0);

            for (int k = 0; k < row; k++) {
                char bufOffset[24] = {0};
                memset(buf, 0, 128);
                sprintf(bufOffset, " %08X  ", offset);
                gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, bufOffset, -1, "hex_offset", NULL);

                for (int m = 0; m < 16; m++) {
                    char bufStr[4] = {0};
                    sprintf(bufStr, "%02X ", hexBuf[i]->p[k * 16 + m]);
                    strcat(buf, bufStr);
                    if (m == 7 || m == 15) {
                        strcat(buf, " ");
                    }
                }

                for (int m = 0; m < 16; m++) {
                    if (isprint(hexBuf[i]->p[k * 16 + m])) {
                        char bufStr[2] = {0};
                        bufStr[0]      = (char)hexBuf[i]->p[k * 16 + m];
                        strcat(buf, bufStr);
                    } else {
                        strcat(buf, ".");
                    }
                }
                strcat(buf, "\n");
                gtk_text_buffer_insert_with_tags_by_name(g_pTxtBuf[i], &iter, buf, -1, "bold", NULL);
                offset += 16;
            }

            // 填充 TreeView
            add_dhcp_tree_colums(g_ptvDHcp[i]);
            create_dhcp_tree_mode((PDHCP_PACKAGE)hexBuf[i]->p, g_ptvDHcp[i]);
        }
    }

    gtk_widget_show_all(g_detailWnd);
}

static gboolean button_release_event(GtkWidget *self, GdkEventButton event, gpointer user_data) {
    GtkTextIter    iter, start_sel, end_sel;
    GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(self));
    gtk_text_buffer_get_iter_at_mark(buffer, &iter, gtk_text_buffer_get_insert(buffer));
    int row = gtk_text_iter_get_line(&iter);
    int col = gtk_text_iter_get_line_offset(&iter);

    if (col <= 57 && col >= 11) {
        if ((col <= 35 && ((col - 11) % 3) == 0) || (col > 35 && (col - 12) % 3 == 0)) {
            GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
            gtk_text_buffer_get_iter_at_mark(buffer, &start_sel, mark);
            gtk_text_buffer_get_iter_at_line_offset(buffer, &end_sel, row, 59);
            //char *text = (char *)gtk_text_buffer_get_text(buffer, &iter, &end_sel, FALSE);
            //printf("Select %s(%d, %d)\n", text, row, col);
        }
    }

    return FALSE;
}

void details_wnd_create(GtkBuilder *builder) {
    // 创建主窗口
    GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    //设置窗口大小
    gtk_widget_set_size_request(window, 900, 600);

    // 创建笔记本控件
    GtkWidget *notebook = gtk_notebook_new();
    // 笔记本放进窗口
    gtk_container_add(GTK_CONTAINER(window), notebook);

    // 页标签的位置,可以有四种位置:上、下、左或右。
    gtk_notebook_set_tab_pos(GTK_NOTEBOOK(notebook), GTK_POS_TOP);

    for (int i = 0; i < 4; i++) {
        GtkTreeIter iter;
        const char *pTabName[] = {"Discover", "Offer", "Request", "Ack"};
        // 第一个页面
        GtkWidget  *label      = gtk_label_new(pTabName[i]);

        // 水平分割面板
        GtkWidget *boxTab = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 3);

        // 左侧 Hex 窗口
        GtkWidget *scHex  = gtk_scrolled_window_new(NULL, NULL);
        GtkWidget *scText = gtk_text_view_new();
        gtk_container_add(GTK_CONTAINER(scHex), scText);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scHex), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);

        // 分隔条
        GtkWidget *vSepar = gtk_separator_new(GTK_ORIENTATION_VERTICAL);
        // 右侧 详细信息窗口
        GtkWidget *scMsg  = gtk_scrolled_window_new(NULL, NULL);
        // 上下分割面板
        GtkWidget *vpaned = gtk_paned_new(GTK_ORIENTATION_VERTICAL);

        // 上半部分控件
        GtkWidget *tvDhcp = gtk_tree_view_new();
        gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(tvDhcp), GTK_TREE_VIEW_GRID_LINES_BOTH);
        gtk_container_add(GTK_CONTAINER(scMsg), tvDhcp);

        gtk_paned_pack1(GTK_PANED(vpaned), scMsg, TRUE, FALSE);
        gtk_widget_set_size_request(tvDhcp, -1, 300);
        gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scMsg), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);

        // 下半部分控件
        GtkWidget         *tvHex    = gtk_tree_view_new();
        GtkCellRenderer   *renderer = gtk_cell_renderer_text_new();
        GtkTreeViewColumn *column   = gtk_tree_view_column_new_with_attributes("类型", renderer, "text", 0, NULL);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tvHex), column);
        g_object_set(column, "min-width", 120, NULL);
        renderer = gtk_cell_renderer_text_new();
        column   = gtk_tree_view_column_new_with_attributes("值", renderer, "text", 1, NULL);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tvHex), column);

        GtkListStore *store = gtk_list_store_new(2, G_TYPE_STRING, G_TYPE_STRING);

        const char *itemTitle[] = {"Binary", "unsigne char", "unsigne short", "unsigne int", "unsigne char long long",
                                   "char",   "short",        "int",           "long long"};
        for (int k = 0; k < ARRAY_SIZE(itemTitle); k++) {
            gtk_list_store_append(store, &iter);
            gtk_list_store_set(store, &iter, 0, itemTitle[k], 1, "", -1);
        }
        gtk_tree_view_set_model(GTK_TREE_VIEW(tvHex), GTK_TREE_MODEL(store));

        gtk_tree_view_set_grid_lines(GTK_TREE_VIEW(tvHex), GTK_TREE_VIEW_GRID_LINES_BOTH);
        gtk_paned_pack2(GTK_PANED(vpaned), tvHex, FALSE, FALSE);

        // 控件布局
        gtk_box_pack_start(GTK_BOX(boxTab), scHex, TRUE, TRUE, 1);
        gtk_box_pack_start(GTK_BOX(boxTab), vSepar, FALSE, FALSE, 1);
        gtk_box_pack_start(GTK_BOX(boxTab), vpaned, TRUE, TRUE, 1);
        gtk_widget_set_size_request(scHex, 580, -1);
        gtk_widget_set_size_request(tvDhcp, 300, -1);

        gtk_notebook_append_page(GTK_NOTEBOOK(notebook), boxTab, label);
        g_pTxtBuf[i] = gtk_text_view_get_buffer(GTK_TEXT_VIEW(scText));
        gtk_text_view_set_editable(GTK_TEXT_VIEW(scText), FALSE);
        gtk_text_view_set_monospace(GTK_TEXT_VIEW(scText), TRUE);

        gtk_text_buffer_create_tag(g_pTxtBuf[i], "blue_foreground", "foreground", "blue", NULL);
        gtk_text_buffer_create_tag(g_pTxtBuf[i], "hex_head", "foreground", "red", "pixels_below_lines", 5, NULL);
        gtk_text_buffer_create_tag(g_pTxtBuf[i], "hex_offset", "foreground", "red", NULL);
        gtk_text_buffer_create_tag(g_pTxtBuf[i], "bold", "weight", PANGO_WEIGHT_BOLD, NULL);
        g_signal_connect(G_OBJECT(scText), "button_release_event", G_CALLBACK(button_release_event), builder);

        g_ptvDHcp[i] = tvDhcp;
        g_ptvHex[i]  = tvHex;
    }

    g_signal_connect(G_OBJECT(window), "delete_event", G_CALLBACK(delete_event), builder);

    g_detailWnd = window;
}