// gcc -o xpathd xpathd.c fort.c $(xml2-config --cflags --libs) -lxslt -lxml2

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <signal.h>
#include <errno.h>
#include <time.h>

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxml/xpathInternals.h>
#include <libxml/HTMLparser.h>
#include <libxml/uri.h>

#define STB_ARGPARSE_IMPLEMENTATION
#include "stb_argparse.h"
#include "fort.h"

void help_xpath(void)
{
    printf("XPath 1.0 Syntax Reference (libxml2)\n");
    printf("\n");
    printf("1. Core Syntax: Path Expressions\n");
    ft_table_t *table = ft_create_table();
    /* Set "header" type for the first row */
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Expression", "Description", "Example", "Result Description");
    ft_write_ln(table, "`/`", "Selects from the absolute root", "`/bookstore/book`", "All `book` elements & children of `bookstore`");
    ft_write_ln(table, "`//`", "Selects nodes anywhere in the document", "`//book`", "All `book` elements regardless of location");
    ft_write_ln(table, "`.`", "Selects the current node", "`xpath n1 .`", "Returns the current node itself");
    ft_write_ln(table, "`..`", "Selects the parent of the current node", "`xpath n1 ..`", "Returns the parent of the current node");
    ft_write_ln(table, "`@`", "Selects attributes", "`//@lang`", "Selects all attributes named `lang`");
    ft_write_ln(table, "`*`", "Wildcard, matches any element node", "`/bookstore/*`", "Selects all child elements of `bookstore`");
    ft_write_ln(table, "`@*`", "Matches any attribute node", "`//*[@*]`", "Selects all elements that have any attribute");
    ft_write_ln(table, "`node()`", "Matches any node\n(element, text, comment, etc.)", "`//node()`", "Selects all nodes");
    ft_write_ln(table, "`text()`", "Matches text nodes", "`//book/text()`", "All direct text children of `book` elements");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("2. Predicates (Filters)\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Expression", "Description", "Example", "Result Description");
    ft_write_ln(table, "`[n]`", "The nth element (starts at 1)", "`/bookstore/book[1]`", "The first `book` element");
    ft_write_ln(table, "`[last()]`", "The last element", "`/bookstore/book[last()]`", "The last `book` element");
    ft_write_ln(table, "`[last()-1]`", "The second last element", "`/bookstore/book[last()-1]`", "The second last `book` element");
    ft_write_ln(table, "`[position()<3]`", "The first two elements", "`/bookstore/book[position()<3]`", "The first two `book` elements");
    ft_write_ln(table, "`[@attr]`", "Nodes that have an attribute", "`//book[@lang]`", "All `book` elements\nw/ a `lang` attribute");
    ft_write_ln(table, "`[@attr=\"v\"]`", "Nodes with attribute=v", "`//book[@lang=\"en\"]`", "All `book` elements w/ `lang=\"en\"`");
    ft_write_ln(table, "`[c and c]`", "Logical AND", "`//book[@lang=\"en\" and @price>30]`", "`book` with `lang=\"en\"`\nand `price>30`");
    ft_write_ln(table, "`[c or c]`", "Logical OR", "`//book[@lang=\"en\" or @lang=\"fr\"]`", "`book` with `lang=\"en\"`\nor `lang=\"fr\"`");
    ft_write_ln(table, "`[function()]`", "Function as predicate", "`//book[starts-with(@lang, \"e\")]`", "`book` whose `lang`\nstarts with \"e\"");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("3. Axes\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Axis", "Description", "Abbreviation", "Example (xpath n1 `child::book`)");
    ft_write_ln(table, "`child::`", "All children of the current node", "default", "`child::book`\n(equivalent to `book`)");
    ft_write_ln(table, "`parent::`", "The parent of the current node", "`..`", "`parent::*`");
    ft_write_ln(table, "`self::`", "The current node itself", "`.`", "`self::book`");
    ft_write_ln(table, "`attribute::`", "Attributes of the current node", "`@`", "`attribute::lang`\n(equivalent to `@lang`)");
    ft_write_ln(table, "`descendant::`", "All descendants ((grand)children, etc.)", "`//`", "`descendant::book`\n(equivalent to `.//book`)");
    ft_write_ln(table, "`ancestor::`", "Ancestors (parent, grandparent, etc.)", "none", "`ancestor::bookstore`");
    ft_write_ln(table, "`following-sibling::`", "All siblings after the current node", "none", "`following-sibling::book`");
    ft_write_ln(table, "`preceding-sibling::`", "All siblings before the current node", "none", "`preceding-sibling::book`");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("4. Operators\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Operator", "Description", "Example");
    ft_write_ln(table, "`|`", "Computes the union of two node-sets", "`//book | //cd`\nSelects all `book` and `cd` elements");
    ft_write_ln(table, "`+`", "Addition", "`price + tax`");
    ft_write_ln(table, "`-`", "Subtraction", "`price - discount`");
    ft_write_ln(table, "`*`", "Multiplication", "`price * 1.1`");
    ft_write_ln(table, "`div`", "Division", "`price div 2`");
    ft_write_ln(table, "`=`", "Equal", "`price = 29.99`");
    ft_write_ln(table, "`!=`", "Not equal", "`price != 29.99`");
    ft_write_ln(table, "`<`", "Less than (must be escaped as `&lt;` or in CDATA)", "`price &lt; 30`");
    ft_write_ln(table, "`<=`", "Less than or equal", "`price &lt;= 30`");
    ft_write_ln(table, "`>`", "Greater than", "`price > 30`");
    ft_write_ln(table, "`>=`", "Greater than or equal", "`price >= 30`");
    ft_write_ln(table, "`or`", "Logical OR", "`price = 29.99 or price = 39.95`");
    ft_write_ln(table, "`and`", "Logical AND", "`price > 29.99 and price < 39.95`");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("5. Core Functions\n");
    printf("\n");
    printf("5.1 Node Set Functions\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Function", "Description", "Example");
    ft_write_ln(table, "`last()`", "Returns the context size (total number of nodes in the node-set)", "`//book[last()]`");
    ft_write_ln(table, "`position()`", "Returns the position of the current node in the node-set", "`//book[position()<3]`");
    ft_write_ln(table, "`count(node-set)`", "Returns the number of nodes in the node-set", "`count(//book)`");
    ft_write_ln(table, "`name([node-set])`", "Returns the name of the node (with namespace prefix)", "`name(.)`");
    ft_write_ln(table, "`local-name([node-set])`", "Returns the local name of the node (without prefix)", "`local-name(.)`");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("5.2 String Functions\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Function", "Description", "Example");
    ft_write_ln(table, "`string([object])`", "Converts an object to a string", "`string(price)`");
    ft_write_ln(table, "`concat(string, string, ...)`", "Concatenates strings", "`concat(firstname, ' ', lastname)`");
    ft_write_ln(table, "`starts-with(string, substring)`", "Checks if the string starts with a substring", "`//book[starts-with(@lang, \"en\")]`");
    ft_write_ln(table, "`contains(string, substring)`", "Checks if the string contains a substring", "`//book[contains(title, \"XML\")]`");
    ft_write_ln(table, "`substring(string, start, [len])`", "Returns a substring (positions start at 1)", "`substring(name, 1, 3)`");
    ft_write_ln(table, "`string-length([string])`", "Returns the length of the string", "`string-length(title)`");
    ft_write_ln(table, "`normalize-space([string])`", "Strips leading/trailing spaces\nand collapses internal spaces", "`normalize-space(description)`");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("5.3 Boolean Functions\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Function", "Description", "Example");
    ft_write_ln(table, "`boolean(object)`", "Converts an object to a boolean", "`boolean(//book)` (true if any book exists)");
    ft_write_ln(table, "`not(boolean)`", "Logical NOT", "`//book[not(@lang)]`\nSelects books without `lang` attribute");
    ft_write_ln(table, "`true()`", "Returns true", "");
    ft_write_ln(table, "`false()`", "Returns false", "");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("5.4 Number Functions\n");
    table = ft_create_table();
    ft_set_cell_prop(table, 0, FT_ANY_COLUMN, FT_CPROP_ROW_TYPE, FT_ROW_HEADER);
    ft_write_ln(table, "Function", "Description", "Example");
    ft_write_ln(table, "`number([object])`", "Converts an object to a number", "`number(price)`");
    ft_write_ln(table, "`sum(node-set)`", "Sums the numeric values of the nodes in the node-set", "`sum(//price)`");
    ft_write_ln(table, "`floor(number)`", "Rounds down to the nearest integer", "`floor(price)`");
    ft_write_ln(table, "`ceiling(number)`", "Rounds up to the nearest integer", "`ceiling(price)`");
    ft_write_ln(table, "`round(number)`", "Rounds to the nearest integer", "`round(price)`");
    printf("%s\n", ft_to_string(table));
    ft_destroy_table(table);
    printf("\n");
    printf("Please run 'man xpathd' for details.\n");
    printf("Note: libxml2 supports XPath 1.0 only. XPath 2.0/3.0 features are not supported.\n");
}

