#!/usr/bin/env bash

function os_support() {
    echo freebsd \
    kubuntu \
    lubuntu \
    macos \
    ubuntu \
    ubuntu-budgie \
    ubuntu-kylin \
    ubuntu-mate \
    ubuntu-studio \
    windows \
    xubuntu
}

function releases_freebsd(){
    echo 12_2 \
    13_0
}

function releases_macos() {
    echo high-sierra \
    mojave \
    catalina \
    big-sur
}

function releases_ubuntu() {
    echo bionic \
    focal \
    hirsute \
    devel
}

function languages_windows() {
    echo Arabic \
    Brazilian Portuguese \
    Bulgarian \
    Chinese \(Simplified\) \
    Chinese \(Traditional\) \
    Croatian \
    Czech \
    Danish \
    Dutch \
    English \
    English International \
    Estonian \
    Finnish \
    French \
    French Canadian \
    German \
    Greek \
    Hebrew \
    Hungarian \
    Italian \
    Japanese \
    Korean \
    Latvian \
    Lithuanian \
    Norwegian \
    Polish \
    Portuguese \
    Romanian \
    Russian \
    Serbian Latin \
    Slovak \
    Slovenian \
    Spanish \
    Spanish \(Mexico\) \
    Swedish \
    Thai \
    Turkish \
    Ukrainian
}


function releases_windows() {
    echo 8 \
    10 \
    11
}

