#!/usr/bin/env bash
# Usage: deck-it                start the dashboard + extended display over USB
#        deck-it stop           tear it all down
#        deck-it env            print MON/PORT/VNC_PORT for this config
#        deck-it pair           pair the tablet for Wi-Fi by scanning a QR code
#        deck-it wifi           start over Wi-Fi
#        deck-it install        print the exec-once line for hypr-theme's autostart
#        deck-it uninstall      forget the generated trigger
#        deck-it watch-usb      start on plug-in, tear down on unplug (run from exec-once)
set -u
DIR="$(dirname "$(readlink -f "$0")")"
SELF="$DIR/deck-it"
CONF="${XDG_CONFIG_HOME:-$HOME/.config}/deck-it"
VNC="$CONF/vnc"
RUN="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/deck-it"

if [[ "${1:-}" == "env" ]]; then
    (cd "$DIR" && exec python3 -m deckit env)
fi

MON=TAB PORT=8765 VNC_PORT=5900
env_out=$(cd "$DIR" && python3 -m deckit env 2>&1) && eval "$env_out" || echo "Config problem: $env_out" >&2

teardown() {
    pkill -f "^bash $SELF watch" 2>/dev/null
    pkill -x wayvnc
    pkill -f '^python3 -m deckit' 2>/dev/null
    hyprctl output remove "$MON" >/dev/null 2>&1
    local s
    for s in $(adb devices | awk 'NR > 1 && $2 == "device" {print $1}'); do
        timeout 5 adb -s "$s" reverse --remove-all 2>/dev/null
    done
    rm -f "$RUN/serial"
}

tunnel() {
    adb -s "$1" reverse "tcp:$VNC_PORT" "localfilesystem:$RUN/vnc.sock" >/dev/null &&
        adb -s "$1" reverse "tcp:$PORT" "localfilesystem:$RUN/http.sock" >/dev/null
}

# The display stream needs a password over TLS; both live in a 0700 dir and AVNC gets them in the link.
vnc_setup() {
    mkdir -p -m 700 "$VNC"
    (
        umask 077
        [[ -s "$VNC/password" ]] || tr -dc A-Za-z0-9 </dev/urandom | head -c 24 > "$VNC/password"
        [[ -s "$VNC/tls_cert.pem" ]] || openssl req -x509 -newkey ec -pkeyopt ec_paramgen_curve:prime256v1 -nodes \
            -days 3650 -subj /CN=deck-it -keyout "$VNC/tls_key.pem" -out "$VNC/tls_cert.pem" 2>/dev/null
        printf 'enable_auth=true\nusername=deck-it\npassword=%s\nprivate_key_file=%s\ncertificate_file=%s\n' \
            "$(cat "$VNC/password")" "$VNC/tls_key.pem" "$VNC/tls_cert.pem" > "$VNC/config"
    )
}

open_avnc() {
    adb -s "$1" shell am start -a android.intent.action.VIEW \
        -d "'vnc://127.0.0.1:$VNC_PORT?VncUsername=deck-it&VncPassword=$(cat "$VNC/password")'" com.gaurav.avnc >/dev/null 2>&1
}

# A dropped Wi-Fi link can still report "device", and adb calls on it hang, so probe with a timeout.
alive() {
    timeout 6 adb -s "$1" shell true >/dev/null 2>&1
}

wifi_connect() {
    local addr
    for addr in $(adb mdns services | awk '/_adb-tls-connect/ {print $NF}' | sort -u); do
        timeout 5 adb connect "$addr" 2>/dev/null | grep -q '^connected\|already connected' &&
            alive "$addr" && { echo "$addr"; return 0; }
    done
    return 1
}

usb_present() {
    [[ "$(timeout 3 adb -d get-state 2>/dev/null)" == device ]]
}

case "${1:-}" in
stop)
    teardown
    echo "Stopped"
    exit 0
    ;;
teardown)
    teardown
    exit 0
    ;;
pair)
    command -v qrencode >/dev/null || { echo "Install qrencode first"; exit 1; }
    name="deck-it-$(tr -dc a-z0-9 </dev/urandom | head -c 8)"
    pass=$(tr -dc 0-9 </dev/urandom | head -c 8)
    echo "On the tablet: Developer options > Wireless debugging > Pair device with QR code, and scan this:"
    echo
    qrencode -t ansiutf8 -m 2 "WIFI:T:ADB;S:$name;P:$pass;;"
    echo "Waiting for the scan (2 minutes)..."
    for _ in {1..120}; do
        addr=$(adb mdns services | awk -v n="$name" '$1 == n && /_adb-tls-pairing/ {print $NF; exit}')
        if [[ -n "$addr" ]]; then
            adb pair "$addr" "$pass" | grep -q 'Successfully paired' || { echo "Pairing failed"; exit 1; }
            echo "Paired. Connecting..."
            for _ in {1..10}; do addr=$(wifi_connect) && break; sleep 2; done
            [[ -n "$addr" ]] && echo "Connected to $addr. Start with: deck-it wifi" || echo "Paired, but couldn't connect yet. Try: deck-it wifi"
            exit 0
        fi
        sleep 1
    done
    echo "No scan detected. Is the tablet on the same Wi-Fi network as this PC?"
    exit 1
    ;;
