#!/usr/bin/env bash
# M18 menus under quibble, driving components/MenuDemo.gdc:
#   1. an accelerator fires with no menu open — when its row does not exist,
#   2. clicking the File title opens its popup (pixels change),
#   3. Down/Down/Right walks into the data-driven Recent submenu,
#   4. Enter chooses a row, and the wire carries that record's id,
#   5. Escape closes one level at a time, not the whole stack,
#   6. right-pressing a row opens *that row's* context menu, and its wire
#      carries that row's id — no handler, no coordinates,
#   7. idle renders zero frames (menus request no timers).
set -euo pipefail
cd "$(dirname "$0")/.."

cargo build -p guiduck --example m18_demo

workdir=$(mktemp -d)
sock="$workdir/quibble.sock"
cleanup() {
    quibblectl --socket "$sock" quit >/dev/null 2>&1 || true
    rm -rf "$workdir"
}
trap cleanup EXIT

GUIDUCK_BACKEND=software GUIDUCK_TRACE_FRAMES=1 \
    quibble --socket "$sock" --width 300 --height 200 \
    target/debug/examples/m18_demo \
    >"$workdir/out.log" 2>"$workdir/err.log" &
for _ in $(seq 1 50); do
    quibblectl --socket "$sock" ping >/dev/null 2>&1 && break
    sleep 0.2
done
quibblectl --socket "$sock" wait-window --timeout-ms 30000 >/dev/null
sleep 1

frames() { grep -c '^guiduck-frame' "$workdir/err.log" || true; }
qc() { quibblectl --socket "$sock" "$@" >/dev/null; }
shot() { quibblectl --socket "$sock" screenshot -o "$workdir/$1" >/dev/null; }
last() { grep '^chose: ' "$workdir/out.log" | tail -1; }
fail=0

KEY_N=49; KEY_ENTER=28; KEY_ESC=1; KEY_DOWN=108; KEY_RIGHT=106
key() {
    quibblectl --socket "$sock" key "$1" press >/dev/null
    quibblectl --socket "$sock" key "$1" release >/dev/null
}
ctrl_key() {
    quibblectl --socket "$sock" key 29 press >/dev/null
    quibblectl --socket "$sock" key "$1" press >/dev/null
    quibblectl --socket "$sock" key "$1" release >/dev/null
    quibblectl --socket "$sock" key 29 release >/dev/null
}

# The File title's center, from the m18-menu-open golden's geometry.
FILE_X=22; FILE_Y=13
# The second "budget.csv" row. From `m18_menus.rs::print_demo_layout`, which
# prints the demo's real geometry — eyeballing a golden put this on the
# dropdown once the demo grew one.
ROW2_X=70; ROW2_Y=120

# 1: the accelerator fires while the menu is shut — its row does not exist.
ctrl_key $KEY_N
sleep 0.3
v=$(last)
if [ "$v" = "chose: new" ]; then
    echo "Ctrl+N fired with no menu open: $v"
else
    echo "FAIL: accelerator while closed; got '$v'" >&2; fail=1
fi

# The closed-menu baseline is taken *here*, after the accelerator: choosing a
# command rewrites the "last:" label, so a shot from before it would never
# match a closed window again, however closed the menus were.
shot s0.png

# 2: clicking the title opens the popup.
qc pointer-move $FILE_X $FILE_Y
qc click
sleep 0.4
shot s1.png
if ! cmp -s "$workdir/s0.png" "$workdir/s1.png"; then
    echo "clicking File opened its menu"
else
    echo "FAIL: File did not open" >&2; fail=1
fi

# 3+4: Down to "New", Down to "Recent", Right into the submenu, then Enter
#      chooses its first row — built from `(for doc recents :key doc.id)`,
#      and the wire carries that record's id.
key $KEY_DOWN
key $KEY_DOWN
key $KEY_RIGHT
sleep 0.4
shot s2.png
if ! cmp -s "$workdir/s1.png" "$workdir/s2.png"; then
    echo "Right opened the Recent submenu"
else
    echo "FAIL: submenu did not open" >&2; fail=1
fi
key $KEY_DOWN
key $KEY_ENTER
sleep 0.4
v=$(last)
if [ "$v" = "chose: recent 1" ]; then
    echo "Enter chose a data-driven row, with its id: $v"
else
    echo "FAIL: submenu Enter; got '$v'" >&2; fail=1
fi
# The label now reads "recent 1", so this closed state is its own baseline —
# and step 5's second Escape has to land right back on it.
shot s3.png
if ! cmp -s "$workdir/s2.png" "$workdir/s3.png"; then
    echo "choosing dismissed the open menus"
else
    echo "FAIL: the stack survived a selection" >&2; fail=1
fi

# 5: Escape closes one level, not the stack. Open File, open Recent, escape
#    once: Recent goes, File stays.
qc pointer-move $FILE_X $FILE_Y
qc click
sleep 0.3
key $KEY_DOWN
key $KEY_DOWN
key $KEY_RIGHT
sleep 0.4
shot s4.png
key $KEY_ESC
sleep 0.4
shot s5.png
if ! cmp -s "$workdir/s4.png" "$workdir/s5.png" && ! cmp -s "$workdir/s3.png" "$workdir/s5.png"; then
    echo "Escape closed one level and left File up"
else
    echo "FAIL: Escape did not close exactly one level" >&2; fail=1
fi
key $KEY_ESC
sleep 0.4
shot s6.png
# Back to exactly the state after the selection: menus gone, label unchanged.
# That is what makes it a closure rather than merely a change.
if cmp -s "$workdir/s3.png" "$workdir/s6.png"; then
    echo "a second Escape closed the bar menu"
else
    echo "FAIL: the second Escape left something up" >&2; fail=1
fi

# 6: a right-press opens the row's own context menu at the pointer, and
#    choosing from it carries that row's record id — the context menu was
#    declared inside the `for`, so it closed over its own loop variable.
qc pointer-move $ROW2_X $ROW2_Y
# evdev right button.
qc click 273
sleep 0.4
shot s7.png
if ! cmp -s "$workdir/s3.png" "$workdir/s7.png"; then
    echo "right-press opened the row's context menu"
else
    echo "FAIL: no context menu appeared" >&2; fail=1
fi
key $KEY_DOWN
key $KEY_DOWN
key $KEY_ENTER
sleep 0.4
v=$(last)
if [ "$v" = "chose: forget 2" ]; then
    echo "the context menu's wire carried its own row's id: $v"
else
    echo "FAIL: context menu selection; got '$v'" >&2; fail=1
fi

# 7: nothing is animating, so an idle window renders nothing.
before=$(frames)
sleep 3
after=$(frames)
if [ "$before" = "$after" ]; then
    echo "idle rendered zero frames ($before)"
else
    echo "FAIL: idle rendered $((after - before)) frames" >&2; fail=1
fi

exit $fail
