#!/usr/bin/bash

if [ $# -lt 1 ]; then
	echo $0 '<file.img> [qemu arguments]'
	exit 1
fi

if ! which qemu-kvm >/dev/null 2>&1; then
	echo qemu is not installed
	exit 1
fi

if [ -z "$TMPDIR" ]; then
	TMPDIR=/tmp
fi

WANT_SPICE=1
SPICE_REMOTE=0
SCREEN=0

while :; do
	case "$1" in
		--nospice)
			WANT_SPICE=0
			;;
		--spiceremote)
			SPICE_REMOTE=1
			;;
		--screen)
			SCREEN=1
			;;
		*)
			break
			;;
	esac
	shift
done

FILE=$1
shift
WIN10=0
WINOLD=0
NBSD=0
FACT=0
EFI=0
RAW=""
OVMF_CODE=/usr/share/qemu/ovmf-x86_64-opensuse-code.bin
OVMF_VARS_SRC=/usr/share/qemu/ovmf-x86_64-opensuse-vars.bin
OVMF_VARS="$FILE.vars"
if [[ "$FILE" == *win10* ]]; then
	WIN10=1
elif [[ "$FILE" == *win* ]]; then
	WINOLD=1
fi
if [[ "$FILE" == *nbsd* ]]; then
	NBSD=1
fi
if [[ "$FILE" == *fact* ]]; then
	FACT=1
	EFI=1
fi
if [[ "$FILE" == *-efi* ]]; then
	EFI=1
fi
if [[ "$FILE" == *32.* ]]; then
	EFI=0
fi
if [[ "$FILE" == *.img ]]; then
	RAW=",format=raw"
fi

QEMU_VER=`qemu-kvm --version | sed 's@.*version \([0-9.]*\)[ ,(].*@\1@'`
QEMU_MAJOR=${QEMU_VER%%.*}

declare -a DRIVE=(-drive file="$FILE$RAW")
if [ "$QEMU_MAJOR" -ge 2 ]; then
	DRIVE[-1]="${DRIVE[-1]},discard=unmap"
	if [ $WINOLD = 0 -a $NBSD = 0 ]; then
		DRIVE[-1]="${DRIVE[-1]},if=none,id=hd"
		DRIVE+=(-device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd)
	fi
fi

declare -a HW=()
if ! echo $@ | grep -q -- '-m '; then
	MEM=`sed -ne 's/^MemTotal: *\([0-9]*\) *kB$/\1/p' /proc/meminfo`
	MEM=$(($MEM/5/1024))

	if [ $MEM -lt 100 ]; then
		MEM=100
	fi

	HW+=(-m $MEM)
fi

if ! echo $@ | grep -q -- '-smp '; then
	SMP=`grep -c ^processor.*: /proc/cpuinfo`
	HW+=(-smp $SMP)
fi

declare -a SOUND=()
if ( which pulseaudio &>/dev/null && pulseaudio --check) || which pipewire &>/dev/null; then
	if [ $WINOLD = 1 ]; then
		SOUND=(-device AC97)
	else
		SOUND=(-device intel-hda -device hda-duplex)
	fi
fi

function find_free_port() {
	FREE_PORT="$1"
	while :; do
		/usr/sbin/ss -tnl "sport = $FREE_PORT" | grep -q ":$FREE_PORT\>" || break
		FREE_PORT=$((FREE_PORT+1))
	done
	echo "$FREE_PORT"
}

declare -a SPICE=()
if [ "$WANT_SPICE" = 1 ] && ( test "$SPICE_REMOTE" = 1 || which spicy >/dev/null 2>&1) ; then
	SPICE_PORT="/tmp/qemu-spice.$$.sock"
	# gl=on,
	SPICE_CONF="unix=on,addr=$SPICE_PORT"
	if [ "$SPICE_REMOTE" = 1 ]; then
		SPICE_PORT=`find_free_port 3333`
		SPICE_CONF="port=$SPICE_PORT"
	fi
	SPICE=(-spice disable-ticketing=on,$SPICE_CONF -device virtio-serial -monitor pty)
	SPICE+=(-chardev spicevmc,id=vdagent,name=vdagent -device virtserialport,chardev=vdagent,name=com.redhat.spice.0)
	SPICE+=(-chardev spicevmc,name=usbredir,id=usbredirchardev1 -device usb-redir,chardev=usbredirchardev1,id=usbredirdev1)
	SPICE+=(-chardev spicevmc,name=usbredir,id=usbredirchardev2 -device usb-redir,chardev=usbredirchardev2,id=usbredirdev2)
fi

