/*
 * cmd_struct.c - 结构体类型与出参构造命令模块
 *
 *   ptype [<struct_type_name>]  列出全部/单个结构体布局
 *   struct <typename> <name> = <value>  构造结构体出参副本
 *   <type> <name> = <value>     构造标量出参副本（如 double value = 0）
 *
 * 出参副本分配在调试进程内，'call' 的 '&name' 引用会在调用时临时
 * 搬运到目标进程栈上，调用完成后回拷覆盖副本，目标进程内无残留。
 */

#include "lnxall_trace.h"

/**
 * @brief 按名称（忽略大小写）查找 DWARF 解析的结构体类型
 *
 * structs[] 的名称不带 'struct ' 前缀（见 dwarf_strip_type_prefix）。
 *
 * @param[in] name 结构体类型名
 * @return 结构体在 g_dbg.structs[] 中的下标，未找到返回 -1
 */
int find_struct_by_name(const char *name)
{
    int i = 0;

    if (!name) return -1;
    for (i = 0; i < g_dbg.struct_count; i++) {
        if (g_dbg.structs[i].name[0] &&
            strcasecmp(g_dbg.structs[i].name, name) == 0)
            return i;
    }
    return -1;
}

/**
 * @brief 列出或打印结构体类型布局：ptype [<struct_type_name>]
 *
 * 无参数时列出全部已知结构体类型；带参数时按名称（忽略大小写）
 * 打印指定结构体的字段偏移/名称/大小。
 *
 * @param[in] tname 结构体类型名（可为 NULL/空，表示列出全部）
 * @return 0 成功，-1 失败
 */
int cmd_ptype(const char *tname)
{
    int i = 0;
    int j = 0;

    if (tname == NULL || *tname == '\0') {
        /* list all known struct types */
        if (g_dbg.struct_count == 0) {
            printf("No struct types available (target may lack DWARF debug info)\n");
            return 0;
        }
        printf("Known struct types (%d):\n", g_dbg.struct_count);
        for (i = 0; i < g_dbg.struct_count; i++) {
            printf("  %-32s  size=%lu  fields=%d\n",
                   g_dbg.structs[i].name,
                   g_dbg.structs[i].size,
                   g_dbg.structs[i].field_count);
        }
        return 0;
    }

    /* show specific struct layout */
    for (i = 0; i < g_dbg.struct_count; i++) {
        if (strcasecmp(g_dbg.structs[i].name, tname) == 0) {
            struct_info_t *si = &g_dbg.structs[i];
            printf("struct %s  (size=%lu bytes, %d fields):\n",
                   si->name, si->size, si->field_count);
            for (j = 0; j < si->field_count; j++) {
                struct_field_t *f = &si->fields[j];
                printf("  +0x%-4lx  %-40s  size=%-4lu\n",
                       f->offset, f->name, f->size);
            }
            return 0;
        }
    }
    printf("Error: Struct type '%s' not found\n", tname);
    return -1;
}

/**
 * @brief 构造出参(scratch)符号：'<type> <name> = <value>'
 *
 * 在调试进程内通过 malloc 分配 <type> 大小的内存副本并初始化，
 * 将 <name> 注册为本地符号（最低优先级）。副本地址仅用于调试进程
 * 内的读写，绝不会直接传给目标；'call' 的 '&name' 引用会在调用时
 * 把副本临时搬运到目标进程栈上构建出参，调用完成后回拷覆盖副本，
 * 目标进程内不残留任何堆内存。副本在 detach/attach 时释放。
 *
 * 类型支持：char/short/int/long/float/double（含 unsigned 前缀），
 * 以及 'struct <typename> <name> = 0' 形式的结构体副本（按 DWARF 解析
 * 的结构体大小分配并清零，字段可用 d/m 直接读写）。
 *
 * 示例：double value = 0
 *       struct node_t node = 0
 *
 * @param[in] spec 用户输入的命令行
 * @return 0 成功，-1 失败
 */
