#include <stdint.h>
#include <windows.h>

#define UNDEFINED 0x7FFF800000000001ULL
#define NULL_VAL  0x7FFF800000000002ULL
#define TRUE_VAL  0x7FFF000000000001ULL
#define FALSE_VAL 0x7FFF000000000000ULL
#define STRING_TAG 0x7FFC000000000000ULL
#define ARRAY_TAG  0x7FFB000000000000ULL
#define OBJECT_TAG 0x7FFA000000000000ULL
#define TAG_MASK   0xFFFF000000000000ULL
#define PTR_MASK   0x0000FFFFFFFFFFFFULL

typedef struct { uint32_t len; uint32_t hash; uint8_t data[]; } JsString;
typedef struct { uint32_t len; uint32_t capacity; uint64_t data[]; } JsArray;
typedef struct { uint64_t key; uint64_t value; } ObjectEntry;
typedef struct { uint32_t size; uint32_t capacity; ObjectEntry entries[]; } JsObject;

static double bits_to_val(uint64_t bits) {
    double result;
    __asm__ volatile ("movq %1, %0" : "=x"(result) : "r"(bits));
    return result;
}

static uint64_t val_to_bits(double val) {
    uint64_t bits;
    __asm__ volatile ("movq %1, %0" : "=r"(bits) : "x"(val));
    return bits;
}

static void* js_malloc(size_t size) {
    return HeapAlloc(GetProcessHeap(), 0, size);
}

static void* js_realloc(void* ptr, size_t size) {
    return HeapReAlloc(GetProcessHeap(), 0, ptr, size);
}

double js_string_new(const char* data, uint32_t len);
double js_string_from_static(const char* data);

static void write_str(const char* s) {
    int len = 0;
    while (s[len]) len++;
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), s, len, NULL, NULL);
}

static void write_buf(const char* buf, int len) {
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, len, NULL, NULL);
}

static int int_to_str(int num, char* buf) {
    if (num == 0) {
        buf[0] = '0';
        return 1;
    }
    
    int neg = 0;
    if (num < 0) {
        neg = 1;
        num = -num;
    }
    
    char tmp[32];
    int i = 0;
    while (num > 0) {
        tmp[i++] = '0' + (num % 10);
        num /= 10;
    }
    
    int len = 0;
    if (neg) buf[len++] = '-';
    while (i > 0) {
        buf[len++] = tmp[--i];
    }
    return len;
}

double js_print(double val) {
    char buf[256];
    uint64_t bits = val_to_bits(val);
    uint64_t tag = bits & TAG_MASK;
    
    int len = 0;
    
    if (bits == UNDEFINED) {
        write_str("undefined\n");
    }
    else if (bits == NULL_VAL) {
        write_str("null\n");
    }
    else if (bits == TRUE_VAL) {
        write_str("true\n");
    }
    else if (bits == FALSE_VAL) {
        write_str("false\n");
    }
    else if (tag == STRING_TAG) {
        JsString* s = (JsString*)(bits & PTR_MASK);
        if (s) {
            write_buf((const char*)s->data, s->len);
            write_str("\n");
        } else {
            write_str("null\n");
        }
    }
    else if (tag == ARRAY_TAG) {
        JsArray* arr = (JsArray*)(bits & PTR_MASK);
        if (arr) {
            buf[len++] = '[';
            write_buf(buf, len);
            len = 0;
            
            for (uint32_t i = 0; i < arr->len; i++) {
                if (i > 0) {
                    write_str(", ");
                }
                
                uint64_t elem_bits = arr->data[i];
                if ((elem_bits & TAG_MASK) == STRING_TAG) {
                    JsString* s = (JsString*)(elem_bits & PTR_MASK);
                    if (s) {
                        write_buf((const char*)s->data, s->len);
                    } else {
                        write_str("null");
                    }
                }
                else if (elem_bits == UNDEFINED) {
                    write_str("undefined");
                } else if (elem_bits == NULL_VAL) {
                    write_str("null");
                } else if (elem_bits == TRUE_VAL) {
                    write_str("true");
                } else if (elem_bits == FALSE_VAL) {
                    write_str("false");
                } else {
                    double elem_val = bits_to_val(elem_bits);
                    int num = (int)elem_val;
                    len = int_to_str(num, buf);
                    write_buf(buf, len);
                    len = 0;
                }
            }
            write_str("]\n");
        } else {
            write_str("null\n");
        }
    }
    else if (tag == OBJECT_TAG) {
        JsObject* obj = (JsObject*)(bits & PTR_MASK);
        if (obj) {
            write_str("{");
            
            for (uint32_t i = 0; i < obj->size; i++) {
                if (i > 0) {
                    write_str(", ");
                }
                
                write_str("\"key\": ");
                
                uint64_t val_bits = obj->entries[i].value;
                if (val_bits == UNDEFINED) {
                    write_str("undefined");
                } else if (val_bits == NULL_VAL) {
                    write_str("null");
                } else if (val_bits == TRUE_VAL) {
                    write_str("true");
                } else if (val_bits == FALSE_VAL) {
                    write_str("false");
                } else {
                    double val = bits_to_val(val_bits);
                    int num = (int)val;
                    len = int_to_str(num, buf);
                    write_buf(buf, len);
                    len = 0;
                }
            }
            write_str("}\n");
        } else {
            write_str("null\n");
        }
    }
    else {
        // 数字：直接从 double 转换
        int num = (int)val;
        len = int_to_str(num, buf);
        buf[len++] = '\n';
        write_buf(buf, len);
    }
    
    return val;
}

