#!/usr/bin/env bash
#
# Drives one closed-loop experiment run for the model comparison harness:
#
#   1. Snapshots existing experiment logs on the phone (so we know which file
#      is the "new" one this run produces).
#   2. Prompts you to confirm Experiment Mode is armed and Start has been
#      tapped on the phone (skipped with --no-confirm).
#   3. Records t0_wall_ms in milliseconds, then plays the test video from the
#      start in QuickTime Player via osascript.
#   4. Sleeps for --duration seconds.
#   5. Pauses QuickTime.
#   6. Prompts you to tap Stop on the phone (skipped with --no-confirm).
#   7. Diffs the post-run file list against the snapshot, pulls only the
#      newly-written JSON file(s) into experiments/<run-id>/.
#   8. Writes experiments/<run-id>/manifest.json with t0_wall_ms, the video
#      path, model tag, device info, and duration.
#
# Usage:
#   tools/run_experiment.sh \
#       --video /abs/or/rel/path/test.mp4 \
#       --duration 10 \
#       --model-tag yolo11n_su_416 \
#       [--device RFCX10EM9LR] \
#       [--no-confirm]
#
# After two runs (model A then model B against the same video) point
# tools/compare_logs.py at experiments/ to produce the report.

set -euo pipefail

ADB="${ADB:-/Users/virtualintern/Library/Android/sdk/platform-tools/adb}"
# DEVICE may be passed via --device or EXP_DEVICE. Empty means "auto-detect:
# if exactly one device is connected, use it; otherwise error and require
# explicit --device".
DEVICE="${EXP_DEVICE:-}"
PKG="com.nate.posedetection.androidApp"
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
EXPERIMENTS_DIR="$REPO_ROOT/experiments"
REMOTE_LOGS_DIR="/sdcard/Android/data/$PKG/files/experiment_logs"

usage() {
    sed -n '3,28p' "$0"
    exit "${1:-1}"
}

VIDEO=""
DURATION=""
MODEL_TAG=""
NO_CONFIRM=0

while [ $# -gt 0 ]; do
    case "$1" in
        --video)      VIDEO="$2"; shift 2 ;;
        --duration)   DURATION="$2"; shift 2 ;;
        --model-tag)  MODEL_TAG="$2"; shift 2 ;;
        --device)     DEVICE="$2"; shift 2 ;;
        --no-confirm) NO_CONFIRM=1; shift ;;
        -h|--help)    usage 0 ;;
        *) echo "unknown arg: $1" >&2; usage ;;
    esac
done

[ -n "$VIDEO" ]     || { echo "error: --video is required" >&2; exit 2; }
[ -n "$DURATION" ]  || { echo "error: --duration is required" >&2; exit 2; }
[ -n "$MODEL_TAG" ] || { echo "error: --model-tag is required" >&2; exit 2; }

if [ ! -f "$VIDEO" ]; then
    echo "error: video file not found: $VIDEO" >&2
    exit 2
fi
VIDEO_ABS="$(cd "$(dirname "$VIDEO")" && pwd)/$(basename "$VIDEO")"

# Sanitize model tag for filesystem use (run-id slug).
SAFE_TAG="$(printf '%s' "$MODEL_TAG" | tr -c 'A-Za-z0-9._-' '_' | sed 's/^_*//; s/_*$//')"
[ -n "$SAFE_TAG" ] || SAFE_TAG="unnamed"

# Resolve / verify device.
if [ -z "$DEVICE" ]; then
    # Auto-detect: only succeed if exactly one device is attached.
    CONNECTED="$("$ADB" devices | awk 'NR>1 && $2=="device" {print $1}')"
    NUM_CONNECTED="$(printf '%s\n' "$CONNECTED" | grep -c . || true)"
    if [ "$NUM_CONNECTED" -eq 0 ]; then
        echo "error: no devices connected. Plug in the phone and run 'adb devices'." >&2
        exit 3
    elif [ "$NUM_CONNECTED" -gt 1 ]; then
        echo "error: multiple devices connected — pass --device <serial> to disambiguate." >&2
        echo "       connected:" >&2
        printf '         %s\n' $CONNECTED >&2
        exit 3
    fi
    DEVICE="$CONNECTED"
    echo "==> auto-selected device: $DEVICE"
fi
if ! "$ADB" -s "$DEVICE" get-state >/dev/null 2>&1; then
    echo "error: device $DEVICE is not connected." >&2
    echo "       adb devices output:" >&2
    "$ADB" devices >&2
    exit 3
fi

now_ms() { python3 -c 'import time; print(int(time.time()*1000))'; }

confirm() {
    if [ "$NO_CONFIRM" -eq 1 ]; then return 0; fi
    printf '%s [y/N] ' "$1"
    local response
    read -r response
    case "$response" in
        [yY]|[yY][eE][sS]) return 0 ;;
        *) return 1 ;;
    esac
}

list_remote_logs() {
    "$ADB" -s "$DEVICE" shell "ls -1 $REMOTE_LOGS_DIR 2>/dev/null" \
        | tr -d '\r' \
        | grep '\.json$' \
        || true
}