#define MAX_DOCS 256
#define MAX_NODES 65536
#define MAX_CLIENTS 64          // 最大并发TCP连接数
#define MAX_SESSIONS 256        // 最大逻辑会话数
#define BUFFER_SIZE 4096
#define LISTEN_PORT 9999
#define SESSION_TIMEOUT 600     // 会话超时时间（秒），0表示永不过期

// ---------- 会话结构（独立于TCP连接）----------
typedef struct {
    int session_id;             // 会话ID（从1开始）
    time_t last_used;           // 最后使用时间（用于超时清理）
    // 文档表
    struct {
        int doc_id;
        xmlDocPtr doc;
        int valid;
    } docs[MAX_DOCS];
    // 节点表
    struct {
        int node_id;
        xmlNodePtr node;
        int doc_id;             // 所属文档ID
        int valid;
    } nodes[MAX_NODES];
} Session;

// 全局会话表
static Session global_sessions[MAX_SESSIONS];

// ---------- TCP客户端连接结构 ----------
typedef struct {
    int sockfd;                 // 客户端socket，-1表示空闲
    char inbuf[BUFFER_SIZE];    // 输入缓冲区
    size_t inlen;               // 缓冲区有效数据长度
} ClientConnection;

static ClientConnection clients[MAX_CLIENTS];
static int listen_fd = -1;
static int max_fd = 0;
static fd_set read_fds;

