#!/usr/bin/env bash
# shellcheck disable=SC1091,SC2154
# sudo pacman -S --needed qemu-base qemu-ui-gtk qemu-audio-pipewire edk2-ovmf
# (swap qemu-ui-gtk for qemu-ui-sdl/qemu-ui-curses if you prefer; set DISPLAY_BACKEND to match)
# (audio backend is a separate pkg: qemu-audio-pipewire (default), qemu-audio-pa, qemu-audio-alsa; set AUDIO_BACKEND to match)
#
# Dev test VM for archinstoo. Boots an ISO under QEMU.
# ISO building is intentionally NOT done here, use isos/ISOMOD for that/default Arch ISO.
# Credits to @Softer, modified for archinstoo.
#
# Host artifacts (created in repo root, git-ignored):
#   .dev-disk.qcow2     VM disk image (qemu-img)
#   .dev-ovmf-vars.fd   persistent UEFI NVRAM (copied from OVMF_VARS)
#
# Usage:
#   ./TVM            - fresh disk, boot ISO
#   ./TVM keep   | k - reuse disk, boot ISO
#   ./TVM boot   | b - boot from installed disk (no ISO)
#   ./TVM clean  | c - remove disk and NVRAM (ISOs untouched)
#   ./TVM help   | h - show this help
# Env overrides:
#   DEV_ISO            explicit ISO path (skips discovery)
#   RAM, CPUS          VM resources (default 4G, 4)
#   DISK_SIZE          new disk size (default 30G)
#   DISPLAY_BACKEND    qemu -display value (default 'gtk,zoom-to-fit=off')
#   VGA                qemu -vga value (default 'std'; 'virtio' needs qemu-hw-display-virtio-vga)
#   GL                 'on' swaps video for virtio-vga-gl + display gl=on (virgl 3D accel).
#                      Overrides VGA. niri/DMS have no software fallback, black-screen
#                      without this. Needs the full module chain (vga-gl alone won't load):
#                      qemu-hw-display-virtio-{gpu,gpu-gl,vga,vga-gl} + qemu-ui-opengl
#   AUDIO_BACKEND      qemu -audiodev value (default 'pipewire'; 'pa'/'alsa' need the matching qemu-audio-* pkg)

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$SCRIPT_DIR"
DISK="$PROJECT_DIR/.dev-disk.qcow2"
OVMF_VARS="$PROJECT_DIR/.dev-ovmf-vars.fd"
ISOMOD_OUT="$PROJECT_DIR/isos/a"
DEV_ISO_DIR="$PROJECT_DIR/.dev-iso"
DISK_SIZE="${DISK_SIZE:-64G}"
RAM="${RAM:-4G}"
CPUS="${CPUS:-4}"
DISPLAY_BACKEND="${DISPLAY_BACKEND:-gtk,zoom-to-fit=off}"
VGA="${VGA:-std}"
GL="${GL:-off}"
AUDIO_BACKEND="${AUDIO_BACKEND:-pipewire}"
ARG="${1:-default}"

OVMF_CODE=""
OVMF_VARS_ORIG=""

err() {
	echo "ERROR: $*" >&2
	exit 1
}

probe_ovmf() {
	local pairs=(
		"/usr/share/edk2/x64/OVMF_CODE.4m.fd:/usr/share/edk2/x64/OVMF_VARS.4m.fd"
		"/usr/share/edk2-ovmf/x64/OVMF_CODE.fd:/usr/share/edk2-ovmf/x64/OVMF_VARS.fd"
		"/usr/share/edk2/ovmf/OVMF_CODE.fd:/usr/share/edk2/ovmf/OVMF_VARS.fd"
		"/usr/share/OVMF/OVMF_CODE_4M.fd:/usr/share/OVMF/OVMF_VARS_4M.fd"
		"/usr/share/OVMF/OVMF_CODE.fd:/usr/share/OVMF/OVMF_VARS.fd"
	)
	local pair code vars
	for pair in "${pairs[@]}"; do
		code="${pair%%:*}"
		vars="${pair##*:}"
		if [ -f "$code" ] && [ -f "$vars" ]; then
			OVMF_CODE="$code"
			OVMF_VARS_ORIG="$vars"
			return 0
		fi
	done
	err "OVMF firmware not found. Install 'edk2-ovmf' or similar on your distro."
}

