#!/usr/local/bin/bash
#
set -euo pipefail
IFS=$'\n\t'
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

: ${CD:=""}
: ${VNC_ENABLE:="NO"}
: ${VNC_LISTEN:="127.0.0.1:5900"}
: ${PID_FILE:="/var/run/unifi.pid"}

############## Setup #########################

function cleanup {
    for vm in "${vms[@]}"; do
        log "Destroying bhyve vm $vm"
        bhyvectl "--vm=$vm" --destroy
        log "Destroyed bhyve vm $vm"
    done
}
vms=()
for sig in EXIT; do
  trap "set +e; sleep 10; cleanup" "$sig"
done

function die {
    local status_code="$1"
    shift
    (>&2 echo "${@}")
    exit "$status_code"
}

function log {
    (>&2 echo "${@}")
}

############## Program #########################

function main {
    start_vm
}

function start_vm {
    local name="unifi"



    # -H release the CPU when guest issues HLT instruction. Otherwise 100% of core will be consumed.
         # -s 3,ahci-cd,/vm/.iso/archlinux-2023.04.01-x86_64.iso \
             # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080,wait \
            # -s 29,fbuf,tcp=0.0.0.0:5900,w=1920,h=1080 \

    # TODO: Look into using nmdm instead of stdio for serial console
    if [ -n "$CD" ]; then
        additional_args+=("-s" "5,ahci-cd,$CD")
    fi
    if [ "$VNC_ENABLE" = "YES" ]; then
        additional_args+=("-s" "29,fbuf,tcp=$VNC_LISTEN,w=1920,h=1080")
    fi

    local bridge_name="bridge_vm"
    wait_for_bridge "$bridge_name"

    local mac_address
    mac_address=$(calculate_mac_address "$name")

    local bridge_link_name
    bridge_link_name=$(detect_available_link "${bridge_name}")

    additional_args+=("-s" "2:0,virtio-net,netgraph,path=${bridge_name}:,peerhook=${bridge_link_name},mac=${mac_address}")
    # additional_args+=("-s" "2:0,virtio-net,netgraph,path=${bridge_name}:,peerhook=${bridge_link_name},mac=${mac_address}" "-s" "3,ahci-cd,/home/talexander/disk.iso")
    vms+=("$name")
    while true; do
        set -x
        set +e
        bhyve \
            -D \
            -c 1 \
            -m 3G \
            -H \
            -o 'rtc.use_localtime=false' \
            -s 0,hostbridge \
            -s "4,nvme,/dev/zvol/zroot/vm/unifi/disk0" \
            -s 30,xhci,tablet \
            -s 31,lpc -l com1,stdio \
            -l "bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd,/vm/unifi/BHYVE_UEFI_VARS.fd" \
            "${additional_args[@]}" \
            "$name"
        # local bhyvepid=$!
        # echo "$bhyvepid" > "$PID_FILE"
        # wait $bhyvepid
        local exit_code=$?
        set +x
        set -e
        if [ $exit_code -eq 0 ]; then
            echo "Rebooting."
            sleep 5
        elif [ $exit_code -eq 1 ]; then
            echo "Powered off."
            break
        elif [ $exit_code -eq 2 ]; then
            echo "Halted."
            break
        elif [ $exit_code -eq 3 ]; then
            echo "Triple fault."
            break
        elif [ $exit_code -eq 4 ]; then
            echo "Exited due to an error."
            break
        fi
    done
}

function ng_exists {
    ngctl status "${1}" >/dev/null 2>&1
}

function wait_for_bridge {
    local bridge_name="$1"
    while ! ng_exists "${bridge_name}:"; do
        echo "${bridge_name} does not yet exist, sleeping."
        sleep 10
    done
}

function detect_available_link {
    local bridge_name="$1"
    local linknum=1
    while true; do
        local link_name="link${linknum}"
        if ! ng_exists "${bridge_name}:${link_name}"; then
            echo "$link_name"
            return
        fi
        linknum=$((linknum + 1))
        if [ "$linknum" -gt 90 ]; then
            (>&2 echo "No available links on bridge $bridge_name")
            exit 1
        fi
    done
}

function calculate_mac_address {
    local name="$1"
    local source
    source=$(md5 -r -s "$name" | awk '{print $1}')
    echo "06:${source:0:2}:${source:2:2}:${source:4:2}:${source:6:2}:${source:8:2}"
}


main "${@}"