// 初始化libxml2
static void init_libxml() {
    LIBXML_TEST_VERSION
}

// 清理libxml2
static void cleanup_libxml() {
    xmlCleanupParser();
}

// ---------- 会话管理函数 ----------

// 查找空闲会话槽，返回索引，失败返回-1
static int find_free_session() {
    for (int i = 1; i < MAX_SESSIONS; i++) {   // ID从1开始，0保留
        if (global_sessions[i].session_id == 0) { // 空闲
            return i;
        }
    }
    return -1;
}

// 创建新会话，返回会话ID（索引），失败返回-1
static int create_session() {
    int idx = find_free_session();
    if (idx == -1) return -1;
    memset(&global_sessions[idx], 0, sizeof(Session));
    global_sessions[idx].session_id = idx;      // 直接用索引作为ID，方便
    global_sessions[idx].last_used = time(NULL);
    return idx;
}

// 根据会话ID获取会话指针
static Session* get_session(int sess_id) {
    if (sess_id < 1 || sess_id >= MAX_SESSIONS) return NULL;
    if (global_sessions[sess_id].session_id != sess_id) return NULL; // 已释放
    return &global_sessions[sess_id];
}

// 关闭会话，释放所有文档和节点
static void close_session(int sess_id) {
    Session *sess = get_session(sess_id);
    if (!sess) return;
    // 释放所有文档
    for (int i = 1; i < MAX_DOCS; i++) {
        if (sess->docs[i].valid) {
            xmlFreeDoc(sess->docs[i].doc);
            sess->docs[i].valid = 0;
        }
    }
    // 节点表无需单独释放（节点属于文档）
    memset(sess, 0, sizeof(Session));
}

// 检查并清理超时会话（在主循环中调用）
static void cleanup_expired_sessions() {
    time_t now = time(NULL);
    for (int i = 1; i < MAX_SESSIONS; i++) {
        if (global_sessions[i].session_id != 0) {
            if (SESSION_TIMEOUT > 0 && (now - global_sessions[i].last_used) > SESSION_TIMEOUT) {
                // 超时，关闭会话
                close_session(i);
            }
        }
    }
}

// 更新会话最后使用时间
static void touch_session(int sess_id) {
    Session *sess = get_session(sess_id);
    if (sess) {
        sess->last_used = time(NULL);
    }
}

// ---------- 在会话内分配文档句柄 ----------
static int alloc_doc(Session *sess, xmlDocPtr doc) {
    for (int i = 1; i < MAX_DOCS; i++) {
        if (!sess->docs[i].valid) {
            sess->docs[i].doc = doc;
            sess->docs[i].doc_id = i;
            sess->docs[i].valid = 1;
            return i;
        }
    }
    return -1;
}