function unattended_windows() {
    cat << 'EOF' > "${1}"
<?xml version="1.0" encoding="utf-8"?>
<unattend xmlns="urn:schemas-microsoft-com:unattend"
  xmlns:wcm="http://schemas.microsoft.com/WMIConfig/2002/State"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <!--
       For documentation on components:
       http://technet.microsoft.com/en-us/library/ff699038.aspx
  -->

  <settings pass="generalize">
    <!--
         The PersistAllDeviceInstalls setting indicates whether all plug and
         play devices on the destination computer remain installed during the
         generalize configuration pass.
    -->
    <component name="Microsoft-Windows-PnPSysprep"
      processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35"
      language="neutral"
      versionScope="nonSxS">
      <PersistAllDeviceInstalls>true</PersistAllDeviceInstalls>
    </component>
  </settings>

  <settings pass="windowsPE">
    <component name="Microsoft-Windows-Setup" processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <DiskConfiguration>
        <Disk wcm:action="add">
          <DiskID>0</DiskID>
          <WillWipeDisk>true</WillWipeDisk>
          <CreatePartitions>
            <CreatePartition wcm:action="add">
              <Order>1</Order>
              <Type>EFI</Type>
              <Size>100</Size>
            </CreatePartition>
            <CreatePartition wcm:action="add">
              <Order>2</Order>
              <Type>MSR</Type>
              <Size>512</Size>
            </CreatePartition>
            <CreatePartition wcm:action="add">
              <Order>3</Order>
              <Type>Primary</Type>
              <Extend>true</Extend>
            </CreatePartition>
          </CreatePartitions>
          <ModifyPartitions>
            <ModifyPartition wcm:action="add">
              <Order>1</Order>
              <PartitionID>1</PartitionID>
              <Label>System</Label>
              <Format>FAT32</Format>
            </ModifyPartition>
            <ModifyPartition wcm:action="add">
              <Order>2</Order>
              <PartitionID>3</PartitionID>
              <Label>Windows</Label>
              <Letter>C</Letter>
              <Format>NTFS</Format>
            </ModifyPartition>
          </ModifyPartitions>
        </Disk>
        <WillShowUI>OnError</WillShowUI>
      </DiskConfiguration>
      <ImageInstall>
        <OSImage>
          <WillShowUI>OnError</WillShowUI>
          <InstallTo>
            <DiskID>0</DiskID>
            <PartitionID>3</PartitionID>
          </InstallTo>
          </OSImage>
      </ImageInstall>
      <UserData>
        <AcceptEula>true</AcceptEula>
        <ProductKey>
          <key>VK7JG-NPHTM-C97JM-9MPGT-3V66T</key>
          <WillShowUI>Never</WillShowUI>
        </ProductKey>
      </UserData>
      <DynamicUpdate>
        <Enable>true</Enable>
        <WillShowUI>Never</WillShowUI>
      </DynamicUpdate>
    </component>

    <component name="Microsoft-Windows-PnpCustomizationsWinPE"
      publicKeyToken="31bf3856ad364e35" language="neutral"
      versionScope="nonSxS" processorArchitecture="amd64">

      <!--
           This makes the VirtIO drivers available to Windows, assuming that
           the VirtIO driver disk
           (https://alt.fedoraproject.org/pub/alt/virtio-win/latest/images/bin/)
           is available as drive E:
      -->
      <DriverPaths>
        <PathAndCredentials wcm:action="add" wcm:keyValue="1">
          <Path>E:\amd64\w10</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="2">
          <Path>E:\viogpudo\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="3">
          <Path>E:\viorng\w10\amd64</Path>
        </PathAndCredentials>
        <PathAndCredentials wcm:action="add" wcm:keyValue="4">
          <Path>E:\NetKVM\w10\amd64</Path>
        </PathAndCredentials>
      </DriverPaths>
    </component>
  </settings>

  <settings pass="specialize">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <ComputerName>*</ComputerName>
      <OEMInformation>
        <Manufacturer>Wimpys World</Manufacturer>
        <Model>Quickemu</Model>
        <SupportHours>24/7</SupportHours>
        <SupportPhone></SupportPhone>
        <SupportProvider>Wimpys World</SupportProvider>
        <SupportURL>https://github.com/wimpysworld/quickemu/issues</SupportURL>
      </OEMInformation>
      <OEMName>Wimpys World</OEMName>
    </component>
  </settings>

  <settings pass="oobeSystem">
    <component name="Microsoft-Windows-Shell-Setup" processorArchitecture="amd64"
      publicKeyToken="31bf3856ad364e35" language="neutral" versionScope="nonSxS">
      <OOBE>
        <HideOEMRegistrationScreen>true</HideOEMRegistrationScreen>
        <HideWirelessSetupInOOBE>true</HideWirelessSetupInOOBE>
      </OOBE>
    </component>
  </settings>
</unattend>
EOF
}

function web_get() {
    local DIR="${2}"
    local FILE=""
    local URL="${1}"
    if [ -n "${3}" ]; then
        FILE="${3}"
    else
        FILE="${URL##*/}"
    fi

    mkdir -p "${DIR}" 2>/dev/null
    if ! wget --quiet --continue --show-progress --progress=bar:force:noscroll "${URL}" -O "${DIR}/${FILE}"; then
        echo "ERROR! Failed to download ${URL}. Try running 'quickget' again."
        exit 1
    fi
}

function zsync_get() {
    local DIR="${2}"
    local FILE=""
    local OUT=""
    local URL="${1}"
    FILE="${URL##*/}"
    if [ -n "${3}" ]; then
        OUT="${3}"
    else
        OUT="${FILE}"
    fi

    mkdir -p "${DIR}" 2>/dev/null
    if ! zsync "${URL}.zsync" -i "${DIR}/${OUT}" -o "${DIR}/${OUT}"; then
        echo "ERROR! Failed to download ${URL}.zsync"
        exit 1
    fi

    if [ -e "${DIR}/${FILE}.zs-old" ]; then
        rm -v "${DIR}/${FILE}.zs-old"
    fi
}

function make_vm_dir() {
    if ! mkdir -p "${VM_PATH}" 2>/dev/null; then
      echo "ERROR! Unable to create directory ${VM_PATH}"
    fi
}

function make_vm_config() {
    local IMAGE_FILE=""
    local ISO_FILE=""
    local IMAGE_TYPE=""
    local GUEST=""
    IMAGE_FILE="${1}"
    ISO_FILE="${2}"
    if [[ "${OS}" == "freebsd" ]]; then
        GUEST="freebsd"
        IMAGE_TYPE="iso"
    elif [[ "${OS}" == *"ubuntu"* ]]; then
        GUEST="linux"
        IMAGE_TYPE="iso"
    elif [ "${OS}" == "macos" ]; then
        GUEST="macos"
        IMAGE_TYPE="img"
    elif [ "${OS}" == "windows" ]; then
        GUEST="windows"
        IMAGE_TYPE="iso"
    fi

    if [ ! -e "${OS}-${RELEASE}.conf" ]; then
        echo "Making VM configuration for ${OS}-${RELEASE}..."
        cat << EOF > "${OS}-${RELEASE}.conf"
guest_os="${GUEST}"
disk_img="${VM_PATH}/disk.qcow2"
${IMAGE_TYPE}="${VM_PATH}/${IMAGE_FILE}"
EOF
        if [ -n "${ISO_FILE}" ]; then
            echo "fixed_iso=\"${VM_PATH}/${ISO_FILE}\"" >> "${OS}-${RELEASE}.conf"
        fi

        if [ "${OS}" == "macos" ]; then
            echo "macos_release=\"${RELEASE}\"" >> "${OS}-${RELEASE}.conf"
        fi

        # Enable TPM for Windows 11
        if [ "${OS}" == "windows" ] && [ ${RELEASE} -ge 11 ]; then
            echo "tpm=\"on\"" >> "${OS}-${RELEASE}.conf"
        fi
    fi
}

function start_vm_info() {
    echo
    echo "To start your ${OS} ${RELEASE} virtual machine run:"
    echo "    quickemu --vm ${OS}-${RELEASE}.conf"
    echo
}

function get_freebsd() {
    # For future releases, use dvd1 iso files.
    local URL=""
    local DL_BASE="https://download.freebsd.org/ftp/releases/amd64/amd64/ISO-IMAGES"
    local VERSION=""

    case ${RELEASE} in
        12_2|13_0) VERSION=${RELEASE//_/.};;
        *)
            echo "ERROR! FreeBSD ${RELEASE} is not a supported release."
            releases_freebsd
            exit 1
            ;;
    esac

    URL="${DL_BASE}/${VERSION}/FreeBSD-${VERSION}-RELEASE-amd64-dvd1.iso"

    ISO="FreeBSD-${VERSION}-RELEASE-amd64-dvd1.iso"
    web_get ${URL} ${VM_PATH}
    make_vm_dir
    make_vm_config ${ISO}
    start_vm_info
}