# 1. Snapshot existing logs so we can diff after the run.
echo "==> Snapshotting existing logs on $DEVICE..."
LOGS_BEFORE="$(list_remote_logs)"
EXISTING_COUNT=$(printf '%s\n' "$LOGS_BEFORE" | grep -c '\.json$' || true)
echo "    ($EXISTING_COUNT existing log file(s))"

# 2. Confirm the phone side is armed.
if ! confirm "Phone ready? Model picked, Experiment Mode on, Start tapped?"; then
    echo "Aborted by user."
    exit 0
fi

# 3. Record t0 and play the video.
T0_WALL_MS="$(now_ms)"
echo "==> t0_wall_ms = $T0_WALL_MS"
echo "==> Playing $VIDEO_ABS in QuickTime..."
osascript \
    -e 'tell application "QuickTime Player" to activate' \
    -e "tell application \"QuickTime Player\" to open POSIX file \"$VIDEO_ABS\"" \
    -e 'tell application "QuickTime Player" to set current time of front document to 0' \
    -e 'tell application "QuickTime Player" to play front document' >/dev/null

# 4. Wait the requested duration.
echo "==> Sleeping ${DURATION}s..."
sleep "$DURATION"

# 5. Pause playback.
echo "==> Pausing QuickTime..."
osascript -e 'tell application "QuickTime Player" to pause front document' >/dev/null

STOPPED_WALL_MS="$(now_ms)"

# 6. Prompt the user to tap Stop on the phone.
if [ "$NO_CONFIRM" -eq 0 ]; then
    echo
    echo "==> Tap 'Stop Experiment' on the phone now."
    printf "    Press Enter when done... "
    read -r _
fi

# 7. Diff the file list, pull anything new.
LOGS_AFTER="$(list_remote_logs)"
NEW_LOGS="$(comm -13 <(printf '%s\n' "$LOGS_BEFORE" | sort -u) <(printf '%s\n' "$LOGS_AFTER" | sort -u) | grep '\.json$' || true)"

RUN_ID="${T0_WALL_MS}_${SAFE_TAG}"
RUN_DIR="$EXPERIMENTS_DIR/$RUN_ID"
mkdir -p "$RUN_DIR"

if [ -z "$NEW_LOGS" ]; then
    echo "warning: no new log files appeared on the phone. Was Experiment Mode" >&2
    echo "         actually started/stopped? Was a model selected? The manifest" >&2
    echo "         will still be written so you can debug, but the run is empty." >&2
else
    echo "==> Pulling new log(s) into $RUN_DIR/"
    while IFS= read -r f; do
        [ -z "$f" ] && continue
        "$ADB" -s "$DEVICE" pull "$REMOTE_LOGS_DIR/$f" "$RUN_DIR/" >/dev/null
        echo "    pulled $f"
    done <<< "$NEW_LOGS"
fi

# 8. Write the manifest. Use python for JSON to avoid quoting headaches.
DEVICE_MANUFACTURER="$("$ADB" -s "$DEVICE" shell getprop ro.product.manufacturer 2>/dev/null | tr -d '\r' || true)"
DEVICE_MODEL_NAME="$("$ADB" -s "$DEVICE" shell getprop ro.product.model 2>/dev/null | tr -d '\r' || true)"
DEVICE_LABEL="$DEVICE_MANUFACTURER $DEVICE_MODEL_NAME"

NEW_LOGS_JOINED="$(printf '%s\n' "$NEW_LOGS" | tr '\n' ',' | sed 's/,$//')"

RUN_ID="$RUN_ID" \
MODEL_TAG="$MODEL_TAG" \
VIDEO_ABS="$VIDEO_ABS" \
DURATION="$DURATION" \
T0_WALL_MS="$T0_WALL_MS" \
STOPPED_WALL_MS="$STOPPED_WALL_MS" \
DEVICE="$DEVICE" \
DEVICE_LABEL="$DEVICE_LABEL" \
NEW_LOGS_JOINED="$NEW_LOGS_JOINED" \
python3 -c '
import json, os
files = [f for f in os.environ["NEW_LOGS_JOINED"].split(",") if f]
manifest = {
    "run_id": os.environ["RUN_ID"],
    "model_tag": os.environ["MODEL_TAG"],
    "video_path": os.environ["VIDEO_ABS"],
    "duration_seconds": float(os.environ["DURATION"]),
    "t0_wall_ms": int(os.environ["T0_WALL_MS"]),
    "stopped_wall_ms": int(os.environ["STOPPED_WALL_MS"]),
    "device_serial": os.environ["DEVICE"],
    "device_label": os.environ["DEVICE_LABEL"].strip(),
    "log_files": files,
}
print(json.dumps(manifest, indent=2))
' > "$RUN_DIR/manifest.json"

echo "==> Wrote $RUN_DIR/manifest.json"
echo
echo "Run dir: $RUN_DIR"
echo "Next:    tools/compare_logs.py experiments/   (after at least 2 runs against the same video)"