// 释放文档句柄及相关节点句柄
static void free_doc(Session *sess, int doc_id) {
    if (doc_id < 1 || doc_id >= MAX_DOCS || !sess->docs[doc_id].valid) return;
    xmlFreeDoc(sess->docs[doc_id].doc);
    sess->docs[doc_id].valid = 0;
    // 释放所有属于该文档的节点句柄
    for (int i = 1; i < MAX_NODES; i++) {
        if (sess->nodes[i].valid && sess->nodes[i].doc_id == doc_id) {
            sess->nodes[i].valid = 0;
        }
    }
}

static xmlDocPtr get_doc(Session *sess, int doc_id) {
    if (doc_id < 1 || doc_id >= MAX_DOCS || !sess->docs[doc_id].valid) return NULL;
    return sess->docs[doc_id].doc;
}

// 分配节点句柄（去重）
static int alloc_node(Session *sess, xmlNodePtr node, int doc_id) {
    // 先查找是否已存在相同节点指针
    for (int i = 1; i < MAX_NODES; i++) {
        if (sess->nodes[i].valid && sess->nodes[i].node == node && sess->nodes[i].doc_id == doc_id) {
            return i;
        }
    }
    // 不存在，找空闲位置
    for (int i = 1; i < MAX_NODES; i++) {
        if (!sess->nodes[i].valid) {
            sess->nodes[i].node = node;
            sess->nodes[i].doc_id = doc_id;
            sess->nodes[i].valid = 1;
            return i;
        }
    }
    return -1;
}

static xmlNodePtr get_node(Session *sess, int node_id) {
    if (node_id < 1 || node_id >= MAX_NODES || !sess->nodes[node_id].valid) return NULL;
    return sess->nodes[node_id].node;
}

static void free_node(Session *sess, int node_id) {
    if (node_id < 1 || node_id >= MAX_NODES || !sess->nodes[node_id].valid) return;
    sess->nodes[node_id].valid = 0;
}

// ---------- 辅助函数 ----------

// 向客户端发送格式化字符串
static void client_printf(int sockfd, const char *fmt, ...) {
    char buf[4096];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);
    send(sockfd, buf, strlen(buf), 0);
}

// 解析带前缀的句柄字符串（如 d1, n2a），返回ID，并通过type输出类型（'d'或'n'）
static int parse_handle(const char *str, char *type) {
    if (!str || strlen(str) < 2) return -1;
    if (str[0] == 'd' || str[0] == 'D') {
        *type = 'd';
    } else if (str[0] == 'n' || str[0] == 'N') {
        *type = 'n';
    } else {
        return -1;
    }
    char *endptr;
    long id = strtol(str + 1, &endptr, 16);
    if (*endptr != '\0' || id <= 0) return -1;
    return (int)id;
}

// 输出节点句柄列表（空格分隔）
static void print_node_handles(int sockfd, Session *sess, xmlNodeSetPtr nodeset, int doc_id) {
    if (!nodeset) return;
    int first = 1;
    for (int i = 0; i < nodeset->nodeNr; i++) {
        xmlNodePtr node = nodeset->nodeTab[i];
        int node_id = alloc_node(sess, node, doc_id);
        if (node_id > 0) {
            if (!first) client_printf(sockfd, " ");
            client_printf(sockfd, "n%x", node_id);
            first = 0;
        }
    }
    client_printf(sockfd, "\n");
}

// 处理XPath查询结果
static void handle_xpath_result(int sockfd, Session *sess, xmlXPathObjectPtr result, int doc_id) {
    if (!result) {
        client_printf(sockfd, "XPath evaluation returned NULL\n");
        return;
    }
    switch (result->type) {
        case XPATH_NODESET:
            print_node_handles(sockfd, sess, result->nodesetval, doc_id);
            break;
        case XPATH_BOOLEAN:
            client_printf(sockfd, "%s\n", result->boolval ? "true" : "false");
            break;
        case XPATH_NUMBER:
            client_printf(sockfd, "%g\n", result->floatval);
            break;
        case XPATH_STRING:
            client_printf(sockfd, "%s\n", result->stringval);
            break;
        default:
            client_printf(sockfd, "Unsupported XPath result type: %d\n", result->type);
    }
}

