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

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"

make -C "$ROOT" -s >/dev/null

mkdir -p "$ROOT/build"

BIN="$ROOT/flowlog"
PROG="$ROOT/tests/and_par_user_pred.pl"

fail() {
  echo "fail: $1" >&2
  exit 1
}

run_out() {
  "$BIN" --threads 2 -f "$PROG" -g "$1" | tr -d '\r'
}

run_prog_out() {
  local prog="$1"
  local goal="$2"
  "$BIN" --threads 2 -f "$prog" -g "$goal" | tr -d '\r'
}

time_real() {
  if command -v /usr/bin/time >/dev/null 2>&1; then
    (/usr/bin/time -p "$@" >/dev/null) 2>&1 | awk '/^real[[:space:]]+/{print $2; exit}'
    return 0
  fi

  # Fallback: coarse (seconds).
  local t0 t1
  t0="$(date +%s)"
  "$@" >/dev/null
  t1="$(date +%s)"
  echo "$((t1 - t0))"
}

out="$("$BIN" --threads 1 --engine interp -g '\+ (true -> fail).' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "parse_not_if_then"
fi

out="$(run_out 'doubleComplex(1,O).')"
out="${out%$'\n'}"
if [[ "$out" != "O = 4" ]]; then
  echo "--- expected ---" >&2
  echo "O = 4" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "and_par_user_pred_output"
fi

seq="$(time_real "$BIN" --threads 2 --engine interp -f "$PROG" -g 'set_prolog_flag(flowlog_parallel_and,off), doubleComplex(1,O).')"
par="$(time_real "$BIN" --threads 2 --engine interp -f "$PROG" -g 'set_prolog_flag(flowlog_parallel_and,on), doubleComplex(1,O).')"

awk -v seq="$seq" -v par="$par" 'BEGIN {
  if (seq + 0.0 <= 0.0 || par + 0.0 <= 0.0) exit 1;
  # Expect a noticeable wall-time reduction when AND-par is enabled.
  if ((seq / par) < 1.3) exit 2;
}' || fail "and_par_user_pred_timing (seq=$seq par=$par)"

seq_wam="$(time_real "$BIN" --threads 2 --engine wam -f "$PROG" -g 'current_prolog_flag(flowlog_engine_active,wam), set_prolog_flag(flowlog_parallel_and,off), doubleComplex(1,O).')"
par_wam="$(time_real "$BIN" --threads 2 --engine wam -f "$PROG" -g 'current_prolog_flag(flowlog_engine_active,wam), set_prolog_flag(flowlog_parallel_and,on), doubleComplex(1,O).')"

awk -v seq="$seq_wam" -v par="$par_wam" 'BEGIN {
  if (seq + 0.0 <= 0.0 || par + 0.0 <= 0.0) exit 1;
  # Expect a noticeable wall-time reduction when AND-par is enabled.
  if ((seq / par) < 1.3) exit 2;
}' || fail "and_par_user_pred_timing_wam (seq=$seq_wam par=$par_wam)"

bash "$ROOT/tests/run_sigint_repl.sh" >/dev/null
bash "$ROOT/tests/run_siginfo_repl.sh" >/dev/null
bash "$ROOT/tests/run_siginfo_key_repl.sh" >/dev/null
bash "$ROOT/tests/run_ctrld_repl.sh" >/dev/null
bash "$ROOT/tests/run_bracket_consult_repl.sh" >/dev/null
bash "$ROOT/tests/run_orpar_nesting.sh" >/dev/null
bash "$ROOT/tests/run_orpar_nesting_wam.sh" >/dev/null

out="$("$BIN" --threads 2 -f "$ROOT/tests/length_long.pl" -g 'ok.' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "length_long"
fi

WAM_PROG="$ROOT/tests/wam_engine_smoke.pl"
out="$(run_prog_out "$WAM_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_ok"
fi

out="$(run_prog_out "$WAM_PROG" 'fact(5,F).')"
out="${out%$'\n'}"
if [[ "$out" != "F = 120" ]]; then
  echo "--- expected ---" >&2
  echo "F = 120" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_fact"
fi

out="$(run_prog_out "$WAM_PROG" 'count(C).')"
out="${out%$'\n'}"
if [[ "$out" != "C = 2" ]]; then
  echo "--- expected ---" >&2
  echo "C = 2" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_engine_findall_length"
fi

WAM_TABLING_PROG="$ROOT/tests/wam_tabling.pl"
out="$(run_prog_out "$WAM_TABLING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_tabling"
fi

FINDALL_PROG="$ROOT/tests/findall_non_ground.pl"
out="$(run_prog_out "$FINDALL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "findall_non_ground_fallback"
fi

FINDALL_VARS_PROG="$ROOT/tests/findall_vars.pl"
out="$(run_prog_out "$FINDALL_VARS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "findall_vars_wam"
fi

WAM_CTRL_PROG="$ROOT/tests/wam_control_constructs.pl"
out="$(run_prog_out "$WAM_CTRL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_control_constructs"
fi

