// Bytecode compiler test
// Compile with: cc -I../include -L../build -lant tests/bc_test.c -o bc_test

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "ant.h"
#include "bytecode.h"

static void test_expr(struct js *js, const char *expr, const char *expected_type) {
  printf("\n--- Testing: %s ---\n", expr);
  bc_compile_debug(js, expr, strlen(expr));
}

static void test_run(struct js *js, const char *expr) {
  printf("\n--- Running: %s ---\n", expr);
  jsval_t result = bc_compile_run(js, expr, strlen(expr));
  printf("Result: %s (type=%d)\n", js_str(js, result), js_type(result));
}

int main(void) {
  size_t mem_size = 1024 * 1024;
  void *mem = malloc(mem_size);
  if (!mem) {
    fprintf(stderr, "Failed to allocate memory\n");
    return 1;
  }
  
  struct js *js = js_create(mem, mem_size);
  if (!js) {
    fprintf(stderr, "Failed to create JS context\n");
    free(mem);
    return 1;
  }
  
  printf("=== Bytecode Compiler Tests ===\n");
  
  // Test number literals
  test_expr(js, "42", "number");
  test_expr(js, "0", "number");
  test_expr(js, "1", "number");
  test_expr(js, "3.14", "number");
  
  // Test simple arithmetic
  test_expr(js, "1 + 2", "add");
  test_expr(js, "5 - 3", "sub");
  test_expr(js, "4 * 5", "mul");
  test_expr(js, "10 / 2", "div");
  
  // Test precedence
  test_expr(js, "1 + 2 * 3", "precedence");
  test_expr(js, "(1 + 2) * 3", "parens");
  
  // Test comparison
  test_expr(js, "5 > 3", "comparison");
  test_expr(js, "5 === 5", "strict equal");
  
  // Test logical
  test_expr(js, "true && false", "and");
  test_expr(js, "true || false", "or");
  test_expr(js, "!true", "not");
  
  // Test ternary
  test_expr(js, "true ? 1 : 2", "ternary");
  
  // Test unary
  test_expr(js, "-5", "negate");
  test_expr(js, "+5", "positive");
  test_expr(js, "~5", "bitnot");
  
  // Now test execution
  printf("\n\n=== Bytecode VM Execution Tests ===\n");
  
  test_run(js, "42");
  test_run(js, "1 + 2");
  test_run(js, "1 + 2 * 3");
  test_run(js, "10 - 3");
  test_run(js, "6 / 2");
  test_run(js, "2 ** 10");
  test_run(js, "true ? 100 : 200");
  test_run(js, "false ? 100 : 200");
  test_run(js, "-42");
  test_run(js, "!true");
  test_run(js, "5 > 3");
  test_run(js, "5 < 3");
  
  // Test program compilation
  printf("\n\n=== Program Compilation Tests ===\n");
  
  // Simple loop
  const char *loop_code = 
    "let result = 0;\n"
    "for (let i = 0; i < 10; i = i + 1) {\n"
    "  result = result + i;\n"
    "}\n"
    "result";
  
  printf("\n--- Compiling loop program ---\n");
  printf("Code:\n%s\n\n", loop_code);
  bc_chunk_t *chunk = bc_compile_program(js, loop_code, strlen(loop_code));
  if (chunk) {
    printf("Compilation successful!\n");
    bc_disassemble(chunk, "loop_test");
    
    bc_vm_t *vm = bc_vm_new(js);
    if (vm) {
      bc_vm_reset(vm);
      jsval_t result = bc_vm_run(vm, chunk);
      printf("Loop result: %s (expected: 45)\n", js_str(js, result));
      bc_vm_free(vm);
    }
    bc_chunk_free(chunk);
  } else {
    printf("Loop compilation failed!\n");
  }
  
  // Function test
  const char *func_code = 
    "function add(a, b) {\n"
    "  return a + b;\n"
    "}\n"
    "add(3, 4)";
  
  printf("\n--- Compiling function program ---\n");
  chunk = bc_compile_program(js, func_code, strlen(func_code));
  if (chunk) {
    printf("Compilation successful!\n");
    bc_disassemble(chunk, "func_test");
    
    bc_vm_t *vm2 = bc_vm_new(js);
    if (vm2) {
      bc_vm_reset(vm2);
      jsval_t result = bc_vm_run(vm2, chunk);
      printf("Function call result: %s (expected: 7)\n", js_str(js, result));
      bc_vm_free(vm2);
    }
    bc_chunk_free(chunk);
  } else {
    printf("Function compilation failed!\n");
  }
  
  // Microbenchmark
  printf("\n=== Microbenchmark ===\n");
  const char *bench_code = 
    "function add(a, b) {\n"
    "  return a + b;\n"
    "}\n"
    "let result = 0;\n"
    "for (let i = 0; i < 200000; i = i + 1) {\n"
    "  result = result + add(i, i + 1);\n"
    "}\n"
    "result";
  
  chunk = bc_compile_program(js, bench_code, strlen(bench_code));
  if (chunk) {
    printf("Benchmark compiled successfully!\n");
    
    // Time it
    clock_t start = clock();
    bc_vm_t *vm3 = bc_vm_new(js);
    bc_vm_reset(vm3);
    jsval_t result = bc_vm_run(vm3, chunk);
    clock_t end = clock();
    
    double ms = (double)(end - start) * 1000.0 / CLOCKS_PER_SEC;
    printf("Result: %s\n", js_str(js, result));
    printf("Time: %.2f ms\n", ms);
    
    bc_vm_free(vm3);
    bc_chunk_free(chunk);
  } else {
    printf("Benchmark compilation failed!\n");
  }
  
  js_destroy(js);
  free(mem);
  
  printf("\n=== All tests completed ===\n");
  return 0;
}
