#!/bin/bash
# Classroom v2 test lab driver.
#
# The development host is a VM with no /dev/kvm, so the desktop under test
# runs inside a systemd container instead of a virtual machine. Nothing this
# script does touches the host outside Docker.
#
# The session is nested: an outer headless sway exists only to give the
# desktop under test a display that grim can capture and that sway's IPC can
# aim a real pointer at. The outer compositor is lab equipment, not part of
# the classroom design.
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
NAME="${LAB_NAME:-classroom-v2-lab}"
IMAGE="${LAB_IMAGE:-classroom-v2-lab:plasma}"
DOCKERFILE="${LAB_DOCKERFILE:-tools/lab/Dockerfile.plasma}"
SHOTS="${LAB_SHOTS:-$ROOT/docs/screenshots}"

OUTER=wayland-1

session_env() {
    docker exec -u classroom \
        -e XDG_RUNTIME_DIR=/run/user/1000 \
        -e HOME=/home/classroom \
        -e LANG=C.UTF-8 \
        -e DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus \
        "$@"
}

# Talk to the outer lab compositor: grim, swaymsg.
outer() {
    local sock
    sock=$(docker exec "$NAME" sh -c 'ls /run/user/1000/sway-ipc.* 2>/dev/null | head -1')
    session_env -e WAYLAND_DISPLAY="$OUTER" -e SWAYSOCK="$sock" "$NAME" "$@"
}

# Talk to the Plasma session under test.
inner() {
    session_env -e WAYLAND_DISPLAY=wayland-0 -e XDG_CURRENT_DESKTOP=KDE \
        -e QT_QPA_PLATFORM=wayland "$NAME" "$@"
}

case "${1:-}" in
build)
    shift
    exec docker build -f "$ROOT/$DOCKERFILE" -t "$IMAGE" "$@" "$ROOT"
    ;;

up)
    docker rm -f "$NAME" >/dev/null 2>&1 || true
    docker run -d --name "$NAME" \
        --privileged --cgroupns=private \
        --tmpfs /run --tmpfs /run/lock \
        --shm-size=1g \
        -v "$ROOT:/srv/classroom:ro" \
        "$IMAGE" >/dev/null
    for _ in $(seq 1 90); do
        state=$(docker exec "$NAME" systemctl is-system-running 2>/dev/null || true)
        [[ "$state" == running || "$state" == degraded ]] && break
        sleep 1
    done
    for _ in $(seq 1 30); do
        docker exec "$NAME" test -S /run/user/1000/bus && break
        sleep 1
    done
    # startplasma looks for `qdbus`; Debian ships it as qdbus6.
    docker exec "$NAME" ln -sf /usr/bin/qdbus6 /usr/local/bin/qdbus
    echo "lab $NAME is up ($(docker exec "$NAME" systemctl is-system-running 2>/dev/null))"
    ;;

down)
    docker rm -f "$NAME" >/dev/null 2>&1 || true
    echo "lab $NAME is down"
    ;;

outer-up)
    # A headless compositor has no input devices, and a wl_seat with no
    # pointer capability never gives the session under test a pointer to
    # click with — sway's cursor commands then report success and move
    # nothing. So the lab runs wlroots' libinput backend as well, over the
    # devices the privileged container inherits, with seatd standing in for
    # the login session it does not have. Clicks in this experiment are
    # real pointer events arriving through libinput.
    docker exec "$NAME" sh -c 'pgrep -x seatd >/dev/null || \
        (SEATD_VTBOUND=0 setsid seatd -g input > /tmp/seatd.log 2>&1 &)'
    docker exec "$NAME" usermod -aG input classroom 2>/dev/null || true
    sleep 2
    session_env \
        -e WLR_BACKENDS=headless,libinput -e LIBSEAT_BACKEND=seatd \
        -e WLR_RENDERER=pixman -e LIBGL_ALWAYS_SOFTWARE=1 \
        -d "$NAME" sh -c 'exec sway -c /srv/classroom/tools/lab/outer-sway.conf \
            > /tmp/outer-sway.log 2>&1'
    for _ in $(seq 1 30); do
        docker exec "$NAME" test -S "/run/user/1000/$OUTER" && break
        sleep 1
    done
    echo "outer compositor on $OUTER"
    ;;