// ---------- 命令处理函数（均需传入会话指针和客户端socket）----------
static void cmd_open(int sockfd, Session *sess, char *type_str, char *uri) {
    xmlDocPtr doc = NULL;
    if (strcmp(type_str, "xml") == 0) {
        doc = xmlReadFile(uri, NULL, XML_PARSE_RECOVER);
    } else if (strcmp(type_str, "html") == 0) {
        doc = htmlReadFile(uri, NULL, HTML_PARSE_RECOVER | HTML_PARSE_NOERROR | HTML_PARSE_NOWARNING);
    } else {
        client_printf(sockfd, "Invalid type: %s (use xml or html)\n", type_str);
        return;
    }
    if (!doc) {
        client_printf(sockfd, "Failed to open %s\n", uri);
        return;
    }
    int doc_id = alloc_doc(sess, doc);
    if (doc_id < 0) {
        client_printf(sockfd, "No free document slot\n");
        xmlFreeDoc(doc);
        return;
    }
    client_printf(sockfd, "d%x\n", doc_id);
}

static void cmd_xpath(int sockfd, Session *sess, char *handle_str, char *expr) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0) {
        client_printf(sockfd, "Invalid handle\n");
        return;
    }

    xmlDocPtr doc = NULL;
    xmlNodePtr node = NULL;
    int doc_id = -1;

    if (type == 'd') {
        doc = get_doc(sess, id);
        if (!doc) {
            client_printf(sockfd, "Document not found\n");
            return;
        }
        doc_id = id;
    } else if (type == 'n') {
        node = get_node(sess, id);
        if (!node) {
            client_printf(sockfd, "Node not found\n");
            return;
        }
        doc = node->doc;
        doc_id = sess->nodes[id].doc_id;
        if (!doc) {
            client_printf(sockfd, "Node's document not found\n");
            return;
        }
    } else {
        client_printf(sockfd, "Unknown handle type\n");
        return;
    }

    xmlXPathContextPtr ctx = xmlXPathNewContext(doc);
    if (!ctx) {
        client_printf(sockfd, "Failed to create XPath context\n");
        return;
    }

    if (type == 'n' && node) {
        if (xmlXPathSetContextNode(node, ctx) != 0) {
            client_printf(sockfd, "Failed to set context node\n");
            xmlXPathFreeContext(ctx);
            return;
        }
    }

    xmlXPathObjectPtr result = xmlXPathEvalExpression(BAD_CAST expr, ctx);
    if (!result) {
        client_printf(sockfd, "XPath evaluation failed\n");
        xmlXPathFreeContext(ctx);
        return;
    }

    handle_xpath_result(sockfd, sess, result, doc_id);

    xmlXPathFreeObject(result);
    xmlXPathFreeContext(ctx);
}

static void cmd_get(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlBufferPtr buf = xmlBufferCreate();
    xmlNodeDump(buf, node->doc, node, 0, 1);
    client_printf(sockfd, "%s\n", (char*)buf->content);
    xmlBufferFree(buf);
}

static void cmd_close(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0) {
        client_printf(sockfd, "Invalid handle\n");
        return;
    }
    if (type == 'd') {
        free_doc(sess, id);
        client_printf(sockfd, "OK\n");
    } else {
        free_node(sess, id);
        client_printf(sockfd, "OK\n");
    }
}

static void cmd_text(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlChar *content = xmlNodeGetContent(node);
    if (content) {
        client_printf(sockfd, "%s\n", (char*)content);
        xmlFree(content);
    } else {
        client_printf(sockfd, "\n");
    }
}

static void cmd_parent(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr parent = node->parent;
    if (!parent) {
        client_printf(sockfd, "\n");
        return;
    }
    int parent_id = alloc_node(sess, parent, sess->nodes[id].doc_id);
    if (parent_id > 0) {
        client_printf(sockfd, "n%x\n", parent_id);
    } else {
        client_printf(sockfd, "Failed to allocate handle for parent\n");
    }
}

static void cmd_first_child(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr child = node->children;
    if (child) {
        int child_id = alloc_node(sess, child, sess->nodes[id].doc_id);
        if (child_id > 0) {
            client_printf(sockfd, "n%x\n", child_id);
        } else {
            client_printf(sockfd, "\n");
        }
    } else {
        client_printf(sockfd, "\n");
    }
}