function get_macos() {
    local CWD=""
    local MACRECOVERY=""

    case ${RELEASE} in
        high-sierra)
            BOARD_ID="Mac-7BA5B2D9E42DDD94"
            MLB="00000000000J80300";;
        mojave)
            BOARD_ID="Mac-7BA5B2DFE22DDD8C"
            MLB="00000000000KXPG00";;
        catalina)
            BOARD_ID="Mac-CFF7D910A743CAAF"
            MLB="00000000000PHCD00";;
        big-sur)
            BOARD_ID="Mac-E43C1C25D4880AD6"
            MLB="00000000000000000";;
        *) echo "ERROR! Unknown release: ${RELEASE}"
           releases_macos
           exit 1;;
    esac

    # Use a bundled macrecovery if possible
    CWD="$(dirname ${0})"
    if [ -x "${CWD}/macrecovery" ]; then
        MACRECOVERY="${CWD}/macrecovery"
    elif [ -x /usr/bin/macrecovery ]; then
        MACRECOVERY="/usr/bin/macrecovery"
    else
        web_get "https://raw.githubusercontent.com/acidanthera/OpenCorePkg/master/Utilities/macrecovery/macrecovery.py" "${HOME}/.quickemu"
        MACRECOVERY="python3 ${HOME}/.quickemu/macrecovery.py"
        sed -i 's/\<env python\>/env python3/g' "${MACRECOVERY}"
    fi

    if [ -z "${MACRECOVERY}" ]; then
        echo "ERROR! Can not find a usable macrecovery.py."
        exit 1
    fi

    make_vm_dir

    # Get firmware
    web_get "https://github.com/kholia/OSX-KVM/raw/master/OpenCore-Catalina/OpenCore.qcow2" "${VM_PATH}"
    web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_CODE.fd" "${VM_PATH}"
    if [ ! -e "${VM_PATH}/OVMF_VARS-1024x768.fd" ]; then
        web_get "https://github.com/kholia/OSX-KVM/raw/master/OVMF_VARS-1024x768.fd" "${VM_PATH}"
    fi

    if [ ! -e "${VM_PATH}/RecoveryImage.chunklist" ]; then
        echo "Downloading ${RELEASE}..."
        ${MACRECOVERY} \
            --board-id "${BOARD_ID}" \
            --mlb "${MLB}" \
            --basename RecoveryImage \
            --outdir "${VM_PATH}" \
            download
    fi

    if [ -e "${VM_PATH}/RecoveryImage.dmg" ] && [ ! -e "${VM_PATH}/RecoveryImage.img" ]; then
        echo "Converting RecoveryImage..."
        qemu-img convert "${VM_PATH}/RecoveryImage.dmg" -O raw "${VM_PATH}/RecoveryImage.img"
    fi

    make_vm_config RecoveryImage.img
    start_vm_info
}