double js_array_new(uint32_t capacity) {
    if (capacity == 0) capacity = 8;
    JsArray* arr = (JsArray*)js_malloc(sizeof(JsArray) + capacity * 8);
    if (!arr) return bits_to_val(UNDEFINED);
    arr->len = 0;
    arr->capacity = capacity;
    return bits_to_val(ARRAY_TAG | (uint64_t)arr);
}

double js_array_push(double arr_val, double value) {
    uint64_t arr_bits = val_to_bits(arr_val);
    if ((arr_bits & TAG_MASK) != ARRAY_TAG) return arr_val;
    
    JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
    if (!arr) return arr_val;
    
    if (arr->len >= arr->capacity) {
        uint32_t new_cap = arr->capacity * 2;
        JsArray* new_arr = (JsArray*)js_realloc(arr, sizeof(JsArray) + new_cap * 8);
        if (!new_arr) return arr_val;
        new_arr->capacity = new_cap;
        arr = new_arr;
    }
    
    arr->data[arr->len++] = val_to_bits(value);
    return bits_to_val(ARRAY_TAG | (uint64_t)arr);
}

double js_array_get(double arr_val, double idx_val) {
    uint64_t arr_bits = val_to_bits(arr_val);
    if ((arr_bits & TAG_MASK) != ARRAY_TAG) return bits_to_val(UNDEFINED);
    JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
    int idx = (int)idx_val;
    if (!arr || idx < 0 || (uint32_t)idx >= arr->len) return bits_to_val(UNDEFINED);
    return bits_to_val(arr->data[idx]);
}

double js_array_set(double arr_val, double idx_val, double value) {
    uint64_t arr_bits = val_to_bits(arr_val);
    if ((arr_bits & TAG_MASK) != ARRAY_TAG) return value;
    
    JsArray* arr = (JsArray*)(arr_bits & PTR_MASK);
    uint32_t idx = (uint32_t)idx_val;
    
    if (!arr) return value;
    
    if (idx >= arr->len) {
        while (arr->len <= idx) {
            if (arr->len >= arr->capacity) {
                uint32_t new_cap = arr->capacity * 2;
                JsArray* new_arr = (JsArray*)js_realloc(arr, sizeof(JsArray) + new_cap * 8);
                if (!new_arr) return value;
                new_arr->capacity = new_cap;
                arr = new_arr;
            }
            arr->data[arr->len++] = UNDEFINED;
        }
    }
    
    arr->data[idx] = val_to_bits(value);
    return value;
}

double js_object_new() {
    JsObject* obj = (JsObject*)js_malloc(sizeof(JsObject) + 8 * sizeof(ObjectEntry));
    if (!obj) return bits_to_val(UNDEFINED);
    obj->size = 0;
    obj->capacity = 8;
    return bits_to_val(OBJECT_TAG | (uint64_t)obj);
}

double js_object_get(double obj_val, double key_val) {
    uint64_t obj_bits = val_to_bits(obj_val);
    uint64_t key_bits = val_to_bits(key_val);
    
    if ((obj_bits & TAG_MASK) != OBJECT_TAG) return bits_to_val(UNDEFINED);
    
    JsObject* obj = (JsObject*)(obj_bits & PTR_MASK);
    if (!obj) return bits_to_val(UNDEFINED);
    
    for (uint32_t i = 0; i < obj->size; i++) {
        if (obj->entries[i].key == key_bits) {
            return bits_to_val(obj->entries[i].value);
        }
    }
    
    return bits_to_val(UNDEFINED);
}

double js_object_set(double obj_val, double key_val, double value_val) {
    uint64_t obj_bits = val_to_bits(obj_val);
    uint64_t key_bits = val_to_bits(key_val);
    uint64_t value_bits = val_to_bits(value_val);
    
    if ((obj_bits & TAG_MASK) != OBJECT_TAG) return value_val;
    
    JsObject* obj = (JsObject*)(obj_bits & PTR_MASK);
    if (!obj) return value_val;
    
    for (uint32_t i = 0; i < obj->size; i++) {
        if (obj->entries[i].key == key_bits) {
            obj->entries[i].value = value_bits;
            return value_val;
        }
    }
    
    if (obj->size >= obj->capacity) {
        uint32_t new_cap = obj->capacity * 2;
        JsObject* new_obj = (JsObject*)js_realloc(obj, sizeof(JsObject) + new_cap * sizeof(ObjectEntry));
        if (!new_obj) return value_val;
        new_obj->capacity = new_cap;
        obj = new_obj;
    }
    
    obj->entries[obj->size].key = key_bits;
    obj->entries[obj->size].value = value_bits;
    obj->size++;
    
    return value_val;
}

