#!/usr/bin/env bash
# Host-side regression tests for the AGI port.
#
# Compiles tests/agi/testAgiRes.c + examples/agi/agiRes.c with the
# host gcc and runs it against every game directory in
# examples/agi/gamedata/. Each game is exercised in isolation; the
# script aggregates pass/fail counts and exits non-zero if any test
# fails.
#
# Why a host build instead of running the emulator binaries: the AGI
# resource loader is plain stdio C with no platform dependencies, so
# the parsing logic is identical on every JoeyLib target. Validating
# it on the build host means each iteration takes a second instead
# of the 15-30s of an emulator boot, and reproducible failures don't
# depend on capturing video / log output through DOSBox or MAME.
#
# Usage:
#   scripts/test-agi.sh                # test every subdir of gamedata/
#   scripts/test-agi.sh kq3 agidemo    # test only the named subdirs

set -euo pipefail

repo=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
gamedata=$repo/examples/agi/gamedata
build=$repo/build/tests
testRes=$build/testAgiRes
testPic=$build/testAgiPic
testView=$build/testAgiView
testVm=$build/testAgiVm
testPipeline=$build/testAgiPipeline

mkdir -p "$build"

# Shared host compile flags. agiPic.c calls jlDrawPixel in the blit
# step, which the host test never invokes; we stub it as an empty
# function via the link-line shim below so the host build can resolve
# the symbol without dragging in the JoeyLib runtime.
# Pick a JoeyLib platform for the host build. None of the platform-
# specific code paths are exercised by the host test (it only touches
# the AGI parsers, never the JoeyLib HAL), but joey/platform.h #errors
# unless exactly one is defined. DOS is the closest fit for a Linux
# host (chunky, little-endian, x86 lineage).
hostCflags=(-std=c99 -Wall -Wextra -Werror -O2 -DJOEYLIB_PLATFORM_DOS -I "$repo/include" -I "$repo/examples/agi" -I "$repo/src/core")
stubSrc=$build/hostStubs.c
cat > "$stubSrc" <<'EOF'
#include "joey/draw.h"
#include "joey/core.h"
#include "joey/file.h"
#include "surfaceInternal.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Host jlDataOpen: the real one (assetLoad.c) forces a DATA/ prefix, but
// the test gamedata dirs hold their files flat. Open <AGI_DATA_DIR>/<name>
// so agiRes.c's bare-name jlDataOpen calls resolve against the game dir the
// harness points us at.
FILE *jlDataOpen(const char *name, const char *mode) {
    const char *base = getenv("AGI_DATA_DIR");
    char        path[1024];
    if (base == NULL) { base = "."; }
    if ((size_t)snprintf(path, sizeof(path), "%s/%s", base, name) >= sizeof(path)) { return NULL; }
    return fopen(path, mode);
}
void     jlDrawPixel(jlSurfaceT *s, int16_t x, int16_t y, uint8_t c) { (void)s; (void)x; (void)y; (void)c; }
void     jlFillRect(jlSurfaceT *s, int16_t x, int16_t y, uint16_t w, uint16_t h, uint8_t c) { (void)s; (void)x; (void)y; (void)w; (void)h; (void)c; }
uint32_t jlRandom(void)                           { return (uint32_t)rand(); }
uint16_t jlRandomRange(uint16_t bound)            { return (bound == 0u) ? 0u : (uint16_t)((unsigned)rand() % bound); }
void     jlRandomSeed(uint32_t seed)              { srand((unsigned)seed); }
void    *jlAlloc(uint32_t bytes)                  { return malloc((size_t)bytes); }
void     jlFree(void *p)                          { free(p); }
void     jlLogF(const char *fmt, ...)             { (void)fmt; }
void     jlLogFlush(void)                         { }
void     jlLog(const char *msg)                   { (void)msg; }
/* surfaceMarkDirtyRect is a MACRO since the Phase-6 mark rework -- the
 * host TUs (agiPic/agiView) expand it inline, so the stubs provide the
 * band state it references instead of a function. gStage stays NULL,
 * so every expansion takes the s != gStage no-op branch; the symbols
 * only need to exist to link. */