static void cmd_last_child(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr child = node->last;
    if (child) {
        int child_id = alloc_node(sess, child, sess->nodes[id].doc_id);
        if (child_id > 0) {
            client_printf(sockfd, "n%x\n", child_id);
        } else {
            client_printf(sockfd, "\n");
        }
    } else {
        client_printf(sockfd, "\n");
    }
}

static void cmd_next_sibling(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr sibling = node->next;
    if (sibling) {
        int sibling_id = alloc_node(sess, sibling, sess->nodes[id].doc_id);
        if (sibling_id > 0) {
            client_printf(sockfd, "n%x\n", sibling_id);
        } else {
            client_printf(sockfd, "\n");
        }
    } else {
        client_printf(sockfd, "\n");
    }
}

static void cmd_prev_sibling(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr sibling = node->prev;
    if (sibling) {
        int sibling_id = alloc_node(sess, sibling, sess->nodes[id].doc_id);
        if (sibling_id > 0) {
            client_printf(sockfd, "n%x\n", sibling_id);
        } else {
            client_printf(sockfd, "\n");
        }
    } else {
        client_printf(sockfd, "\n");
    }
}

static void cmd_children(int sockfd, Session *sess, char *handle_str) {
    char type;
    int id = parse_handle(handle_str, &type);
    if (id < 0 || type != 'n') {
        client_printf(sockfd, "Invalid node handle\n");
        return;
    }
    xmlNodePtr node = get_node(sess, id);
    if (!node) {
        client_printf(sockfd, "Node not found\n");
        return;
    }
    xmlNodePtr child = node->children;
    int first = 1;
    while (child) {
        int child_id = alloc_node(sess, child, sess->nodes[id].doc_id);
        if (child_id > 0) {
            if (!first) client_printf(sockfd, " ");
            client_printf(sockfd, "n%x", child_id);
            first = 0;
        }
        child = child->next;
    }
    client_printf(sockfd, "\n");
}

// ---------- 新命令：创建新会话 ----------
static void cmd_new(int sockfd) {
    int sess_id = create_session();
    if (sess_id == -1) {
        client_printf(sockfd, "Failed to create new session (max sessions reached)\n");
        return;
    }
    client_printf(sockfd, "s%x\n", sess_id);   // 返回会话句柄，如 s1
}

// ---------- 新命令：关闭会话 ----------
static void cmd_close_session(int sockfd, int sess_id) {
    if (get_session(sess_id)) {
        close_session(sess_id);
        client_printf(sockfd, "OK\n");
    } else {
        client_printf(sockfd, "Session not found\n");
    }
}

