#!/bin/sh
set -u

steamvr=${STEAMVR:-/opt/steamvr}
vrdir=${DROIDDECK_VR_DIR:-/usr/local/lib/droiddeck-vr}
driver=$vrdir/droiddeck
layer=$vrdir/layer
logs=$HOME/.local/share/Steam/logs
state=${XDG_RUNTIME_DIR:-/tmp}/droiddeck-steamvr
umask 077
mkdir -p "$state"
chmod 700 "$state"

PKG_URL=https://steamdeck-packages.steamos.cloud/archlinux-deckard-hotfixes/release/0.3.x/deckard-steamvr-rel-r25358740%2B28b72a4f-1-aarch64.pkg.tar.zst
PKG_SHA256=90a600775eb3e8856dc475f83166a93a290a0b1f3b4e82a69c8e2a8f06b9ddc9

running() { pgrep -x vrserver >/dev/null 2>&1; }
installed() { [ -x "$steamvr/bin/linuxarm64/vrstartup" ]; }
quest_settings() {
    backup=$HOME/.local/share/droiddeck/steamvr-quest-settings.json
    [ "$1" != restore ] || [ -f "$backup" ] || return 0
    python3 "${DROIDDECK_VR_SETTINGS_HELPER:-/usr/local/bin/droiddeck-steamvr-settings}" "$1" \
        "$HOME/.local/share/Steam/config/steamvr.vrsettings" "$backup"
}

configure_graphics() {
    case "$(cat /sys/class/kgsl/kgsl-3d0/gpu_model 2>/dev/null)" in
        Adreno840*)
            export TU_DEBUG="$(
                IFS=', '
                set -f
                separator=
                for flag in ${TU_DEBUG:-}; do
                    case "$flag" in ''|sysmem) continue ;; esac
                    printf '%s%s' "$separator" "$flag"
                    separator=,
                done
            )"
            ;;
    esac
}