declare -a NET=(-net nic,model=virtio)
declare -a USB=()
if [ $WINOLD = 1 ]; then
	USB+=(-device ich9-usb-ehci1,multifunction=on,id=ehci)
	USB+=(-device ich9-usb-uhci1,masterbus=ehci.0,multifunction=on)
	USB+=(-device ich9-usb-uhci2,masterbus=ehci.0,firstport=2,multifunction=on)
	USB+=(-device ich9-usb-uhci3,masterbus=ehci.0,firstport=4,multifunction=on)
	NET+=(-net nic,model=rtl8139)
else
	USB+=(-device qemu-xhci,p2=8,p3=8)
fi

declare -a OTHER=()
TELNET_PORT=""
SERIAL_WAIT_RE=""
if [ $WINOLD = 1 -o $WIN10 = 1 ]; then
	NET=(-net user,smb=/home/smb,hostfwd=tcp::`find_free_port 3389`-:3389 "${NET[@]}")
	OTHER+=(-rtc base=localtime -vga qxl)
	if [ $WIN10 = 1 ]; then
		OTHER+=(--device virtio-balloon)
	fi
else
	if [ $SCREEN = 1 ]; then
		SERIAL="pty"
		SERIAL_WAIT_RE='redirected to /dev/.*(label serial'
	else
		TELNET_PORT=`find_free_port 2323`
		SERIAL="telnet:localhost:$TELNET_PORT,server"
		SERIAL_WAIT_RE='QEMU waiting for connection.*telnet'
	fi
	NET=(-net user,tftp=/home/xslaby/tftp,bootfile=/pxelinux.0,hostfwd=tcp::`find_free_port 2222`-:22,hostfwd=tcp::`find_free_port 3632`-:3632 "${NET[@]}")
	OTHER+=(-serial $SERIAL --device virtio-balloon)
fi

if [ $EFI = 1 ]; then
	if [ -f "$OVMF_CODE" -a -f "$OVMF_VARS_SRC" ]; then
		if [ ! -f "$OVMF_VARS" ]; then
			cp -v "$OVMF_VARS_SRC" "$OVMF_VARS"
		fi
		OTHER+=(-drive if=pflash,format=raw,unit=0,readonly=on,file="$OVMF_CODE")
		OTHER+=(-drive if=pflash,format=raw,unit=1,file="$OVMF_VARS")
	fi
fi

if [ $FACT = 1 ]; then
	OTHER+=(-device virtio-tablet-pci -device virtio-vga,virgl=on)
else
	OTHER+=(-device usb-tablet)
fi

PID_FILE="$TMPDIR/qemu.pid.$$"
declare -a QEMU_RUN=(qemu-kvm -k en-us -daemonize -pidfile "$PID_FILE")
QEMU_RUN+=("${HW[@]}" -device virtio-rng-pci "${DRIVE[@]}" "${SOUND[@]}")
QEMU_RUN+=("${USB[@]}" "${SPICE[@]}" "${NET[@]}" "${OTHER[@]}" "$@")

SCR="$TMPDIR/qemu.$$"

function finish() {
	echo Cleaning up
	rm -f "$SCR" "$PID_FILE" &>/dev/null
	if [ -n "$SPICE_PORT" ]; then
		rm -f "$SPICE_PORT" &>/dev/null
	fi
}

trap finish EXIT
finish

echo "${QEMU_RUN[@]}"
if [ -n "$TELNET_PORT" ]; then
	"${QEMU_RUN[@]}" |& tee $SCR >&2 &
else
	"${QEMU_RUN[@]}" |& tee $SCR >&2
fi
if [ $? -ne 0 ]; then
	exit 1
fi

if [ -n "$SERIAL_WAIT_RE" ]; then
	while ! [ -f "$PID_FILE" -a -f "$SCR" ] || ! grep -q "$SERIAL_WAIT_RE" "$SCR"; do
		usleep 100000
	done
fi
QEMU=`cat $PID_FILE`
rm -f $PID_FILE

if [ -n "$SPICE_PORT" -a "$SPICE_REMOTE" != 1 ]; then
	(while :; do
		spicy --uri="spice+unix://$SPICE_PORT" 2>/dev/null;
		if [ $? -lt 128 ]; then
			break;
		fi;
	done;
	kill $QEMU 2>/dev/null) &
fi

if [ -n "$TELNET_PORT" ]; then
	telnet localhost $TELNET_PORT
elif [ $SCREEN = 1 ]; then
	TTY=`sed -ne '/redirected to .*label serial/ s@.*redirected to \(/dev[^ ]*\).*@\1@ p' $SCR`
	if [ -c "$TTY" ]; then
		screen "$TTY"
	fi
fi

function kill_qemu() {
	echo Killing qemu
	kill $QEMU
	exit 0
}

trap kill_qemu INT TERM

# wait $QEMU  -- won't work
echo Waiting for qemu to finish
while [ -d /proc/$QEMU ]; do
	sleep 1
done