WAM_TERM_OPS_PROG="$ROOT/tests/wam_term_ops.pl"
out="$(run_prog_out "$WAM_TERM_OPS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_term_ops"
fi

WAM_SORTING_PROG="$ROOT/tests/wam_sorting.pl"
out="$(run_prog_out "$WAM_SORTING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_sorting"
fi

WAM_SUB_STRING_PROG="$ROOT/tests/wam_sub_string.pl"
out="$(run_prog_out "$WAM_SUB_STRING_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_sub_string"
fi

WAM_EXTRA_BUILTINS_PROG="$ROOT/tests/wam_extra_builtins.pl"
out="$(run_prog_out "$WAM_EXTRA_BUILTINS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_extra_builtins"
fi

WAM_EXC_PROG="$ROOT/tests/wam_exceptions.pl"
out="$(run_prog_out "$WAM_EXC_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_exceptions"
fi

WAM_COPY_VARS_PROG="$ROOT/tests/wam_copy_vars.pl"
out="$(run_prog_out "$WAM_COPY_VARS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_copy_vars"
fi

WAM_CONV_PROG="$ROOT/tests/wam_conversions.pl"
out="$(run_prog_out "$WAM_CONV_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_conversions"
fi

WAM_BAGOF_PROG="$ROOT/tests/wam_bagof_setof.pl"
out="$(run_prog_out "$WAM_BAGOF_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_bagof_setof"
fi

WAM_OPS_PROG="$ROOT/tests/wam_ops.pl"
out="$(run_prog_out "$WAM_OPS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_ops"
fi

WAM_FLAGS_PROG="$ROOT/tests/wam_flags.pl"
out="$(run_prog_out "$WAM_FLAGS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_flags"
fi

WAMVM_FLAGS_PROG="$ROOT/tests/wamvm_flags.pl"
out="$(run_prog_out "$WAMVM_FLAGS_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_flags"
fi

WAMVM_VM_SMOKE_PROG="$ROOT/tests/wamvm_vm_smoke.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f "$WAMVM_VM_SMOKE_PROG" -g 'findall(X,p(X),Xs).' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "Xs = [1,2]" ]]; then
  echo "--- expected ---" >&2
  echo "Xs = [1,2]" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_vm_smoke"
fi

WAMVM_FINDALL_VAR_GOAL_PROG="$ROOT/tests/wamvm_findall_var_goal_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f "$WAMVM_FINDALL_VAR_GOAL_PROG" -g 'ok.' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_findall_var_goal_vm"
fi

WAMVM_SELECT1_VM_PROG="$ROOT/tests/wamvm_select1_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f "$WAMVM_SELECT1_VM_PROG" -g 'ok.' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_select1_vm"
fi

WAMVM_TERM_OPS_VM_PROG="$ROOT/tests/wamvm_term_ops_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f "$WAMVM_TERM_OPS_VM_PROG" -g 'ok.' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_term_ops_vm"
fi

WAMVM_EXCEPTIONS_VM_PROG="$ROOT/tests/wamvm_exceptions_vm.pl"
out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm -f "$WAMVM_EXCEPTIONS_VM_PROG" -g 'ok.' | tr -d '\r')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wamvm_exceptions_vm"
fi

# VM-only compatibility pass: ensure common WAM-era ISO tests run entirely in WAMVM mode.
for f in "$ROOT"/tests/wam_*.pl; do
  base="$(basename "$f")"
  case "$base" in
    wam_flags.pl|wam_tabling.pl) continue ;;
  esac
  out="$(FLOWLOG_WAMVM_REQUIRE_VM=1 "$BIN" --threads 2 --engine wamvm --parallel-profile off -f "$f" -g 'ok.' | tr -d '\r')"
  out="${out%$'\n'}"
  if [[ "$out" != "true" ]]; then
    echo "--- expected ---" >&2
    echo "true" >&2
    echo "--- got ---" >&2
    printf '%s\n' "$out" >&2
    fail "wamvm_vm_only_${base%.pl}"
  fi
done

WAM_REFL_PROG="$ROOT/tests/wam_reflective.pl"
out="$(run_prog_out "$WAM_REFL_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_reflective"
fi

WAM_PROP_PROG="$ROOT/tests/wam_properties.pl"
out="$(run_prog_out "$WAM_PROP_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_properties"
fi

WAM_DYN_DB_PROG="$ROOT/tests/wam_dynamic_db.pl"
out="$(run_prog_out "$WAM_DYN_DB_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_dynamic_db"
fi

WAM_IO_PROG="$ROOT/tests/wam_stream_io.pl"
out="$(run_prog_out "$WAM_IO_PROG" 'ok.')"
out="${out%$'\n'}"
if [[ "$out" != "true" ]]; then
  echo "--- expected ---" >&2
  echo "true" >&2
  echo "--- got ---" >&2
  printf '%s\n' "$out" >&2
  fail "wam_stream_io"
fi

echo "ok"