watch)
    # Runs in the background in Wi-Fi mode: reconnect when the tablet drops, then restore the tunnels.
    while sleep 5; do
        serial=$(cat "$RUN/serial" 2>/dev/null) || exit 0
        alive "$serial" && timeout 6 adb -s "$serial" reverse --list 2>/dev/null | grep -q "tcp:$PORT" && continue
        timeout 5 adb disconnect "$serial" >/dev/null 2>&1
        new=$(wifi_connect) || continue
        tunnel "$new" && echo "$new" > "$RUN/serial" && open_avnc "$new"
    done
    ;;
install)
    echo "Add this line to hypr-theme's autostart so the tablet is picked up at login and when plugged in:"
    echo "    exec-once = $SELF watch-usb"
    echo "On a new tablet, run 'deck-it' once by hand to accept the USB debugging prompt."
    exit 0
    ;;
uninstall)
    rm -f "$CONF/trigger"
    echo "Remove the 'exec-once = $SELF watch-usb' line from hypr-theme's autostart."
    exit 0
    ;;
setup-display)
    (cd "$DIR" && exec python3 -m deckit setup-display)
    ;;
watch-usb)
    # A user-space watcher, so it can reach the session: udev RUN= cannot.
    # Start on plug-in, tear down on unplug.
    known=""
    while sleep 5; do
        now=$(timeout 4 adb -d get-serialno 2>/dev/null)
        if [[ -n "$now" && "$now" != "$known" ]]; then
            "$SELF" start >/dev/null 2>&1 &
            known=$now
        elif [[ -z "$now" && -n "$known" ]]; then
            teardown
            known=""
        fi
    done
    ;;
wifi)
    echo "Looking for the tablet on Wi-Fi..."
    while :; do
        [[ "${2:-}" == --wait ]] && usb_present && { echo "Tablet is on USB, leaving it to the USB start"; exit 0; }
        ANDROID_SERIAL=$(wifi_connect) && break
        [[ "${2:-}" == --wait ]] || { echo "Not found. Turn on Wireless debugging on the tablet, or pair first with: deck-it pair"; exit 1; }
        sleep 10
    done
    ;;
"")
    echo "Waiting for tablet on USB (plug it in, allow USB debugging)..."
    timeout 60 adb wait-for-usb-device || { echo "No tablet found"; exit 1; }
    ANDROID_SERIAL=$(adb -d get-serialno)
    ;;
*)
    sed -n '2,9p' "$SELF" | sed 's/^# \{0,1\}//'
    exit 1
    ;;
esac
export ANDROID_SERIAL

for _ in {1..30}; do hyprctl monitors >/dev/null 2>&1 && break; sleep 1; done

(cd "$DIR" && python3 -m deckit setup-display) || exit 1

# Both servers listen on sockets in a 0700 dir, so only this user and its adb tunnel can reach them.
mkdir -p -m 700 "$RUN"
vnc_setup
# wayvnc leaves its socket behind when killed and then refuses to bind, so clear it first.
pgrep -x wayvnc >/dev/null || { rm -f "$RUN/vnc.sock"; setsid wayvnc -C "$VNC/config" -u -o "$MON" "$RUN/vnc.sock" >/dev/null 2>&1 & }
pgrep -f '^python3 -m deckit' >/dev/null || (cd "$DIR" && setsid python3 -m deckit >server.log 2>&1 &)
sleep 1

tunnel "$ANDROID_SERIAL" || { echo "Could not set up the tunnel to the tablet"; exit 1; }
# Opening the page with the secret stores it as a cookie in the tablet's browser, which unlocks the API.
token="$CONF/token"
for _ in {1..10}; do [[ -s "$token" ]] && break; sleep 0.5; done
adb shell am start -a android.intent.action.VIEW -d "http://127.0.0.1:$PORT/?t=$(cat "$token")" >/dev/null 2>&1
sleep 1
open_avnc "$ANDROID_SERIAL"

if [[ "${1:-}" == wifi ]]; then
    echo "$ANDROID_SERIAL" > "$RUN/serial"
    pgrep -f "^bash $SELF watch" >/dev/null || setsid bash "$SELF" watch >/dev/null 2>&1 &
fi

echo "Ready. Display is in AVNC, dashboard at http://127.0.0.1:$PORT"