check_host_deps() {
	local missing=() cmd
	for cmd in qemu-system-x86_64 qemu-img; do
		command -v "$cmd" >/dev/null 2>&1 || missing+=("$cmd")
	done
	if [ ${#missing[@]} -gt 0 ]; then
		err "missing host commands: ${missing[*]} (install qemu-base)"
	fi
}

find_iso() {
	if [ -n "${DEV_ISO:-}" ]; then
		[ -f "$DEV_ISO" ] || err "DEV_ISO=$DEV_ISO not found"
		printf '%s\n' "$DEV_ISO"
		return
	fi
	{ ls -t "$ISOMOD_OUT"/*.iso "$DEV_ISO_DIR"/*.iso 2>/dev/null || true; } | head -n1
}

require_iso() {
	local iso
	iso="$(find_iso)"
	if [ -z "$iso" ]; then
		err "No ISO found. Build one with isos/ISOMOD, drop one in $DEV_ISO_DIR/ or $ISOMOD_OUT, or set DEV_ISO=/path/to.iso"
	fi
	printf '%s\n' "$iso"
}

case "$ARG" in
-h | --help | help)
	sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'
	exit 0
	;;
clean | c)
	rm -fv "$DISK" "$OVMF_VARS"
	exit 0
	;;
boot | b)
	check_host_deps
	probe_ovmf
	[ -f "$DISK" ] || err "Disk missing, run without args first"
	BOOT_ORDER="c"
	ATTACH_ISO=false
	;;
keep | k)
	check_host_deps
	probe_ovmf
	[ -f "$DISK" ] || err "Disk missing, run without args first"
	require_iso >/dev/null
	BOOT_ORDER="d"
	ATTACH_ISO=true
	;;
default)
	check_host_deps
	probe_ovmf
	require_iso >/dev/null
	rm -f "$DISK"
	qemu-img create -f qcow2 "$DISK" "$DISK_SIZE"
	BOOT_ORDER="d"
	ATTACH_ISO=true
	;;
*)
	err "Unknown argument: $ARG (try -h)"
	;;
esac

[ -f "$OVMF_VARS" ] || cp "$OVMF_VARS_ORIG" "$OVMF_VARS"

# virgl needs both ends: GL-capable device AND gl=on on the display.
# -device help skips unloaded modules; probe with device-specific help instead.
if [ "$GL" = "on" ]; then
	if qemu-system-x86_64 -device virtio-vga-gl,help 2>&1 | grep -q "not found"; then
		err "virtio-vga-gl not loadable (install qemu-hw-display-virtio-{gpu,gpu-gl,vga,vga-gl} and qemu-ui-opengl)"
	fi
	VIDEO_ARGS=(-device virtio-vga-gl -display "$DISPLAY_BACKEND,gl=on")
else
	VIDEO_ARGS=(-vga "$VGA" -display "$DISPLAY_BACKEND")
fi

# shellcheck disable=SC2054 # commas are qemu device/netdev value syntax, not array separators
QEMU_ARGS=(
	-machine q35
	-cpu host
	-enable-kvm
	-m "$RAM"
	-smp "$CPUS"
	-drive "file=$DISK,format=qcow2,if=virtio"
	-device virtio-net-pci,netdev=net0
	-netdev user,id=net0
	"${VIDEO_ARGS[@]}"
	-audiodev "$AUDIO_BACKEND,id=snd0"
	-device ich9-intel-hda
	-device hda-duplex,audiodev=snd0
	-monitor stdio
	-drive "if=pflash,format=raw,readonly=on,file=$OVMF_CODE"
	-drive "if=pflash,format=raw,file=$OVMF_VARS"
	-boot "order=$BOOT_ORDER"
)

if [ "$ATTACH_ISO" = "true" ]; then
	ISO="$(require_iso)"
	QEMU_ARGS+=(-cdrom "$ISO")
fi

trap 'stty sane' EXIT
qemu-system-x86_64 "${QEMU_ARGS[@]}"
