#!/usr/bin/env bash
# runMultiSeg.sh — run a multi-segment program in MAME via a
# mini in-Lua loader.  Reads the link816 manifest, loads each
# segment's image at its base address, sets PC to segment 1's
# entry, lets the program run, then reads check-address values.
#
# Usage: runMultiSeg.sh <manifest.json> [check args like runInMame.sh]

set -euo pipefail
source "$(dirname "$0")/common.sh"

MANIFEST="$1"
shift
CHECK_FRAME=${MAME_CHECK_FRAME:-300}
SECS=${MAME_SECS:-6}

# Build address list as Lua table entries, mirroring runInMame.sh.
LUA_CHECKS=""
EXPECT_LIST=()
ADDR_LIST=()
if [ "$1" = "--check" ]; then
    shift
    for pair in "$@"; do
        ADDR="${pair%=*}"
        EXP="${pair#*=}"
        ADDR_LIST+=("$ADDR")
        EXPECT_LIST+=("$EXP")
        LUA_CHECKS="$LUA_CHECKS print(string.format('MAME-READ addr=0x%06x val=0x%04x', $ADDR, mem:read_u16($ADDR)))"$'\n'
    done
else
    ADDR="$1"
    EXP="$2"
    ADDR_LIST+=("$ADDR")
    EXPECT_LIST+=("$EXP")
    LUA_CHECKS="print(string.format('MAME-READ addr=0x%06x val=0x%04x', $ADDR, mem:read_u16($ADDR)))"
fi

[ -f "$MANIFEST" ] || die "manifest not found: $MANIFEST"

# Parse manifest with python (every machine has it).  Emit a Lua
# table of (image_path, base, entry_offset_from_seg1).
PARSED=$(python3 - <<EOF
import json, os, sys
m = json.load(open("$MANIFEST"))
for s in m["segments"]:
    base = int(s["base"], 16)
    entry = int(s.get("entry_offset", "0x0"), 16) if s["num"] == 1 else 0
    sz = s["size"]
    print(f'{s["image"]}|{base}|{entry}|{sz}')
EOF
)
[ -n "$PARSED" ] || die "manifest parse failed"

LUA_PATH=$(mktemp --suffix=.lua)
trap 'rm -f "$LUA_PATH"' EXIT

# Build the per-segment load lua.
LOAD_LUA=""
ENTRY_BASE=0
ENTRY_OFF=0
while IFS='|' read -r img base entry sz; do
    LOAD_LUA="$LOAD_LUA
        do
            local f = io.open('$img', 'rb')
            if not f then print('SEG-MISSING $img'); manager.machine:exit(); return end
            local data = f:read('*all'); f:close()
            for i = 1, #data do
                local addr = $base + i - 1
                if not (addr >= 0x00C000 and addr < 0x00D000) then
                    mem:write_u8(addr, data:byte(i))
                end
            end
            print('SEG-LOADED base=0x' .. string.format('%06x', $base) .. ' bytes=' .. #data)
        end
"
    if [ "$entry" != "0" ] || [ "$ENTRY_BASE" = "0" ]; then
        ENTRY_BASE="$base"
        ENTRY_OFF="$entry"
    fi
done <<< "$PARSED"

cat > "$LUA_PATH" <<EOF
local frame = 0
local loaded = false
emu.register_frame_done(function()
    frame = frame + 1
    if frame == 30 and not loaded then
        local cpu = manager.machine.devices[":maincpu"]
        local mem = cpu.spaces["program"]
$LOAD_LUA
        loaded = true
        cpu.state["PC"].value = $ENTRY_BASE + $ENTRY_OFF
        cpu.state["PB"].value = ($ENTRY_BASE >> 16) & 0xff
        cpu.state["DB"].value = 0x00
        cpu.state["D"].value  = 0x00
        cpu.state["P"].value  = 0x34
        cpu.state["E"].value  = 0
        cpu.state["S"].value  = 0x01FF
        print('MAME-READY pc=0x' .. string.format('%06x', $ENTRY_BASE + $ENTRY_OFF))
    end
    if frame == $CHECK_FRAME then
        local cpu = manager.machine.devices[":maincpu"]
        local mem = cpu.spaces["program"]
$LUA_CHECKS
        manager.machine:exit()
    end
end)
EOF

RAMSIZE_ARG=()
if [ -n "${MAME_RAMSIZE:-}" ]; then
    RAMSIZE_ARG=(-ramsize "$MAME_RAMSIZE")
fi
OUT=$(SDL_VIDEODRIVER=dummy SDL_AUDIODRIVER=dummy timeout "${MAME_TIMEOUT:-30}" mame apple2gs \
    "${RAMSIZE_ARG[@]}" \
    -rompath "$PROJECT_ROOT/tools/mame/roms" \
    -plugins -autoboot_script "$LUA_PATH" \
    -video none -sound none -nothrottle -seconds_to_run "$SECS" 2>&1 | /usr/bin/grep -E "^(MAME-|SEG-)")

echo "$OUT"
mapfile -t GOT_LIST < <(printf '%s\n' "$OUT" | /usr/bin/grep -oE 'val=0x[0-9a-f]+' | sed 's/val=0x//')
ok=1
for i in "${!EXPECT_LIST[@]}"; do
    if [ "${GOT_LIST[$i]:-}" != "${EXPECT_LIST[$i]}" ]; then
        warn "MAME mismatch at ${ADDR_LIST[$i]}: got 0x${GOT_LIST[$i]:-MISSING} expected 0x${EXPECT_LIST[$i]}"
        ok=0
    fi
done
if [ $ok -eq 1 ]; then
    log "MAME (multi-seg) OK: ${#EXPECT_LIST[@]} reads matched"
    exit 0
fi
exit 1
