#!/usr/bin/env bash
set -euo pipefail

cd "$(dirname "$0")"

MKEXE=./mkexe
TESTS=tests
fail=0
total=0

assert_hex_eq() {
    local file=$1 offset=$2 expected=$3 label=$4
    local actual
    actual=$(xxd -s "$offset" -l $((${#expected} / 2)) -p "$file")
    if [ "$actual" != "$expected" ]; then
        echo "FAIL: $label"
        echo "  file:     $file"
        echo "  offset:   $offset"
        echo "  expected: $expected"
        echo "  actual:   $actual"
        fail=$((fail + 1))
    fi
}

run_case() {
    local name=$1; shift
    total=$((total + 1))
    if "$@" >/dev/null 2>&1; then
        :
    else
        echo "FAIL: $name (exit nonzero)"
        fail=$((fail + 1))
        return
    fi
}

expect_failure() {
    local name=$1; shift
    total=$((total + 1))
    if "$@" >/dev/null 2>&1; then
        echo "FAIL: $name (expected nonzero exit, got success)"
        fail=$((fail + 1))
    fi
}

# Case 1: minimal .ihx with one byte RET at 0x4100
run_case "build-single-byte" \
    $MKEXE -L 0x4100 -E 0x4100 -S 0xBFFE -o $TESTS/case1.exe $TESTS/hello.ihx

assert_hex_eq $TESTS/case1.exe 0    "455845"          "case1: signature EXE"
assert_hex_eq $TESTS/case1.exe 3    "00"              "case1: version 0"
assert_hex_eq $TESTS/case1.exe 4    "00020000"        "case1: offset 0x00000200"
assert_hex_eq $TESTS/case1.exe 8    "0000"            "case1: loader=0"
assert_hex_eq $TESTS/case1.exe 0x10 "0041"            "case1: load=0x4100"
assert_hex_eq $TESTS/case1.exe 0x12 "0041"            "case1: start=0x4100"
assert_hex_eq $TESTS/case1.exe 0x14 "febf"            "case1: stack=0xBFFE"
assert_hex_eq $TESTS/case1.exe 0x200 "c9"             "case1: image byte = RET (C9)"

# File size = 512 + 1
size=$(stat -f "%z" $TESTS/case1.exe 2>/dev/null || stat -c "%s" $TESTS/case1.exe)
if [ "$size" != "513" ]; then
    echo "FAIL: case1: file size $size != 513"
    fail=$((fail + 1))
fi
total=$((total + 1))

# Case 2: defaults — load and start auto-derived from .ihx, stack default = 0xBFFE
run_case "defaults-from-ihx" \
    $MKEXE -o $TESTS/case2.exe $TESTS/hello.ihx
assert_hex_eq $TESTS/case2.exe 0x10 "0041" "case2: load defaults to 0x4100 (from ihx)"
assert_hex_eq $TESTS/case2.exe 0x12 "0041" "case2: start defaults to load"
assert_hex_eq $TESTS/case2.exe 0x14 "febf" "case2: stack defaults to 0xBFFE"

# Case 3: .bin input
printf '\xC9' > $TESTS/one.bin
run_case "build-from-bin" \
    $MKEXE -L 0x4100 -o $TESTS/case3.exe $TESTS/one.bin
assert_hex_eq $TESTS/case3.exe 0x10 "0041"  "case3: bin load=0x4100"
assert_hex_eq $TESTS/case3.exe 0x200 "c9"   "case3: bin image"

# Case 4: image extending past 0xFFFF must fail
printf '\xFF%.0s' {1..4096} > $TESTS/big.bin
expect_failure "reject-past-FFFF" \
    $MKEXE -L 0xF800 -o $TESTS/case4.exe $TESTS/big.bin

# Case 5: load above 0xFFFF must fail
expect_failure "reject-load-out-of-range" \
    $MKEXE -L 0x10000 -o $TESTS/case5.exe $TESTS/one.bin

# Case 6: bad checksum in .ihx must fail
cat > $TESTS/bad.ihx <<'EOF'
:01410000C900
:00000001FF
EOF
expect_failure "reject-bad-checksum" \
    $MKEXE -o $TESTS/case6.exe $TESTS/bad.ihx

# Case 7: multi-bank IHX → loader auto-set to HOME size, bank 1 appended (16 KB)
# ELA "0001" puts the next records into bank 1 (virtual 0x1C000+).
# Note: Intel HEX address fields are big-endian inside the record
#   :01 C000 00 77 C8   means addr=0xC000 (record bytes "C000" = high then low)
cat > $TESTS/banked.ihx <<'EOF'
:01410000C9F5
:020000040001F9
:01C0000077C8
:00000001FF
EOF
run_case "banked-auto-loader" \
    $MKEXE -L 0x4100 -o $TESTS/case7.exe $TESTS/banked.ihx
assert_hex_eq $TESTS/case7.exe 8 "0100"  "case7: loader=HOME size (1 byte)"
size=$(stat -f "%z" $TESTS/case7.exe 2>/dev/null || stat -c "%s" $TESTS/case7.exe)
if [ "$size" != "16897" ]; then
    echo "FAIL: case7: file size $size != 16897 (512 hdr + 1 HOME + 16384 bank1)"
    fail=$((fail + 1))
fi
# Byte at file offset 0x201 (first byte of bank1) should be the 0x77 we put at bank1's 0xC000
assert_hex_eq $TESTS/case7.exe 0x201 "77" "case7: bank1 first byte"

total=$((total + 9))
if [ "$fail" -gt 0 ]; then
    echo
    echo "FAILED: $fail of $total assertions/cases"
    exit 1
fi
echo "OK: $total cases passed"