double js_typeof(double val) {
    uint64_t bits = val_to_bits(val);
    uint64_t tag = bits & TAG_MASK;
    
    static char* type_undefined = "undefined";
    static char* type_object = "object";
    static char* type_boolean = "boolean";
    static char* type_string = "string";
    static char* type_number = "number";
    
    if (bits == UNDEFINED) return js_string_from_static(type_undefined);
    if (bits == NULL_VAL) return js_string_from_static(type_object);
    if (bits == TRUE_VAL || bits == FALSE_VAL) return js_string_from_static(type_boolean);
    if (tag == STRING_TAG) return js_string_from_static(type_string);
    if (tag == ARRAY_TAG) return js_string_from_static(type_object);
    if (tag == OBJECT_TAG) return js_string_from_static(type_object);
    return js_string_from_static(type_number);
}

double js_string_new(const char* data, uint32_t len) {
    if (!data || len == 0) return bits_to_val(UNDEFINED);
    
    JsString* str = (JsString*)js_malloc(sizeof(JsString) + len);
    if (!str) return bits_to_val(UNDEFINED);
    
    str->len = len;
    str->hash = 0;
    for (uint32_t i = 0; i < len; i++) {
        str->data[i] = data[i];
        str->hash = str->hash * 31 + data[i];
    }
    
    return bits_to_val(STRING_TAG | (uint64_t)str);
}

double js_string_from_static(const char* data) {
    if (!data) return bits_to_val(UNDEFINED);
    
    int len = 0;
    while (data[len]) len++;
    
    return js_string_new(data, (uint32_t)len);
}

double js_string_concat(double a_val, double b_val) {
    uint64_t a_bits = val_to_bits(a_val);
    uint64_t b_bits = val_to_bits(b_val);
    
    if ((a_bits & TAG_MASK) != STRING_TAG || (b_bits & TAG_MASK) != STRING_TAG) {
        return bits_to_val(UNDEFINED);
    }
    
    JsString* a_str = (JsString*)(a_bits & PTR_MASK);
    JsString* b_str = (JsString*)(b_bits & PTR_MASK);
    
    if (!a_str || !b_str) return bits_to_val(UNDEFINED);
    
    uint32_t new_len = a_str->len + b_str->len;
    JsString* result = (JsString*)js_malloc(sizeof(JsString) + new_len);
    if (!result) return bits_to_val(UNDEFINED);
    
    result->len = new_len;
    result->hash = 0;
    
    for (uint32_t i = 0; i < a_str->len; i++) {
        result->data[i] = a_str->data[i];
        result->hash = result->hash * 31 + a_str->data[i];
    }
    
    for (uint32_t i = 0; i < b_str->len; i++) {
        result->data[a_str->len + i] = b_str->data[i];
        result->hash = result->hash * 31 + b_str->data[i];
    }
    
    return bits_to_val(STRING_TAG | (uint64_t)result);
}

double js_add(double a, double b) {
    uint64_t a_bits = val_to_bits(a);
    uint64_t b_bits = val_to_bits(b);
    
    // 如果两个都是字符串，进行拼接
    if ((a_bits & TAG_MASK) == STRING_TAG && (b_bits & TAG_MASK) == STRING_TAG) {
        return js_string_concat(a, b);
    }
    
    // 否则进行数字加法
    return a + b;
}
double js_sub(double a, double b) { return a - b; }
double js_mul(double a, double b) { return a * b; }
double js_div(double a, double b) { return b != 0 ? a / b : bits_to_val(UNDEFINED); }
double js_mod(double a, double b) { return b != 0 ? (double)((int64_t)a % (int64_t)b) : bits_to_val(UNDEFINED); }

double js_eq(double a, double b) { return bits_to_val(a == b ? TRUE_VAL : FALSE_VAL); }
double js_ne(double a, double b) { return bits_to_val(a != b ? TRUE_VAL : FALSE_VAL); }
double js_lt(double a, double b) { return bits_to_val(a < b ? TRUE_VAL : FALSE_VAL); }
double js_le(double a, double b) { return bits_to_val(a <= b ? TRUE_VAL : FALSE_VAL); }
double js_gt(double a, double b) { return bits_to_val(a > b ? TRUE_VAL : FALSE_VAL); }
double js_ge(double a, double b) { return bits_to_val(a >= b ? TRUE_VAL : FALSE_VAL); }
