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

build_root=$1
export LD_LIBRARY_PATH="$build_root/src/lib:$build_root/src/cli:${LD_LIBRARY_PATH:-}"
cli_dir="$build_root/src/cli"
php2ast="$cli_dir/phpefc-php2ast"
phpast2phpir="$cli_dir/phpefc-phpast2phpir"
phpir2valair="$cli_dir/phpefc-phpir2valair"
valair_optimize="$cli_dir/phpefc-valair-optimize-typing"
valair_optimize_fastpath_runtime="$cli_dir/phpefc-valair-optimize-fastpath-runtime"
valair2vala="$cli_dir/phpefc-valair2vala"

for tool in "$php2ast" "$phpast2phpir" "$phpir2valair" "$valair_optimize" "$valair_optimize_fastpath_runtime" "$valair2vala"; do
    if [ ! -x "$tool" ]; then
        echo "missing built tool: $tool" >&2
        exit 99
    fi
done

work=${MESON_TEST_TMPDIR:-}
if [ -z "$work" ]; then
    work=$(mktemp -d)
    trap "rm -rf \"$work\"" EXIT
fi
mkdir -p "$work"

src="$work/generic-timing-loop.php"
phpast="$work/generic-timing-loop.phpast"
phpir="$work/generic-timing-loop.phpir"
valair="$work/generic-timing-loop.valair"
afteropt="$work/generic-timing-loop.valair.afteropt.typing-zval2static"
afterfastpath="$work/generic-timing-loop.valair.afteropt.fastpath-runtime"
vala="$work/generic-timing-loop.vala"
stderr_file="$work/generic-timing-loop.stderr"
fastpath_stderr_file="$work/generic-timing-loop-fastpath.stderr"

cat >"$src" <<"PHP"
<?php
class generic_clock_runner
{
    private function tick_a(): int
    {
        return 1;
    }

    private function tick_b(): int
    {
        return 2;
    }

    private function measure($methodName): int
    {
        $checkpoint = microtime(true);
        $startedAt = $checkpoint;
        $sampleCount = 0;
        $iterationCount = $sampleCount;
        $iterationTotal = $iterationCount;
        while (true) {
            $this->$methodName();
            $iterationCount = $iterationCount + 1;
            $currentTime = microtime(true);
            if ($currentTime - $checkpoint > 1) {
                $sampleCount = $sampleCount + 1;
                $iterationTotal = $iterationTotal + $iterationCount;
                [$checkpoint, $iterationCount] = [microtime(true), 0];
            }
            if ($currentTime - $startedAt > 2) {
                return round($iterationTotal / $sampleCount);
            }
        }
    }

    public static function entry(): int
    {
        $obj = new self();
        return $obj->measure("tick_a");
    }
}

echo generic_clock_runner::entry();
PHP

"$php2ast" "$src" "$phpast" >/dev/null
"$phpast2phpir" "$phpast" "$phpir" >/dev/null
"$phpir2valair" "$phpir" "$valair" >/dev/null
if ! "$valair_optimize" --suppress-warning "$valair" "$afteropt" >/dev/null 2>"$stderr_file"; then
    echo "typing optimizer failed" >&2
    cat "$stderr_file" >&2
    exit 1
fi
"$valair2vala" "$afteropt" "$vala" >/dev/null

if ! "$valair_optimize_fastpath_runtime" "$afteropt" "$afterfastpath" >/dev/null 2>"$fastpath_stderr_file"; then
    echo "fastpath-runtime optimizer failed" >&2
    cat "$fastpath_stderr_file" >&2
    exit 1
fi
fastpath_vala="$work/generic-timing-loop-fastpath.vala"
"$valair2vala" "$afterfastpath" "$fastpath_vala" >/dev/null

expected_generated=(
    "private static Zval measure"
    "string methodName = PhpefRuntimeCore.argument(args, 0).to_php_string();"
    "double checkpoint ="
    "double startedAt = checkpoint;"
    "double sampleCount = 0.0;"
    "PhpefRuntimeCore.call_instance_method(__phpefc_this, methodName, new Zval[] {}, \"generic_clock_runner\");"
    "return PhpefRuntimeEmbed.phpfunc_round(Zval.from_float((iterationTotal / sampleCount)));"
)
for expected in "${expected_generated[@]}"; do
    if ! grep -Fq "$expected" "$vala"; then
        echo "expected generic timing-loop optimization snippet: $expected" >&2
        cat "$vala" >&2
        exit 1
    fi
done

forbidden_generated=(
    "Zval checkpoint = Zval.undefined();"
    "PhpefRuntimeCore.call_instance_method(__phpefc_this, methodName.to_php_string()"
    "return Zval.from_null();"
)
for forbidden in "${forbidden_generated[@]}"; do
    if grep -Fq "$forbidden" "$vala"; then
        echo "generic timing-loop optimization did not remove Zval/runtime pattern: $forbidden" >&2
        cat "$vala" >&2
        exit 1
    fi
done

fastpath_expected_generated=(
    "if ((methodName == \"tick_a\"))"
    ".tick_a(__phpefc_this, new Zval[] {});"
    "} else if ((methodName == \"tick_b\"))"
    ".tick_b(__phpefc_this, new Zval[] {});"
    "// phpefc optimizer fastpath-runtime result: 2/3 runtime dispatch calls left"
)
for expected in "${fastpath_expected_generated[@]}"; do
    if ! grep -Fq "$expected" "$fastpath_vala"; then
        echo "expected generic fastpath-runtime optimization snippet: $expected" >&2
        cat "$fastpath_vala" >&2
        exit 1
    fi
done

if grep -Fq "PhpefRuntimeCore.call_instance_method(__phpefc_this, methodName" "$fastpath_vala"; then
    echo "fastpath-runtime did not remove native-string dynamic self dispatch" >&2
    cat "$fastpath_vala" >&2
    exit 1
fi

printf "ok - typing-zval2static and fastpath-runtime generic timing-loop optimizations are visible in generated Vala\n"