function get_ubuntu() {
    local DEVEL="daily-live"
    local ISO=""
    local PROJECT=""
    local RELEASES=""
    local URL=""

    case ${OS} in
      kubuntu|lubuntu|ubuntu|ubuntu-budgie|ubuntu-mate|xubuntu)
        PROJECT="${OS}";;
      ubuntu-kylin)
        PROJECT="ubuntukylin";;
      ubuntu-studio)
        PROJECT="ubuntustudio"
        DEVEL="dvd";;
      *)
        echo "ERROR! ${OS} is not a recognised Ubuntu flavour."
        exit 1;;
    esac

    if [ "${RELEASE}" == "devel" ]; then
        URL="http://cdimage.ubuntu.com/${PROJECT}/${DEVEL}/current"
    elif [ "${PROJECT}" == "ubuntu" ]; then
        URL="http://releases.ubuntu.com/${RELEASE}"
    else
        URL="http://cdimage.ubuntu.com/${PROJECT}/releases/${RELEASE}/release"
    fi

    RELEASES=$(releases_ubuntu)
    if [[ "${RELEASES}" != *"${RELEASE}"* ]]; then
        echo "ERROR! ${RELEASE} is not a supported release."
        releases_ubuntu
        exit 1
    fi

    make_vm_dir

    echo "Downloading SHA256SUMS..."
    web_get "${URL}/SHA256SUMS" "${VM_PATH}"

    ISO=$(grep 'desktop\|dvd' "${VM_PATH}/SHA256SUMS" | grep amd64 | cut -d' ' -f2 | sed 's|*||g')

    echo "Downloading "${URL}/${ISO}"..."
    if [ "${RELEASE}" == "devel" ]; then
        zsync_get "${URL}/${ISO}" "${VM_PATH}" "${OS}-${RELEASE}.iso"
        make_vm_config "${OS}-${RELEASE}.iso"
    else
        web_get "${URL}/${ISO}" "${VM_PATH}"
        echo "Checking SHA256SUMS..."
        cd "${VM_PATH}"
        if ! sha256sum --check SHA256SUMS --ignore-missing --status; then
            echo "ERROR! ${ISO} doesn't match ${VM_PATH}/SHA256SUMS. Try running 'quickget' again."
            exit 1
        else
            echo "All good."
        fi
        cd ..
        make_vm_config "${ISO}"
    fi

    start_vm_info
}

