/*
 * cmd_m.c - m 命令模块（修改目标数据）
 *
 *   m <var>=<value>             写变量（int / float / double 自动识别）
 *   m <struct>.<field>=<value>  写结构体成员（支持嵌套与数组下标）
 *   m <addr>,<len>=<hex>        写目标内存：hex 字节串循环填充 <len> 字节
 *
 * 依赖 cmd_d.c 的 resolve_struct_field_path（字段路径解析）。
 */

#include "lnxall_trace.h"

/* 与 parse_int_value 语义一致但按 64 位承载：ARM32(ILP32) 下 unsigned long
 * 仅 4 字节，8 字节整型变量值会截断，此处统一用 strtoull。 */
static int parse_int_value64(const char *s, unsigned long long *out)
{
    if (!s || !*s) return -1;
    char *end;
    *out = strtoull(s, &end, 0);
    if (*end != '\0' && *end != ',' && *end != ')') return -1;
    return 0;
}

/**
 * @brief 修改目标进程内存：m <addr>,<len>=<hex_bytes>
 *
 * 值串解析为字节序列后循环重复填充至 <len> 长度并整体写入，
 * 便于按固定模式填充（如 m 0x1234,64=0x00 清零 64 字节）。
 *
 * @param[in] spec     命令参数，格式 "<addr>,<len>"
 * @param[in] val_str  十六进制字节串（不含等号）
 * @return 0 成功，-1 失败
 */
int cmd_write_memory(const char *spec, const char *val_str)
{
    unsigned long addr = 0;
    unsigned long len  = 0;
    char *ep = NULL;

    if (!g_dbg.is_attached) {
        printf("Error: Not attached.\n");
        return -1;
    }
    addr = strtoul(spec, &ep, 0);
    if (ep == spec || *ep != ',') {
        printf("Usage: m <addr>,<len>=<hex_bytes>   (e.g. m 0x40a000,8=0x1122334455667788)\n");
        return -1;
    }
    len = strtoul(ep + 1, &ep, 0);
    if (*ep != '\0' || len == 0) {
        printf("Usage: m <addr>,<len>=<hex_bytes>\n");
        return -1;
    }
    if (len > MAX_MEM_OPS_SIZE) {
        printf("Error: len %lu exceeds max write size %d\n", len, MAX_MEM_OPS_SIZE);
        return -1;
    }

    unsigned char *bytes = malloc(len);
    if (bytes == NULL) {
        printf("Error: out of memory (%lu bytes)\n", len);
        return -1;
    }
    int nbytes = parse_hex_bytes(val_str, bytes, len);
    if (nbytes <= 0) {
        free(bytes);
        printf("Error: invalid hex byte string '%s'\n", val_str);
        return -1;
    }
    for (size_t i = (size_t)nbytes; i < len; i++) {
        bytes[i] = bytes[i % (size_t)nbytes];
    }

    if (ptrace_stop_target(g_dbg.target_pid) != 0) {
        free(bytes);
        return -1;
    }
    stop_all_threads();
    int rc = mem_write(g_dbg.target_pid, addr, bytes, len);
    if (rc != 0) {
        printf("Error: cannot write target memory at 0x%lx\n", addr);
    } else {
        if (g_dbg.verbose)
            printf("Wrote %lu bytes @ 0x%lx (pattern: %s)\n", len, addr, val_str);
    }
    free(bytes);
    resume_all_threads();
    ptrace_continue_target(g_dbg.target_pid);
    return rc;
}

