// AVM ISA v1 — C++ Test Harness
#include <cassert>
#include <iostream>
#include "avm.hpp"
using namespace avm;

#define QS Q16_SCALE

int32_t val_i(const AnyVal& v) { return std::get<int32_t>(v.val); }
bool val_b(const AnyVal& v) { return std::get<bool>(v.val); }

void test_basic_add() {
    auto prog = std::vector<Instr>{
        {Op::PushQ16, 5 * QS}, {Op::PushQ16, 3 * QS},
        {Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}, {Op::Halt}};
    auto s = run(init_state(), prog, 100);
    assert(s.has_value());
    assert(val_i(s->stack[0]) == 8 * QS);
    std::cout << "  ✅ basic_add: 5 + 3 = 8\n";
}

void test_div_q16() {
    auto prog = std::vector<Instr>{
        {Op::PushQ16, 3 * QS}, {Op::PushQ16, 5 * QS},
        {Op::Primitive, static_cast<int32_t>(Prim::DivSatQ16)}, {Op::Halt}};
    auto s = run(init_state(), prog, 100);
    int exp = (3 * QS) / 5;
    assert(val_i(s->stack[0]) == exp);
    std::cout << "  ✅ div_q16: 3/5 = 0.6\n";
}

void test_saturation() {
    auto prog = std::vector<Instr>{
        {Op::PushQ16, AVM_CLAMP_MAX - 1}, {Op::PushQ16, 2},
        {Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}, {Op::Halt}};
    auto s = run(init_state(), prog, 100);
    assert(val_i(s->stack[0]) == AVM_CLAMP_MAX);
    std::cout << "  ✅ saturation: max-1+2 = max\n";
}

void test_v6_lt() {
    struct Case { int32_t a, b; bool exp; } cases[] = {
        {-5*QS, -3*QS, true}, {-3*QS, -5*QS, false},
        {5*QS, 3*QS, false}, {3*QS, 5*QS, true}, {-1*QS, 2*QS, true}};
    for (auto c : cases) {
        auto prog = std::vector<Instr>{
            {Op::PushQ16, c.a}, {Op::PushQ16, c.b},
            {Op::Primitive, static_cast<int32_t>(Prim::LtQ16)}, {Op::Halt}};
        auto s = run(init_state(), prog, 100);
        assert(std::get<bool>(s->stack[0].val) == c.exp);  // bool variant OK
    }
    std::cout << "  ✅ v6_lt: 5 cases pass\n";
}

void test_type_mismatch() {
    auto prog = std::vector<Instr>{
        {Op::PushBool, 0, true}, {Op::PushQ16, QS},
        {Op::Primitive, static_cast<int32_t>(Prim::AddSatQ16)}};
    auto s = run(init_state(), prog, 100);
    assert(!s.has_value());
    std::cout << "  ✅ type_mismatch: rejected\n";
}

void test_div_by_zero() {
    auto prog = std::vector<Instr>{
        {Op::PushQ16, QS}, {Op::PushQ16, 0},
        {Op::Primitive, static_cast<int32_t>(Prim::DivSatQ16)}};
    auto s = run(init_state(), prog, 100);
    assert(!s.has_value());
    std::cout << "  ✅ div_by_zero: rejected\n";
}

void test_stack_overflow() {
    auto prog = std::vector<Instr>(AVM_MAX_STACK + 1, Instr{Op::PushQ16, 0});
    auto s = run(init_state(), prog, 10000);
    assert(!s.has_value());
    std::cout << "  ✅ stack_overflow: rejected\n";
}

void test_control_flow() {
    auto prog = std::vector<Instr>{
        {Op::PushBool, 0, true}, {Op::JumpIf, 4},
        {Op::PushQ16, 0}, {Op::Halt},
        {Op::PushQ16, QS}, {Op::Halt}};
    auto s = run(init_state(), prog, 100);
    assert(val_i(s->stack[0]) == QS);
    std::cout << "  ✅ control_flow: jump_if true\n";
}

void test_locals() {
    auto prog = std::vector<Instr>{
        {Op::PushQ16, 42 * QS}, {Op::Store, 0},
        {Op::Load, 0}, {Op::Halt}};
    auto s = run(init_state(1), prog, 100);
    assert(val_i(s->stack[0]) == 42 * QS);
    std::cout << "  ✅ locals: store+load\n";
}

int main() {
    std::cout << "AVM C++ Port — Test Harness\n=========================\n";
    test_basic_add(); test_div_q16(); test_saturation(); test_v6_lt();
    test_type_mismatch(); test_div_by_zero(); test_stack_overflow();
    test_control_flow(); test_locals();
    std::cout << "\nAll C++ tests passed.\n";
    return 0;
}