# Adapted from https://gist.github.com/hongkongkiwi/15a5bf16437315df256c118c163607cb
function get_windows() {
    case ${RELEASE} in
      8|10|11) true;;
      *) echo "ERROR! Windows ${RELEASE} is not supported."
         releases_windows
         exit 1
         ;;
    esac

    local ARCH="x64"
    local LANG_CODE="en"
    local LATEST_WINDOWS_VERSION=""
    local WINDOWS_NAME=""
    local VERSION_ID=""
    local EDITION_ID=""
    local LANGUAGE_ID=""
    local FILE_NAME=""
    local ARCH_ID=""
    local DOWNLOAD_INFO=""
    local DOWNLOAD_ID=""
    local DOWNLOAD_URL=""

    echo "Getting Windows ${RELEASE} URL..."
    WINDOWS_VERSIONS=$(wget -q -O- "https://tb.rg-adguard.net/php/get_version.php?type_id=1" | jq '.versions | sort_by(-(.version_id | tonumber))')
    LATEST_WINDOWS_VERSION=$(echo "${WINDOWS_VERSIONS}" | jq -c 'map(select(.name | contains("Windows '${RELEASE}'")))[0]')

    WINDOWS_NAME=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .name)
    VERSION_ID=$(echo "${LATEST_WINDOWS_VERSION}" | jq -r .version_id)

    case ${RELEASE} in
        8) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows 8.1 Pro + Core").edition_id');;
        10|11) EDITION_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_edition.php?version_id=${VERSION_ID}&lang=name_${LANG_CODE}" | jq -r '.editions[] | select(.name_'${LANG_CODE}'=="Windows '${RELEASE}'").edition_id');;
    esac

    LANGUAGE_ID=$(wget -q -O- "https://tb.rg-adguard.net/php/get_language.php?edition_id=${EDITION_ID}&lang=name_${LANG_CODE}" | jq -r '.languages[] | select(.name_'${LANG_CODE}'=="'"${LANG_NAME}"'").language_id')
    ARCH_INFO=$(wget -q -O- "https://tb.rg-adguard.net/php/get_arch.php?language_id=${LANGUAGE_ID}")
    FILE_NAME=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).name')
    ARCH_ID=$(echo "${ARCH_INFO}" | jq -r '.archs[] | select(.name | contains("'${ARCH}'")).arch_id')
    DOWNLOAD_INFO=$(wget -q -O- "https://tb.rg-adguard.net/dl.php?fileName=${ARCH_ID}&lang=en")
    DOWNLOAD_ID=$(echo "${DOWNLOAD_INFO}" | grep -oP '(?<=https:\/\/tb\.rg-adguard\.net/dl\.php\?go=)[0-9a-z]+')
    DOWNLOAD_URL="https://tb.rg-adguard.net/dl.php?go=${DOWNLOAD_ID}"

    make_vm_dir

    echo "Downloading ${WINDOWS_NAME}..."
    web_get "${DOWNLOAD_URL}" "${VM_PATH}" "${FILE_NAME}"

    echo "Downloading virtio-win.iso..."
    web_get "https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/virtio-win.iso" "${VM_PATH}"

    #if [ ! -e "${VM_PATH}/unattended.iso" ]; then
        case ${RELEASE} in
            10|11)
                echo "Making unattended.iso"
                unattended_windows "${VM_PATH}/unattended/autounattend.xml"
                mkisofs -quiet -l -o "${VM_PATH}/unattended.iso" "${VM_PATH}/unattended/"
                ;;
        esac
    #fi
    make_vm_config "${FILE_NAME}" "virtio-win.iso"

    start_vm_info
}

if [ -n "${1}" ]; then
    OS="${1,,}"
else
    echo "ERROR! You must specify an OS:"
    os_support
    exit 1
fi

if [ -n "${2}" ]; then
    RELEASE="${2,,}"
else
    echo "ERROR! You must specify an OS release name."
    if [ "${OS}" == "freebsd" ]; then
        releases_freebsd
    elif [ "${OS}" == "macos" ]; then
        releases_macos
    elif [[ "${OS}" == *"ubuntu"* ]]; then
        releases_ubuntu
    elif [ "${OS}" == "windows" ]; then
        releases_windows
    fi
    exit 1
fi

VM_PATH="${OS}-${RELEASE}"

if [ "${OS}" == "macos" ]; then
    get_macos
elif [[ "${OS}" == *"freebsd" ]]; then
    get_freebsd
elif [[ "${OS}" == *"ubuntu"* ]]; then
    get_ubuntu
elif [ "${OS}" == "windows" ]; then
    if [ -n "${3}" ]; then
        LANG_NAME="${3}"
        LANG_NAMES=$(languages_windows)
        if [[ "${LANG_NAMES}" != *"${LANG_NAME}"* ]]; then
            echo "ERROR! ${LANG_NAME} is not a supported language:"
            languages_windows
            exit 1
        fi
    else
        LANG_NAME="English International"
    fi
    get_windows "${LANG_NAME}"
else
    echo "ERROR! You must specify an OS:"
    os_support
    exit 1
fi