int cmd_create_local(const char *spec)
{
    unsigned long size = 0;
    const char *cp = spec;
    char name[MAX_NAME_LEN];
    unsigned long init_val = 0;
    unsigned long long raw = 0;
    symbol_t *sym;
    unsigned char *copy;
    int n = 0;

    if (!g_dbg.is_attached) {
        printf("Error: Not attached.\n");
        return -1;
    }

    while (*cp && isspace((unsigned char)*cp)) cp++;

    /* struct <typename> <name> = <value>: allocate a debugger-side copy
     * sized from the DWARF-resolved struct type.  Only '= 0' (zeroing) is
     * meaningful; any non-zero init is ignored with a notice. */
    if (strncasecmp(cp, "struct", 6) == 0 &&
        (cp[6] == ' ' || cp[6] == '\t')) {
        cp += 6;
        while (*cp && isspace((unsigned char)*cp)) cp++;

        char stypename[64] = {0};
        if (sscanf(cp, "%63s%n", stypename, &n) != 1) {
            printf("Error: struct type name missing\n");
            return -1;
        }
        cp += n;
        while (*cp && isspace((unsigned char)*cp)) cp++;

        if (sscanf(cp, "%255s%n", name, &n) != 1) {
            printf("Error: variable name missing\n");
            return -1;
        }
        cp += n;
        while (*cp && isspace((unsigned char)*cp)) cp++;

        unsigned long init_val_u = 0;
        if (*cp == '=') {
            cp++;
            while (*cp && isspace((unsigned char)*cp)) cp++;
            if (parse_int_value(cp, &init_val_u) != 0) {
                printf("Error: invalid initial value '%s'\n", cp);
                return -1;
            }
        }

        int si_idx = find_struct_by_name(stypename);
        if (si_idx < 0) {
            printf("Error: struct type '%s' not found (use 'ptype' to list)\n",
                   stypename);
            return -1;
        }
        struct_info_t *si = &g_dbg.structs[si_idx];
        size = si->size;

        if (find_symbol_any(name)) {
            printf("Error: symbol '%s' already exists\n", name);
            return -1;
        }
        if (g_dbg.local_sym_count >= MAX_LOCAL_SYMS) {
            printf("Error: local symbol table full (%d)\n", MAX_LOCAL_SYMS);
            return -1;
        }

        copy = malloc(size);
        if (copy == NULL) {
            printf("Error: malloc(%lu) failed in debugger for '%s'\n",
                   size, name);
            return -1;
        }
        memset(copy, 0, size);   /* struct copies are zero-initialized */

        sym = &g_dbg.local_sym_table[g_dbg.local_sym_count++];
        safe_strcpy(sym->name, name, MAX_NAME_LEN);
        sym->addr = (unsigned long)(uintptr_t)copy;   /* DEBUGGER address */
        sym->size = size;
        sym->type = SYM_OBJECT;
        sym->kind = SYM_KIND_STRUCT;
        sym->struct_idx = si_idx;
        sym->dwarf_type_ref = 0;
        sym->has_dwarf = 1;

        if (g_dbg.verbose)
            printf("Created out-param struct '%s' (%lu bytes, struct %s) "
                   "as debugger copy @ %p%s\n",
                   name, size, si->name[0] ? si->name : "<anonymous>",
                   (void *)copy,
                   (init_val_u != 0) ? "  (non-zero init ignored; zeroed)" : "");
        return 0;
    }

    if (base_type_lookup(cp, &size) != 0) {
        printf("Error: unrecognized type in '<type> <name> = <value>'\n");
        return -1;
    }

    /* skip type token(s): one word, or two for "unsigned <type>"
     * NOTE: use a receiving conversion ('%31s') — '%*s%n' returns 0 on
     * success (suppressed assignments / '%n' do not count), which made
     * the old '!= 1' check fail for every input. */
    {
        char tok[32];
        if (sscanf(cp, "%31s%n", tok, &n) != 1) {
            printf("Error: type token parse failed\n");
            return -1;
        }
    }
    cp += n;
    while (*cp && isspace((unsigned char)*cp)) cp++;
    if (strncmp(cp, "unsigned", 8) == 0) {
        char tok[32];
        if (sscanf(cp, "%31s%n", tok, &n) != 1) {
            printf("Error: 'unsigned' type token parse failed\n");
            return -1;
        }
        cp += n;
        while (*cp && isspace((unsigned char)*cp)) cp++;
    }

    /* variable name */
    if (sscanf(cp, "%255s%n", name, &n) != 1) {
        printf("Error: variable name missing\n");
        return -1;
    }
    cp += n;
    while (*cp && isspace((unsigned char)*cp)) cp++;

    /* '=' <value> */
    if (*cp != '=') {
        printf("Error: expected '=' in '<type> <name> = <value>'\n");
        return -1;
    }
    cp++;
    while (*cp && isspace((unsigned char)*cp)) cp++;
    if (parse_int_value(cp, &init_val) != 0) {
        printf("Error: invalid initial value '%s'\n", cp);
        return -1;
    }

    /* name collision check (target / so / local) */
    if (find_symbol_any(name)) {
        printf("Error: symbol '%s' already exists\n", name);
        return -1;
    }
    if (g_dbg.local_sym_count >= MAX_LOCAL_SYMS) {
        printf("Error: local symbol table full (%d)\n", MAX_LOCAL_SYMS);
        return -1;
    }

    /* Allocate the value copy in the DEBUGGER process, never in the
     * target: no injected malloc, no thread stop, no target-heap leak.
     * On each 'call ... &name' the copy is staged onto the target stack
     * and the result is copied back afterwards (see cmd_call_function). */
    copy = malloc(size);
    if (copy == NULL) {
        printf("Error: malloc(%lu) failed in debugger for '%s'\n",
               size, name);
        return -1;
    }
    memset(copy, 0, size);
    raw = (unsigned long long)init_val;
    memcpy(copy, &raw, size <= sizeof(raw) ? size : sizeof(raw));

    /* register the scratch symbol (lowest lookup priority) */
    sym = &g_dbg.local_sym_table[g_dbg.local_sym_count++];
    safe_strcpy(sym->name, name, MAX_NAME_LEN);
    sym->addr = (unsigned long)(uintptr_t)copy;   /* DEBUGGER address */
    sym->size = size;
    sym->type = SYM_OBJECT;
    sym->kind = SYM_KIND_INT;
    sym->struct_idx = -1;
    sym->dwarf_type_ref = 0;
    sym->has_dwarf = 0;

    if (g_dbg.verbose)
        printf("Created out-param '%s' (%lu byte%s) as debugger copy @ %p\n",
               name, size, size > 1 ? "s" : "", (void *)copy);
    return 0;
}