// ---------- 处理一行命令 ----------
// 格式：<session-id> <command> [args...]
// 其中 <session-id> 可以是 "new"（创建新会话）或 "s123"（会话句柄）
static void process_line(ClientConnection *client, char *line) {
    int sockfd = client->sockfd;
    // 去除末尾的 \r
    line[strcspn(line, "\r")] = '\0';
    if (strlen(line) == 0) return;

    char *token = strtok(line, " \t");
    if (!token) return;

    // 判断第一个token是否是 "new"
    if (strcmp(token, "new") == 0) {
        cmd_new(sockfd);
        return;
    }

    // 否则第一个token应为会话句柄，如 "s1"
    if (token[0] != 's' && token[0] != 'S') {
        client_printf(sockfd, "Invalid session handle (must start with s)\n");
        return;
    }
    char *endptr;
    long sess_id = strtol(token + 1, &endptr, 16);
    if (*endptr != '\0' || sess_id <= 0) {
        client_printf(sockfd, "Invalid session handle format\n");
        return;
    }

    Session *sess = get_session((int)sess_id);
    if (!sess) {
        client_printf(sockfd, "Session not found or expired\n");
        return;
    }

    // 更新会话最后使用时间
    touch_session((int)sess_id);

    // 解析命令
    char *cmd = strtok(NULL, " \t");
    if (!cmd) {
        client_printf(sockfd, "Missing command\n");
        return;
    }

    if (strcmp(cmd, "quit") == 0 || strcmp(cmd, "exit") == 0) {
        client_printf(sockfd, "Goodbye\n");
        // 关闭客户端连接，将在主循环中处理
        shutdown(sockfd, SHUT_RDWR);
        close(sockfd);
        client->sockfd = -1;
        return;
    } else if (strcmp(cmd, "open") == 0) {
        char *type = strtok(NULL, " \t");
        char *uri = strtok(NULL, "");
        if (!type || !uri) {
            client_printf(sockfd, "Usage: open <xml|html> <uri>\n");
            return;
        }
        while (*uri == ' ' || *uri == '\t') uri++;
        cmd_open(sockfd, sess, type, uri);
    } else if (strcmp(cmd, "xpath") == 0) {
        char *handle = strtok(NULL, " \t");
        char *expr = strtok(NULL, "");
        if (!handle || !expr) {
            client_printf(sockfd, "Usage: xpath <doc-handle|node-handle> <xpath-expr>\n");
            return;
        }
        while (*expr == ' ' || *expr == '\t') expr++;
        cmd_xpath(sockfd, sess, handle, expr);
    } else if (strcmp(cmd, "get") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: get <node-handle>\n");
            return;
        }
        cmd_get(sockfd, sess, handle);
    } else if (strcmp(cmd, "close") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: close <handle>\n");
            return;
        }
        cmd_close(sockfd, sess, handle);
    } else if (strcmp(cmd, "text") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: text <node-handle>\n");
            return;
        }
        cmd_text(sockfd, sess, handle);
    } else if (strcmp(cmd, "parent") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: parent <node-handle>\n");
            return;
        }
        cmd_parent(sockfd, sess, handle);
    } else if (strcmp(cmd, "first-child") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: first-child <node-handle>\n");
            return;
        }
        cmd_first_child(sockfd, sess, handle);
    } else if (strcmp(cmd, "last-child") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: last-child <node-handle>\n");
            return;
        }
        cmd_last_child(sockfd, sess, handle);
    } else if (strcmp(cmd, "next-sibling") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: next-sibling <node-handle>\n");
            return;
        }
        cmd_next_sibling(sockfd, sess, handle);
    } else if (strcmp(cmd, "prev-sibling") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: prev-sibling <node-handle>\n");
            return;
        }
        cmd_prev_sibling(sockfd, sess, handle);
    } else if (strcmp(cmd, "children") == 0) {
        char *handle = strtok(NULL, " \t");
        if (!handle) {
            client_printf(sockfd, "Usage: children <node-handle>\n");
            return;
        }
        cmd_children(sockfd, sess, handle);
    } else if (strcmp(cmd, "close-session") == 0) {
        // 关闭当前会话（会话ID已从token解析）
        cmd_close_session(sockfd, (int)sess_id);
    } else {
        client_printf(sockfd, "Unknown command: %s\n", cmd);
    }
}

// 处理客户端可读事件
static void handle_client_read(ClientConnection *client) {
    char buf[1024];
    ssize_t n = read(client->sockfd, buf, sizeof(buf) - 1);
    if (n <= 0) {
        if (n == 0) {
            printf("Client %d disconnected\n", client->sockfd);
        } else {
            perror("read");
        }
        close(client->sockfd);
        client->sockfd = -1;
        return;
    }

    // 追加到缓冲区
    if (client->inlen + n >= BUFFER_SIZE) {
        // 缓冲区满，关闭连接防止攻击
        close(client->sockfd);
        client->sockfd = -1;
        return;
    }
    memcpy(client->inbuf + client->inlen, buf, n);
    client->inlen += n;

    // 按行处理
    char *line_start = client->inbuf;
    char *p;
    while ((p = memchr(line_start, '\n', client->inlen - (line_start - client->inbuf))) != NULL) {
        *p = '\0';
        process_line(client, line_start);
        line_start = p + 1;
    }
    // 移动剩余数据
    if (line_start > client->inbuf) {
        size_t remaining = client->inlen - (line_start - client->inbuf);
        if (remaining > 0) {
            memmove(client->inbuf, line_start, remaining);
        }
        client->inlen = remaining;
    }
}

// 创建监听socket
static int create_listen_socket(int port) {
    int listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd < 0) {
        perror("socket");
        return -1;
    }

    int opt = 1;
    if (setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
        perror("setsockopt");
        close(listen_fd);
        return -1;
    }

    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = INADDR_ANY;
    addr.sin_port = htons(port);

    if (bind(listen_fd, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
        perror("bind");
        close(listen_fd);
        return -1;
    }

    if (listen(listen_fd, 10) < 0) {
        perror("listen");
        close(listen_fd);
        return -1;
    }

    return listen_fd;
}

