/* cvm_arr.c —— PHP 风格的有序关联数组
 *
 * 设计取舍 (为什么这样一个结构最"对标 PHP"):
 *   PHP 的 array 同时是 list 与 map, 且保留插入顺序。这是它"一把梭"写业务
 *   的核心。我们用一个统一结构实现: 整数键(0..n-1 自动追加) 与 字符串键共存,
 *   底层是哈希表(开放链地址) + 插入顺序数组, 二者共享同一批 ArrEntry 节点。
 *
 *   按引用语义共享(同 Lua table / Python list): 赋值把一个数组的指针交给变量,
 *   多个变量可指向同一个数组并看到彼此的修改。数据全部分配在 arena, 随运行时
 *   销毁, 运行期不单独释放——刻意避免为教学过程引入 GC 复杂度。
 */
#include "cvm_core.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>

/* ---------------- 哈希 ---------------- */
static unsigned long long arr_fnv(const char *s) {
    unsigned long long h = 1469598103934665603ULL;
    while (*s) { h ^= (unsigned char)(*s++); h *= 1099511628211ULL; }
    return h;
}
static unsigned long long arr_ihash(int64_t k) {
    unsigned long long h = 1469598103934665603ULL;
    unsigned char *p = (unsigned char *)&k;
    for (int i = 0; i < (int)sizeof(int64_t); ++i) { h ^= p[i]; h *= 1099511628211ULL; }
    return h;
}
static int bucket_of(int cap, int is_str, const char *skey, int64_t ikey) {
    unsigned long long h = is_str ? arr_fnv(skey) : arr_ihash(ikey);
    return (int)(h % (unsigned long long)cap);
}

/* ---------------- 容量增长 ---------------- */
static void arr_grow_order(Arena *a, Arr *arr) {
    if (arr->count < arr->order_cap) return;
    int nc = arr->order_cap ? arr->order_cap * 2 : 8;
    ArrEntry **no = (ArrEntry **)arena_alloc(a, (size_t)nc * sizeof(ArrEntry *));
    if (arr->count) memcpy(no, arr->order, (size_t)arr->count * sizeof(ArrEntry *));
    arr->order = no;
    arr->order_cap = nc;
}
static void arr_grow_buckets(Arena *a, Arr *arr) {
    int nc = arr->cap * 2;
    ArrEntry **nb = (ArrEntry **)arena_alloc(a, (size_t)nc * sizeof(ArrEntry *));
    memset(nb, 0, (size_t)nc * sizeof(ArrEntry *));
    for (int i = 0; i < arr->count; ++i) {
        ArrEntry *e = arr->order[i];
        int b = bucket_of(nc, e->is_str, e->skey, e->ikey);
        e->hnext = nb[b];
        nb[b] = e;
    }
    arr->buckets = nb;
    arr->cap = nc;
}

/* ---------------- 生命周期 ---------------- */
void arr_init(Arena *a, Arr *arr) {
    arr->cap = 8;
    arr->count = 0;
    arr->buckets = (ArrEntry **)arena_alloc(a, (size_t)arr->cap * sizeof(ArrEntry *));
    memset(arr->buckets, 0, (size_t)arr->cap * sizeof(ArrEntry *));
    arr->order = NULL;
    arr->order_cap = 0;
    arr->next_ikey = 0;
}

/* ---------------- 查找 ---------------- */
ArrEntry *arr_find(int is_str, const char *skey, int64_t ikey, const Arr *arr) {
    int b = bucket_of(arr->cap, is_str, skey, ikey);
    for (ArrEntry *e = arr->buckets[b]; e; e = e->hnext) {
        if (is_str) {
            if (e->is_str && strcmp(e->skey, skey) == 0) return e;
        } else {
            if (!e->is_str && e->ikey == ikey) return e;
        }
    }
    return NULL;
}

static int key_of(Value key, int *is_str, const char **skey, int64_t *ikey) {
    if (key.type == VAL_INT)      { *is_str = 0; *ikey = key.as.i;         *skey = NULL; return 1; }
    if (key.type == VAL_NUM)      { *is_str = 0; *ikey = (int64_t)key.as.num; *skey = NULL; return 1; }
    if (key.type == VAL_STR)      { *is_str = 1; *skey = key.as.str;       *ikey = 0;    return 1; }
    if (key.type == VAL_BOOL)     { *is_str = 0; *ikey = key.as.boolean ? 1 : 0; *skey = NULL; return 1; }
    return 0;
}