jlSurfaceT *gStage = 0;
uint8_t     gStageMinWord[SURFACE_HEIGHT];
uint8_t     gStageMaxWord[SURFACE_HEIGHT];
void     surfaceMarkDirtyRows(uint16_t y, uint16_t yEnd, uint8_t minWord, uint8_t maxWord) { (void)y; (void)yEnd; (void)minWord; (void)maxWord; }
void     surfaceMarkDirtyAll(const jlSurfaceT *s) { (void)s; }
EOF

echo "Building host tests..."
gcc "${hostCflags[@]}" \
    "$repo/tests/agi/testAgiRes.c" \
    "$repo/examples/agi/agiRes.c" \
    "$stubSrc" \
    -o "$testRes"

gcc "${hostCflags[@]}" \
    "$repo/tests/agi/testAgiPic.c" \
    "$repo/examples/agi/agiRes.c" \
    "$repo/examples/agi/agiPic.c" \
    "$stubSrc" \
    -o "$testPic"

gcc "${hostCflags[@]}" \
    "$repo/tests/agi/testAgiView.c" \
    "$repo/examples/agi/agiRes.c" \
    "$repo/examples/agi/agiPic.c" \
    "$repo/examples/agi/agiView.c" \
    "$stubSrc" \
    -o "$testView"

gcc "${hostCflags[@]}" \
    "$repo/tests/agi/testAgiVm.c" \
    "$repo/examples/agi/agiRes.c" \
    "$repo/examples/agi/agiVm.c" \
    "$repo/examples/agi/agiObj.c" \
    "$stubSrc" \
    -o "$testVm"

gcc "${hostCflags[@]}" \
    "$repo/tests/agi/testAgiPipeline.c" \
    "$repo/examples/agi/agiRes.c" \
    "$repo/examples/agi/agiVm.c" \
    "$repo/examples/agi/agiObj.c" \
    "$stubSrc" \
    -o "$testPipeline"

# Pick the game directories to exercise. With no args, every
# subdirectory of examples/agi/gamedata that contains LOGDIR is a
# candidate (skips stray README files etc).
if [[ $# -gt 0 ]]; then
    games=("$@")
else
    games=()
    if [[ -d $gamedata ]]; then
        for dir in "$gamedata"/*/; do
            [[ -d $dir ]] || continue
            name=$(basename "$dir")
            if [[ -f $dir/LOGDIR ]]; then
                games+=("$name")
            fi
        done
    fi
fi

if [[ ${#games[@]} -eq 0 ]]; then
    echo
    echo "No AGI game directories found under $gamedata"
    echo "Drop an AGI v2 game in examples/agi/gamedata/<name>/ and rerun."
    echo "See examples/agi/gamedata/README.md for the required files."
    exit 0
fi

passed=0
failed=0
echo
for game in "${games[@]}"; do
    dir=$gamedata/$game
    echo "===== $game ====="
    if [[ ! -d $dir ]]; then
        echo "  FAIL: $dir does not exist"
        failed=$((failed + 1))
        continue
    fi

    # agiRes.c opens data by bare name via jlDataOpen; the host stub
    # resolves those against AGI_DATA_DIR (see stubSrc above).
    export AGI_DATA_DIR="$dir"
    gameOk=1
    echo "--- resource loader ---"
    if ! "$testRes" "$dir"; then
        gameOk=0
    fi
    echo "--- PIC decoder ---"
    if ! "$testPic" "$dir"; then
        gameOk=0
    fi
    echo "--- VIEW decoder ---"
    if ! "$testView" "$dir"; then
        gameOk=0
    fi
    echo "--- LOGIC VM (logic.0) ---"
    if ! "$testVm" "$dir"; then
        gameOk=0
    fi
    echo "--- VM pipeline (callbacks + room transitions) ---"
    if ! "$testPipeline" "$dir"; then
        gameOk=0
    fi

    if [[ $gameOk -eq 1 ]]; then
        passed=$((passed + 1))
    else
        failed=$((failed + 1))
    fi
    echo
done

total=$((passed + failed))
echo "===== summary: $passed/$total passed ====="
if [[ $failed -gt 0 ]]; then
    exit 1
fi
exit 0