// 主程序
int main(int argc, char **argv) {
    argument_parser_t parser;
    argparse_init(&parser, argc, argv, "xpath tcp server", "With ession persistence featured for bash scripting\n");
    int verbosity;
    int listen_port = LISTEN_PORT;
    bool xpath_help;

    argparse_arg_t arg1 = ARGPARSE_COUNT(
        'v', "--verbose", &verbosity, "verbosity level"
    );
    argparse_arg_t arg2 = ARGPARSE_OPTION(
        INT, 'p', "--port", &listen_port, "tcp port to listen (>1999 or 9999)"
    );
    argparse_arg_t arg3 = ARGPARSE_TOGGLE(
        'x', "--xpath-help", &xpath_help, "print out xpath help only"
    ); 
    argparse_add_argument(&parser, &arg1);
    argparse_add_argument(&parser, &arg2);
    argparse_add_argument(&parser, &arg3);
/*
    argparse_arg_t args[] = {
        ARGPARSE_COUNT('v', "--verbose", &verbosity, "verbosity level"),
        ARGPARSE_OPTION(INT, 'p', "--port", &listen_port, "tcp port to listen (>1999 or 9999)"),
        ARGPARSE_TOGGLE('x', "--xpath-help", &xpath_help, "print out xpath help only")
    };
    argparse_add_arguments(&parser, args, 3);
*/
    argparse_parse_args(&parser);
    if (xpath_help) {
	    help_xpath();
	    exit(0);
    }
    if (listen_port < 2000) {
	    listen_port = LISTEN_PORT;
    }

    signal(SIGPIPE, SIG_IGN);
    init_libxml();

    // 初始化全局会话表
    memset(global_sessions, 0, sizeof(global_sessions));
    // 初始化客户端连接表
    for (int i = 0; i < MAX_CLIENTS; i++) {
        clients[i].sockfd = -1;
    }

    listen_fd = create_listen_socket(listen_port);
    if (listen_fd < 0) {
        fprintf(stderr, "Failed to create listening socket\n");
        exit(1);
    }

    printf("xpathd server (with session persistence) listening on port %d\n", listen_port);

    time_t last_cleanup = time(NULL);
    while (1) {
        FD_ZERO(&read_fds);
        FD_SET(listen_fd, &read_fds);
        max_fd = listen_fd;

        for (int i = 0; i < MAX_CLIENTS; i++) {
            if (clients[i].sockfd != -1) {
                FD_SET(clients[i].sockfd, &read_fds);
                if (clients[i].sockfd > max_fd) {
                    max_fd = clients[i].sockfd;
                }
            }
        }

        // 设置select超时，以便定期清理超时会话
        struct timeval tv;
        tv.tv_sec = 10;   // 每10秒检查一次超时
        tv.tv_usec = 0;

        if (select(max_fd + 1, &read_fds, NULL, NULL, &tv) < 0) {
            if (errno == EINTR) continue;
            perror("select");
            continue;
        }

        // 检查新连接
        if (FD_ISSET(listen_fd, &read_fds)) {
            struct sockaddr_in client_addr;
            socklen_t addrlen = sizeof(client_addr);
            int client_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &addrlen);
            if (client_fd < 0) {
                perror("accept");
            } else {
                int i;
                for (i = 0; i < MAX_CLIENTS; i++) {
                    if (clients[i].sockfd == -1) {
                        clients[i].sockfd = client_fd;
                        clients[i].inlen = 0;
                        memset(clients[i].inbuf, 0, BUFFER_SIZE);
                        printf("New client connected from %s:%d, fd=%d\n",
                               inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);
                        break;
                    }
                }
                if (i == MAX_CLIENTS) {
                    fprintf(stderr, "Too many clients, rejecting new connection\n");
                    close(client_fd);
                }
            }
        }

        // 检查每个客户端是否有数据
        for (int i = 0; i < MAX_CLIENTS; i++) {
            if (clients[i].sockfd != -1 && FD_ISSET(clients[i].sockfd, &read_fds)) {
                handle_client_read(&clients[i]);
            }
        }

        // 定期清理超时会话
        time_t now = time(NULL);
        if (now - last_cleanup >= 10) {
            cleanup_expired_sessions();
            last_cleanup = now;
        }
    }

    close(listen_fd);
    cleanup_libxml();
    return 0;
}
