/* AVM ISA v1 — C Port (Strict Functional Execution) */
#include <stdint.h>
#include <stdbool.h>

#define Q16_SCALE 65536
#define AVM_Q0_MIN (-32767)
#define AVM_Q0_MAX 32767
#define AVM_Q16_MIN (-2147483648)
#define AVM_Q16_MAX 2147483647

/* ── Types ─────────────────────────────────────── */
typedef enum { VAL_Q0, VAL_Q16, VAL_BOOL } ValType;
typedef struct { ValType ty; int32_t val; } AvmVal;

AvmVal avm_q0(int32_t x) {
    if (x < AVM_Q0_MIN) x = AVM_Q0_MIN;
    if (x > AVM_Q0_MAX) x = AVM_Q0_MAX;
    return (AvmVal){VAL_Q0, x};
}
AvmVal avm_q16(int32_t x) { return (AvmVal){VAL_Q16, x}; }
AvmVal avm_bool(bool x) { return (AvmVal){VAL_BOOL, x ? 1 : 0}; }

/* ── Primitives ─────────────────────────────────── */
typedef enum {
    PRIM_ADD_Q16, PRIM_SUB_Q16, PRIM_MUL_Q16, PRIM_DIV_Q16,
    PRIM_LT_Q16, PRIM_EQ_Q16, PRIM_AND, PRIM_OR, PRIM_NOT
} Prim;

typedef struct { AvmVal *data; int len, cap; } Stack;
typedef struct { AvmVal *data; int len; } Locals;

typedef struct State {
    int pc;
    Stack stack;
    Locals locals;
    int halted;
} State;

int64_t q16_mul(int32_t a, int32_t b) {
    return (int64_t)a * (int64_t)b / Q16_SCALE;
}
int64_t q16_div(int32_t a, int32_t b) {
    if (b == 0) return AVM_Q16_MAX;
    return (int64_t)a * Q16_SCALE / b;
}

/* ── Instr ──────────────────────────────────────── */
typedef enum {
    INSTR_PUSH_Q16, INSTR_PUSH_BOOL, INSTR_POP, INSTR_DUP, INSTR_SWAP,
    INSTR_LOAD, INSTR_STORE, INSTR_JUMP, INSTR_JUMP_IF, INSTR_PRIM, INSTR_HALT
} InstrOp;

typedef struct { InstrOp op; int32_t arg; int arg2; } Instr;

Instr i_push_q16(int32_t x) { return (Instr){INSTR_PUSH_Q16, x, 0}; }
Instr i_push_bool(int x) { return (Instr){INSTR_PUSH_BOOL, x, 0}; }
Instr i_pop(void) { return (Instr){INSTR_POP, 0, 0}; }
Instr i_dup(void) { return (Instr){INSTR_DUP, 0, 0}; }
Instr i_swap(void) { return (Instr){INSTR_SWAP, 0, 0}; }
Instr i_load(int i) { return (Instr){INSTR_LOAD, i, 0}; }
Instr i_store(int i) { return (Instr){INSTR_STORE, i, 0}; }
Instr i_jump(int t) { return (Instr){INSTR_JUMP, t, 0}; }
Instr i_jump_if(int t) { return (Instr){INSTR_JUMP_IF, t, 0}; }
Instr i_prim(Prim p) { return (Instr){INSTR_PRIM, p, 0}; }
Instr i_halt(void) { return (Instr){INSTR_HALT, 0, 0}; }

/* ── Step ───────────────────────────────────────── */
int step(State *s, Instr *prog, int prog_len) {
    if (s->halted) return -1;
    if (s->pc < 0 || s->pc >= prog_len) { s->halted = 1; return 0; }

    Instr instr = prog[s->pc];
    s->pc++;
    AvmVal a, b;

    switch (instr.op) {
    case INSTR_PUSH_Q16:
        s->stack.data[s->stack.len++] = avm_q16(instr.arg);
        break;
    case INSTR_PUSH_BOOL:
        s->stack.data[s->stack.len++] = avm_bool(instr.arg);
        break;
    case INSTR_POP: s->stack.len--; break;
    case INSTR_DUP:
        s->stack.data[s->stack.len] = s->stack.data[s->stack.len - 1];
        s->stack.len++;
        break;
    case INSTR_SWAP: {
        AvmVal t = s->stack.data[s->stack.len - 1];
        s->stack.data[s->stack.len - 1] = s->stack.data[s->stack.len - 2];
        s->stack.data[s->stack.len - 2] = t;
        break;
    }
    case INSTR_LOAD:
        s->stack.data[s->stack.len++] = s->locals.data[instr.arg];
        break;
    case INSTR_STORE:
        s->locals.data[instr.arg] = s->stack.data[--s->stack.len];
        break;
    case INSTR_JUMP: s->pc = instr.arg; break;
    case INSTR_JUMP_IF:
        if (s->stack.data[--s->stack.len].val) s->pc = instr.arg;
        break;
    case INSTR_PRIM: {
        int arity = (instr.arg == PRIM_NOT) ? 1 : 2;
        if (arity == 2) b = s->stack.data[--s->stack.len];
        a = s->stack.data[--s->stack.len];
        switch (instr.arg) {
        case PRIM_ADD_Q16:
            s->stack.data[s->stack.len++] = avm_q16(a.val + b.val);
            break;
        case PRIM_SUB_Q16:
            s->stack.data[s->stack.len++] = avm_q16(a.val - b.val);
            break;
        case PRIM_MUL_Q16:
            s->stack.data[s->stack.len++] = avm_q16(q16_mul(a.val, b.val));
            break;
        case PRIM_DIV_Q16:
            s->stack.data[s->stack.len++] = avm_q16(q16_div(a.val, b.val));
            break;
        case PRIM_LT_Q16:
            s->stack.data[s->stack.len++] = avm_bool(a.val < b.val);
            break;
        case PRIM_EQ_Q16:
            s->stack.data[s->stack.len++] = avm_bool(a.val == b.val);
            break;
        case PRIM_AND:
            s->stack.data[s->stack.len++] = avm_bool(a.val && b.val);
            break;
        case PRIM_OR:
            s->stack.data[s->stack.len++] = avm_bool(a.val || b.val);
            break;
        case PRIM_NOT:
            s->stack.data[s->stack.len++] = avm_bool(!a.val);
            break;
        }
        break;
    }
    case INSTR_HALT: s->halted = 1; break;
    }
    return 0;
}

int run(State *s, Instr *prog, int prog_len, int fuel) {
    for (int i = 0; i < fuel && !s->halted; i++)
        if (step(s, prog, prog_len)) return -1;
    return 0;
}