plasma-up)
    session_env \
        -e WAYLAND_DISPLAY="$OUTER" -e XDG_SESSION_TYPE=wayland \
        -e XDG_CURRENT_DESKTOP=KDE -e LIBGL_ALWAYS_SOFTWARE=1 \
        -e QT_LOGGING_RULES="kwin_scripting.debug=true" \
        -e XDG_CONFIG_DIRS=/etc/ekklesia-classroom/xdg:/etc/xdg \
        -d "$NAME" sh -c 'exec startplasma-wayland > /tmp/plasma.log 2>&1'
    for _ in $(seq 1 60); do
        docker exec "$NAME" pgrep -x plasmashell >/dev/null 2>&1 && break
        sleep 1
    done
    sleep 5
    echo "plasma session up"
    ;;

plasma-down)
    docker exec "$NAME" pkill -u classroom -f startplasma-wayland || true
    docker exec "$NAME" pkill -u classroom -x plasmashell || true
    docker exec "$NAME" pkill -u classroom -x kwin_wayland || true
    sleep 2
    echo "plasma session down"
    ;;

session)
    "$0" outer-up
    "$0" plasma-up
    ;;

shot)
    # shot NAME [GEOMETRY]  — geometry as grim wants it, e.g. "0,980 1920x100"
    name="${2:?usage: lab shot NAME}"
    if [[ -n "${3:-}" ]]; then
        outer grim -g "$3" /tmp/lab-shot.png >/dev/null
    else
        outer grim /tmp/lab-shot.png >/dev/null
    fi
    mkdir -p "$SHOTS"
    docker exec "$NAME" cat /tmp/lab-shot.png > "$SHOTS/$name.png"
    echo "$SHOTS/$name.png"
    ;;

click)
    # Absolute, through sway's own seat. This only works because the seat
    # has a pointer capability at all, which is what the uinput device in
    # `outer-up` is for.
    outer swaymsg seat - cursor set "$2" "$3" >/dev/null
    sleep 0.4
    outer swaymsg seat - cursor press button1 >/dev/null
    sleep 0.15
    outer swaymsg seat - cursor release button1 >/dev/null
    sleep 0.5
    ;;

rclick)
    outer swaymsg seat - cursor set "$2" "$3" >/dev/null
    sleep 0.4
    outer swaymsg seat - cursor press button3 >/dev/null
    sleep 0.15
    outer swaymsg seat - cursor release button3 >/dev/null
    sleep 0.5
    ;;

move)
    outer swaymsg seat - cursor set "$2" "$3" >/dev/null
    ;;

drag)
    outer swaymsg seat - cursor set "$2" "$3" >/dev/null
    sleep 0.3
    outer swaymsg seat - cursor press button1 >/dev/null
    sleep 0.3
    outer swaymsg seat - cursor set "$4" "$5" >/dev/null
    sleep 0.3
    outer swaymsg seat - cursor release button1 >/dev/null
    sleep 0.4
    ;;

key)
    shift
    outer wtype "$@"
    ;;

sh)
    shift
    exec docker exec -it "$NAME" bash "$@"
    ;;

run)      shift; session_env "$NAME" "$@" ;;
irun)     shift; session_env -it "$NAME" "$@" ;;
inner)    shift; inner "$@" ;;
inner-d)  shift; session_env -d -e WAYLAND_DISPLAY=wayland-0 -e XDG_CURRENT_DESKTOP=KDE \
              -e QT_QPA_PLATFORM=wayland "$NAME" "$@" ;;
inner-it) shift; session_env -it -e WAYLAND_DISPLAY=wayland-0 -e XDG_CURRENT_DESKTOP=KDE "$NAME" "$@" ;;
outer)    shift; outer "$@" ;;

*)
    cat <<USAGE
usage: tools/lab/lab COMMAND

  build            build the lab image
  up               start the lab container (systemd inside)
  down             remove it
  session          outer compositor + a Plasma session on top of it
  outer-up         just the outer compositor
  plasma-up        just the Plasma session
  plasma-down      stop the Plasma session
  shot NAME        capture the screen to docs/screenshots/NAME.png
  click X Y        press the pointer at a screen position
  drag X1 Y1 X2 Y2 drag from one position to another
  move X Y         move the pointer
  key ARGS...      wtype, aimed at the session
  inner CMD...     run CMD as a client of the Plasma session
  inner-d CMD...   the same, detached
  outer CMD...     run CMD against the lab compositor
  run CMD...       run CMD as the classroom user, no display
  sh               root shell in the lab
USAGE
    exit 1
    ;;
esac