int cmd_write_var(const char *var_name, const char *val_str)
{
    if (!g_dbg.is_attached) {
        printf("Error: Not attached.\n"); return -1;
    }

    unsigned long var_addr  = 0;
    unsigned long var_size  = 0;

    /* --- handle struct.field access --- */
    if (strchr(var_name, '.')) {
        symbol_t *base_sym = NULL;
        if (resolve_struct_field_path(var_name, &var_addr, &var_size,
                                      NULL, NULL, NULL, NULL,
                                      &base_sym) != 0)
            return -1;

        /* Detect float format */
        int is_float = 0;
        for (const char *p = val_str; *p; p++) {
            if (*p == '.' || *p == 'e' || *p == 'E') { is_float = 1; break; }
        }

        /* 8 字节承载：ARM32(ILP32) 下 unsigned long 仅 4B，double/8 字节
         * 整型必须用 64 位类型，否则 memcpy 越界、写入只落低 4 字节。 */
        unsigned long long new_value = 0;
        if (is_float && (var_size == 4 || var_size == 8)) {
            char *end;
            double dval = strtod(val_str, &end);
            if (*end != '\0') {
                printf("Error: Invalid float value '%s'\n", val_str); return -1;
            }
            if (var_size == 4) { float f = (float)dval; memcpy(&new_value, &f, 4); }
            else               { memcpy(&new_value, &dval, 8); }
        } else {
            if (parse_int_value64(val_str, &new_value) != 0) {
                printf("Error: Invalid value '%s'\n", val_str); return -1;
            }
        }

        if (base_sym && is_local_symbol(base_sym)) {
            /* debugger-side out-param struct copy: update in place,
             * no ptrace, no thread stop */
            size_t ncpy = (var_size <= 8) ? (size_t)var_size : 8;
            memcpy((void *)var_addr, &new_value, ncpy);
            if (var_size > 8)
                printf("Warning: only wrote first 8 bytes of %lu-byte field\n",
                       var_size);
            printf("%s := %llu (0x%llx)\n", var_name, new_value, new_value);
            return 0;
        }

        if (ptrace_stop_target(g_dbg.target_pid) != 0) return -1;
        stop_all_threads();

        /* 按实际长度整段写：mem_write 内部逐 word 读-改-写（兼容非对齐与
         * 部分字），同时覆盖 arm32(word=4) 与 aarch64(word=8)；不再手工
         * RMW（word 大小差异正是旧实现的越界/截断根源）。 */
        {
            size_t ncpy = (var_size <= 8) ? (size_t)var_size : 8;
            if (mem_write(g_dbg.target_pid, var_addr, &new_value, ncpy) != 0) {
                resume_all_threads();
                ptrace_continue_target(g_dbg.target_pid); return -1;
            }
            if (var_size > 8)
                printf("Warning: only wrote first 8 bytes of %lu-byte field\n",
                       var_size);
        }

        resume_all_threads();
        ptrace_continue_target(g_dbg.target_pid);
        printf("%s := %llu (0x%llx)\n", var_name, new_value, new_value);
        return 0;
    }

    /* --- regular variable write --- */
    symbol_t *sym = find_symbol(var_name, SYM_OBJECT);
    if (!sym) {
        sym = find_symbol_any(var_name);
        if (sym && sym->type != SYM_OBJECT) {
            printf("Error: '%s' is not a variable\n", var_name);
            return -1;
        }
        printf("Error: Variable '%s' not found\n", var_name);
        return -1;
    }

    var_addr = sym->addr;
    var_size = sym->size;

    /* Detect float format: contains '.' or 'e'/'E' */
    int is_float = 0;
    for (const char *p = val_str; *p; p++) {
        if (*p == '.' || *p == 'e' || *p == 'E') { is_float = 1; break; }
    }

    /* 8 字节承载：ARM32(ILP32) 下 unsigned long 仅 4B，double/8 字节整型
     * 必须用 64 位类型，否则 memcpy 越界、写入只落低 4 字节。 */
    unsigned long long new_value;
    double     dval = 0;

    if (is_float && (sym->size == 4 || sym->size == 8)) {
        char *end;
        dval = strtod(val_str, &end);
        if (*end != '\0') {
            printf("Error: Invalid float value '%s'\n", val_str); return -1;
        }
        if (sym->size == 4) {
            float f = (float)dval;
            memcpy(&new_value, &f, 4);
        } else {
            memcpy(&new_value, &dval, 8);
        }
    } else {
        if (parse_int_value64(val_str, &new_value) != 0) {
            printf("Error: Invalid value '%s'\n", val_str); return -1;
        }
    }

    DBG("cmd_write_var: '%s' found, addr=0x%lx, size=%lu, is_float=%d",
        var_name, sym->addr, sym->size, is_float);

    if (is_local_symbol(sym)) {
        /* Debugger-side out-param copy: update in place, no ptrace, no
         * thread stop (the target never sees this memory). */
        size_t ncpy = (sym->size <= 8) ? (size_t)sym->size : 8;
        memcpy((void *)sym->addr, &new_value, ncpy);
        if (sym->size > 8)
            printf("Warning: only wrote first 8 bytes of %lu-byte variable\n",
                   sym->size);
        goto write_echo;
    }

    if (ptrace_stop_target(g_dbg.target_pid) != 0) return -1;
    stop_all_threads();

    /* 按实际长度整段写：mem_write 内部逐 word 读-改-写（兼容非对齐与部分
     * 字），同时覆盖 arm32(word=4) 与 aarch64(word=8)；不再手工 RMW。 */
    {
        size_t ncpy = (sym->size <= 8) ? (size_t)sym->size : 8;
        if (mem_write(g_dbg.target_pid, sym->addr, &new_value, ncpy) != 0) {
            resume_all_threads();
            ptrace_continue_target(g_dbg.target_pid); return -1;
        }
        if (sym->size > 8)
            printf("Warning: only wrote first 8 bytes of %lu-byte variable\n",
                   sym->size);
    }

    resume_all_threads();
    ptrace_continue_target(g_dbg.target_pid);

write_echo:
    /* Human-friendly echo */
    if (is_float && sym->size == 4) {
        float f; memcpy(&f, &new_value, 4);
        printf("%s := %f (float)\n", var_name, (double)f);
    } else if (is_float && sym->size == 8) {
        double d; memcpy(&d, &new_value, 8);
        printf("%s := %.10g (double)\n", var_name, d);
    } else {
        printf("%s := %llu (0x%llx)\n", var_name, new_value, new_value);
    }
    return 0;
}