ArrEntry *arr_lookup(Runtime *rt, Arr *arr, Value key) {
    int is_str; const char *sk; int64_t ik;
    if (!key_of(key, &is_str, &sk, &ik)) {
        snprintf(rt->errbuf, sizeof(rt->errbuf), "数组索引必须是数字、字符串或布尔");
        rt->has_error = 1;
        return NULL;
    }
    return arr_find(is_str, sk, ik, arr);
}

/* ---------------- 写入 ---------------- */
void arr_set(Arena *a, Arr *arr, int is_str, const char *skey, int64_t ikey, Value v) {
    ArrEntry *e = arr_find(is_str, skey, ikey, arr);
    if (e) { e->value = v; return; }                 /* 已存在: 仅更新值 */
    if (arr->count + 1 > arr->cap) arr_grow_buckets(a, arr);
    e = (ArrEntry *)arena_alloc(a, sizeof(ArrEntry));
    e->is_str = is_str;
    e->skey   = is_str ? (char *)skey : NULL;
    e->ikey   = is_str ? 0 : ikey;
    e->value  = v;
    int b = bucket_of(arr->cap, is_str, skey, ikey);
    e->hnext = arr->buckets[b];
    arr->buckets[b] = e;
    arr_grow_order(a, arr);
    arr->order[arr->count++] = e;
    if (!is_str && ikey >= arr->next_ikey) arr->next_ikey = ikey + 1;
}

void arr_append(Arena *a, Arr *arr, Value v) {
    arr_set(a, arr, 0, NULL, arr->next_ikey, v);
}

void arr_set_from_key(Runtime *rt, Arr *arr, Value key, Value v) {
    int is_str; const char *sk; int64_t ik;
    if (!key_of(key, &is_str, &sk, &ik)) {
        snprintf(rt->errbuf, sizeof(rt->errbuf), "数组键必须是数字、字符串或布尔");
        rt->has_error = 1;
        return;
    }
    arr_set(&rt->arena, arr, is_str, sk, ik, v);
}

/* ---------------- 删除 ---------------- */
int arr_del(Arr *arr, Value key) {
    int is_str; const char *sk; int64_t ik;
    if (!key_of(key, &is_str, &sk, &ik)) return 0;   /* 非法键当作"不存在" */
    int b = bucket_of(arr->cap, is_str, sk, ik);
    ArrEntry *prev = NULL, *e = arr->buckets[b];
    while (e) {
        int hit = is_str ? (e->is_str && strcmp(e->skey, sk) == 0)
                         : (!e->is_str && e->ikey == ik);
        if (hit) {
            if (prev) prev->hnext = e->hnext;
            else      arr->buckets[b] = e->hnext;
            for (int i = 0; i < arr->count; ++i)
                if (arr->order[i] == e) {
                    memmove(&arr->order[i], &arr->order[i + 1],
                            (size_t)(arr->count - i - 1) * sizeof(ArrEntry *));
                    break;
                }
            arr->count--;
            return 1;
        }
        prev = e; e = e->hnext;
    }
    return 0;
}

/* ---------------- 读取辅助 ---------------- */
int arr_len(const Arr *arr) { return arr->count; }
ArrEntry *arr_at(const Arr *arr, int idx) {
    if (idx < 0 || idx >= arr->count) return NULL;
    return arr->order[idx];
}

/* ---------------- 派生数组 ---------------- */
Arr *arr_copy(Arena *a, const Arr *src) {
    Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
    arr_init(a, d);
    for (int i = 0; i < src->count; ++i) {
        ArrEntry *e = src->order[i];
        arr_set(a, d, e->is_str, e->skey, e->ikey, e->value);
    }
    return d;
}
Arr *arr_keys(Arena *a, const Arr *src) {
    Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
    arr_init(a, d);
    for (int i = 0; i < src->count; ++i) {
        ArrEntry *e = src->order[i];
        Value k = e->is_str ? val_str(e->skey) : val_int(e->ikey);
        arr_append(a, d, k);
    }
    return d;
}
Arr *arr_values(Arena *a, const Arr *src) {
    Arr *d = (Arr *)arena_alloc(a, sizeof(Arr));
    arr_init(a, d);
    for (int i = 0; i < src->count; ++i)
        arr_append(a, d, src->order[i]->value);
    return d;
}