unregister() {
    if grep -qF "\"$steamvr\"" "$HOME/.config/openvr/openvrpaths.vrpath" 2>/dev/null; then
        rm -f "$HOME/.config/openvr/openvrpaths.vrpath"
    fi
    case "$(readlink "$HOME/.config/openxr/1/active_runtime.json" 2>/dev/null)" in
    "$steamvr"/*) rm -f "$HOME/.config/openxr/1/active_runtime.json" ;;
    esac
}

idle() {
    [ ! -f "$state/active" ] && ! running && ! pgrep -x vrstartup >/dev/null 2>&1 &&
        ( flock -n 9 ) 9>"$state/start.lock"
}

stop() {
    rm -f "$state/active"
    for n in vrcompositor vrserver vrmonitor vrdashboard vrstartup vrwebhelper; do
        for p in $(pgrep -x "$n"); do kill "$p" 2>/dev/null; done
    done
    for p in $(pgrep -f "$steamvr/bin/linuxarm64/vrcompositor-launcher"); do kill "$p" 2>/dev/null; done
    attempts=0
    while pgrep -x 'vrserver|vrcompositor' >/dev/null 2>&1; do
        attempts=$((attempts + 1))
        if [ "$attempts" = 10 ]; then
            for p in $(pgrep -x 'vrserver|vrcompositor'); do kill -KILL "$p" 2>/dev/null; done
        fi
        [ "$attempts" -lt 20 ] || { echo 'SteamVR did not stop; preferences remain backed up' >&2; return 1; }
        sleep 0.1
    done
    unregister
    quest_settings restore
}

case "${1:-status}" in
launch)
    exec 8>"$state/launch.lock"
    flock -n 8 || exit 0
    if ! installed; then
        "$0" install || exit $?
        while [ -f "$state/installing" ]; do sleep 1; done
    fi
    "$0" start
    ;;
status)
    i=0; installed && i=1
    r=0; running && r=1
    if [ "$r" = 0 ] && [ -n "$(find "$state/active" -mmin +2 2>/dev/null)" ]; then rm -f "$state/active"; fi
    if idle; then unregister; fi
    n=0; [ -f "$state/installing" ] && n=1
    d=0; [ -f "$driver/bin/linuxarm64/driver_droiddeck.so" ] && [ -f "$layer/libVkLayer_droiddeck_vr.so" ] && d=1
    echo "installed=$i running=$r installing=$n driver=$d"
    ;;
install)
    exec 7>"$state/install.lock"
    flock -n 7 || { echo "already installing"; exit 0; }
    installed && { echo "SteamVR is already in $steamvr"; exit 0; }
    : > "$state/installing"
    trap 'rm -f "$state/installing"' EXIT
    cache=$HOME/.cache/droiddeck-vr
    mkdir -p "$cache"
    pkg=$cache/steamvr.pkg.tar.zst
    echo "downloading SteamVR (571 MB) from Valve's package server"
    curl -fsSL --retry 5 -C - -o "$pkg" "$PKG_URL" || { echo "download failed"; exit 1; }
    echo "$PKG_SHA256  $pkg" | sha256sum -c - >/dev/null 2>&1 || { echo "checksum mismatch: removing the download"; rm -f "$pkg"; exit 1; }
    tmp=$cache/extract
    rm -rf "$tmp" && mkdir -p "$tmp"
    zstd -dc "$pkg" | tar -x -C "$tmp" opt/steamvr || { echo "extract failed"; rm -rf "$tmp"; exit 1; }
    mkdir -p "$(dirname "$steamvr")"
    rm -rf "$steamvr" && mv "$tmp/opt/steamvr" "$steamvr" && rm -rf "$tmp" "$pkg"
    echo "SteamVR installed in $steamvr"
    ;;
stop)
    stop || exit 1
    ;;
unregister)
    if idle; then unregister; fi
    ;;
start)
    exec 9>"$state/start.lock"
    flock -n 9 || { echo "SteamVR start already in progress"; exit 0; }
    installed || { echo "SteamVR is not installed (droiddeck-steamvr install)"; exit 1; }
    [ -f "$driver/bin/linuxarm64/driver_droiddeck.so" ] || { echo "the Cardboard driver is missing from $driver"; exit 1; }
    if [ -f "$state/active" ] && { running || pgrep -x vrstartup >/dev/null 2>&1; }; then
        echo "DroidDeck SteamVR is already running"
        exit 0
    fi
    stop || exit 1
    sleep 1
    mkdir -p "$HOME/.config/openvr" "$HOME/.config/openxr/1" "$logs" "$HOME/.local/share/Steam/config"
    if [ -S "/run/droiddeck/droiddeck-xr-frames" ] && [ -z "${DROIDDECK_VR_PRESENT:-}" ]; then
        export DROIDDECK_VR_PRESENT=questwsi
    fi
    python3 - "$HOME/.local/share/Steam/config/steamvr.vrsettings" <<'PY'
import json
import os
from pathlib import Path
import sys
import tempfile

path = Path(sys.argv[1])
try:
    settings = json.loads(path.read_text()) if path.exists() else {}
    section = settings.setdefault("steamvr", {})
    defaults = {} if os.environ.get("DROIDDECK_VR_PRESENT", "").startswith("quest") else {"supersampleManualOverride": True, "supersampleScale": 1.0}
    changed = any(key not in section for key in defaults)
    for key, value in defaults.items():
        section.setdefault(key, value)
    driver = settings.get("driver_droiddeck")
    if isinstance(driver, dict) and driver.get("blocked_by_safe_mode") is True:
        driver["blocked_by_safe_mode"] = False
        changed = True
        print("SteamVR had blocked the DroidDeck driver after a crash; unblocked it")
    if changed:
        with tempfile.NamedTemporaryFile(mode="w", dir=path.parent, delete=False) as output:
            temporary = Path(output.name)
            try:
                json.dump(settings, output, indent=3)
                output.write("\n")
            except BaseException:
                temporary.unlink(missing_ok=True)
                raise
        try:
            os.replace(temporary, path)
        finally:
            temporary.unlink(missing_ok=True)
except (OSError, ValueError, AttributeError, TypeError) as error:
    print("Could not set SteamVR resolution defaults: " + str(error), file=sys.stderr)
try:
    (path.parent / "vrserver_crash_timestamp.txt").unlink()
    print("Removed SteamVR's crash marker so it starts without safe mode")
except FileNotFoundError:
    pass
except OSError as error:
    print("Could not remove SteamVR's crash marker: " + str(error), file=sys.stderr)
PY
    cat > "$HOME/.config/openvr/openvrpaths.vrpath" <<JSON
{
  "config": ["$HOME/.local/share/Steam/config"],
  "external_drivers": ["$driver"],
  "jsonid": "vrpathreg",
  "log": ["$logs"],
  "runtime": ["$steamvr"],
  "version": 1
}
JSON
    ln -sf "$steamvr/steamxr_linuxarm64.json" "$HOME/.config/openxr/1/active_runtime.json"
    chaperone=$HOME/.local/share/Steam/config/chaperone_info.vrchap
    [ -s "$chaperone" ] || cat > "$chaperone" <<'JSON'
{
   "jsonid" : "chaperone_info",
   "universes" : [
      {
         "collision_bounds" : [
            [ [ -1.0, 0.0, -1.0 ], [ -1.0, 2.43, -1.0 ], [ -1.0, 2.43, 1.0 ], [ -1.0, 0.0, 1.0 ] ],
            [ [ -1.0, 0.0, 1.0 ], [ -1.0, 2.43, 1.0 ], [ 1.0, 2.43, 1.0 ], [ 1.0, 0.0, 1.0 ] ],
            [ [ 1.0, 0.0, 1.0 ], [ 1.0, 2.43, 1.0 ], [ 1.0, 2.43, -1.0 ], [ 1.0, 0.0, -1.0 ] ],
            [ [ 1.0, 0.0, -1.0 ], [ 1.0, 2.43, -1.0 ], [ -1.0, 2.43, -1.0 ], [ -1.0, 0.0, -1.0 ] ]
         ],
         "play_area" : [ 2.0, 2.0 ],
         "seated" : { "translation" : [ 0.0, -1.6, 0.0 ], "yaw" : 0.0 },
         "standing" : { "translation" : [ 0.0, 0.0, 0.0 ], "yaw" : 0.0 },
         "universeID" : "2"
      }
   ],
   "version" : 5
}
JSON

    export VK_LAYER_PATH="$layer${VK_LAYER_PATH:+:$VK_LAYER_PATH}"
    export VK_INSTANCE_LAYERS="VK_LAYER_DROIDDECK_vr${VK_INSTANCE_LAYERS:+:$VK_INSTANCE_LAYERS}"
    export VK_LOADER_LAYERS_DISABLE="${VK_LOADER_LAYERS_DISABLE:-~implicit~}"
    configure_graphics
    export LD_PRELOAD="$layer/libdroiddeck_vr_scheduling.so${LD_PRELOAD:+:$LD_PRELOAD}"
    if [ "${DROIDDECK_VR_PRESENT:-}" = questwsi ]; then
        quest_settings apply || exit 1
        startup_handed_off=0
        trap '[ "$startup_handed_off" = 1 ] || quest_settings restore' EXIT
        cvp=$steamvr/drivers/cv/bin/linuxarm64/libArcturusPerception.so
        if [ -f "$cvp" ]; then
            disabled=$cvp.droiddeck-disabled
            if [ -e "$disabled" ]; then disabled=$(mktemp "$disabled.XXXXXX") || exit 1; fi
            mv "$cvp" "$disabled" || exit 1
            echo "Disabled Frame-only CVP backend for Quest; SteamVR uses its normal compositor"
        fi
    fi
    if [ "${DROIDDECK_VR_PRESENT:-}" = questframes ]; then
        export DROIDDECK_VR_PRODUCER_LEASE=${DROIDDECK_VR_PRODUCER_LEASE:-1}
    fi
    if [ -z "${DROIDDECK_VR_DISPLAY_MODE:-}" ] && [ -x "$driver/bin/linuxarm64/xr-display-mode" ]; then
        mode=$("$driver/bin/linuxarm64/xr-display-mode") || mode=
        if [ -n "$mode" ]; then export DROIDDECK_VR_DISPLAY_MODE="$mode"; fi
    fi

    shims=$state/shims
    mkdir -p "$shims"
    if ! command -v xset >/dev/null 2>&1; then
        printf '#!/bin/sh\n# xset stand-in for vrwebhelper.sh: "q" only checks the display answers.\nexec xprop -root -display "${DISPLAY:-:0}" _NET_SUPPORTED >/dev/null 2>&1\n' > "$shims/xset"
        chmod +x "$shims/xset"
        export PATH="$shims:$PATH"
    fi

    : > "$state/active"
    cd "$steamvr/bin/linuxarm64" || exit 1
    startup_log=$logs/droiddeck-vrstartup.log
    ln -sf "$startup_log" "$state/vrstartup.log"
    setsid ./vrstartup 8>&- 9>&- > "$startup_log" 2>&1 < /dev/null &
    startup_handed_off=1
    echo "SteamVR starting; logs in $logs and $state/vrstartup.log"
    ;;
*)
    echo "usage: droiddeck-steamvr status|install|start|stop|launch|unregister" >&2
    exit 2
    ;;
esac
