#!/bin/bash

set -eu

deviceid_url="" # set during installer startup but used also later

colorize() {
  local color component yellow_limit grey_limit green_limit value text \
        preseed_args

  component=$1
  yellow_limit=$2
  grey_limit=$3
  green_limit=$4
  value=$5
  text=$6

  color='grey'

  if [ -n "$preseed_json" ]; then
    preseed_args=$(printf %s "$preseed_json" \
                     | jq -r --arg component "$component" \
                          '.colorize[$component]') || true
    if [ -n "$preseed_args" -a "$preseed_args" != 'null' ]; then
      case "$component" in
        product_name)
          if printf "%s\n" "$value" | grep -qE "$preseed_args"; then
            color=green
          else
            color=red
          fi
          ;;
        *)
          yellow_limit="$(printf "%s\n" "$preseed_args" | awk '{ print $1 }')"
          grey_limit="$(  printf "%s\n" "$preseed_args" | awk '{ print $2 }')"
          green_limit="$( printf "%s\n" "$preseed_args" | awk '{ print $3 }')"
          ;;
      esac
    fi
  fi

  if [ "$component" = 'deviceid' ]; then
    if [[ "$device_identifier" =~ [0-9] ]]; then
      color=green
    else
      color=red
    fi
  elif [ "$component" != 'product_name' ]; then
    if [ "$value" -ge "$green_limit" ]; then
      color=green
    elif [ "$value" -ge "$grey_limit" ]; then
      color=grey
    elif [ "$value" -ge "$yellow_limit" ]; then
      color=yellow
    else
      color=red
    fi
  fi

  case "$color" in
    green)  echo -e "\e[1;32m${text}\e[0m" ;;
    yellow) echo -e "\e[1;33m${text}\e[0m" ;;
    red)    echo -e "\e[1;31m${text}\e[0m" ;;
    *)      echo "$text"                   ;;   # grey
  esac
}

get_smart_lettercode() {
  local i letter mask val

  val=$1
  mask=1

  for i in 0 1 2 3 4 5 6 7; do
    if [ "$((($val & $mask) && 1))" -eq 1 ]; then
      case "$i" in
        0) letter='P';;
        1) letter='O';;
        2) letter='B';;
        3) letter='F';;
        4) letter='A';;
        5) letter='T';;
        6) letter='E';;
        7) letter='S';;
      esac
    else
      letter='-'
    fi
    echo -n "$letter"
    mask=$(($mask << 1))
  done
}

list_disks() {
  local colorized_disk_info colorized_smart_status color_no_disk disk disk_devices \
        disk_label disk_model disk_size_in_gb smartctl_code smartctl_data \
        smartctl_json smart_color_status smart_lettercodes smart_status

  disk_devices=$(facter --json disks | jq -r '.disks | keys | .[]' \
                   | grep -E '^(mmcblk|nvme|sd|vd|xvda)')

  if [ -z "$disk_devices" ]; then
    color_no_disk=0
    colorized_disk_info=$(colorize disk_devices 1 1 1 "$color_no_disk" "NO DISK FOUND")
    printf "disk %-12s%s\n" "" "$colorized_disk_info"
    return
  fi

  for disk in $disk_devices; do
    smartctl_code=0
    smartctl_json=$(smartctl -a --json=c "/dev/${disk}") || smartctl_code=$?
    smart_lettercode=$(get_smart_lettercode "$smartctl_code")
    if smartctl_data=$(
      printf "%s" "$smartctl_json" | jq -r '
        if has("firmware_version") then
          "firmware:" + .firmware_version + " " else "" end
        + if has("power_on_time") and (.power_on_time|has("hours")) then
            "power_on_hours:" + (.power_on_time.hours|tostring) + " "
          else "" end
        + if has("ata_smart_attributes") and (.ata_smart_attributes|has("table")) then
             [.ata_smart_attributes.table[]
               | select(.name == "Host_Writes_GiB"
                          or .name == "TLC_NAND_GB_Writes")
               | .name + ":" + .raw.string] | join(" ")
           else "" end'); then
      disk_model=$(printf "%s" "$smartctl_json" | jq -r .model_name) \
                     || true
    else
      disk_model=$(facter "disks.${disk}.model") || true
      smartctl_data='NO SMART DATA'
    fi
    if [ -z "$disk_model" ]; then disk_model='unknown model'; fi

    disk_label="$(facter disks.${disk}.size) / ${disk_model}"
    colorized_disk_info=$(colorize disk \
                                   128000000000 256000000000 512000000000 \
                                   "$(facter disks.${disk}.size_bytes)"   \
                                   "$disk_label")

    if [ "$smartctl_code" -eq 0 ]; then
      smart_color_status=2
    else
      smart_color_status=0
    fi

    smart_status="$smart_lettercode : $smartctl_data"
    colorized_smart_status=$(
      colorize smart 1 1 2 "$smart_color_status" "$smart_status")

    printf "disk %-12s%s\n"  "${disk}:" "$colorized_disk_info"
    printf "SMART %-11s%s\n" "${disk}:" "$colorized_smart_status"
  done
}

check_bios_version() {
  if [ $(echo $(facter dmi.manufacturer)) = "HP" ]; then
    hp_ftp_url="https://ftp.ext.hp.com/pub/pcbios"
    mainboard_sysid=$(facter dmi.board.product)
    wget --timeout="2" "$hp_ftp_url/$mainboard_sysid/$mainboard_sysid.xml"
    if [ -e "$mainboard_sysid.xml" ]; then
      latest_bios="$(xmllint --xpath "string(/BIOS/Rel/@Ver)" $mainboard_sysid.xml)"
      if [ "$(echo $(facter dmi.bios.version) | awk '{ print $3 }')" = "$latest_bios" ]; then
        colorize "bios_version" 1 50 100 "100" "$(facter dmi.bios.version)"
      else
        colorize "bios_version" 1 50 100 "1" "$(facter dmi.bios.version)"
      fi
      rm $mainboard_sysid.xml
    else
      echo $(facter dmi.bios.version)
    fi
    return
  fi

  echo $(facter dmi.bios.version)
}

firmwaremode() {
  local fw_colorized_value

  if [ -d /sys/firmware/efi ]; then
    fw_colorized_value=$(colorize firmware 1 1 1 "100" "|| UEFI mode")
    echo "$fw_colorized_value"
  else
    fw_colorized_value=$(colorize firmware 1 1 1 "0" "|| Legacy mode")
    echo "$fw_colorized_value"
  fi
}

is_wifi6_compatible() {
  ### In "iw list" output, Wi-Fi 6 capabilities are listed under
  ### "HE PHY Capabilities" which stands for "High Efficiency" wireless.
  ### If no such capabilities are found, said section is not in the output.
  if [ -n "$(iw list | grep "HE PHY Capabilities")" ]; then
    colorize wifi 1 1 1 "100" "|| Wi-Fi 6 compatible"
  else
    colorize wifi 1 1 1 "0" "|| Not Wi-Fi 6 compatible"
  fi
}

inspect_efivars() {
  local check_for
  check_for="$1"
  efivardump=$(hexdump -v /sys/firmware/efi/efivars/MiscMobileKBCBuiltInConfig-fb3b9ece-4aba-4933-b49d-b4d67d892351 \
        2>/dev/null || echo -n 2)
  dlen=$(echo -n $efivardump | wc -c)
  # The length of this hexdump is 50 on EliteBook G5/G6 and 73 on G7+
  if [ $check_for = "--displaytype" ]; then
    if [ $dlen -eq "50" ]; then
      echo $efivardump | tail -c 17 | head -c 1
    elif [ $dlen -eq "73" ]; then
      echo $efivardump | tail -c 40 | head -c 1
    else
      echo -n "2" # we're not familliar with this variable format, do nothing
    fi
  elif [ $check_for = "--kblight" ]; then
    if [ $dlen -eq "50" ]; then
      echo $efivardump | tail -c 22 | head -c 1
    elif [ $dlen -eq "73" ]; then
      echo $efivardump | tail -c 45 | head -c 1
    else
      echo -n "2"
    fi
  fi
}

list_networks() {
  local netdev network_devices
  network_devices=$(facter --json networking.interfaces \
                      | jq -r '.["networking.interfaces"] | keys | .[]' \
                      | grep -v lo | sort)
  for netdev in $network_devices; do
    if [[ "${netdev}" = "wlan"* ]]; then
      printf "network %-9sIP %s / MAC %s %s\n" "${netdev}:"    \
             "$(facter networking.interfaces.${netdev}.ip)" \
             "$(facter networking.interfaces.${netdev}.mac)" \
             "$(is_wifi6_compatible)"
    else
      printf "network %-9sIP %s / MAC %s\n" "${netdev}:"    \
             "$(facter networking.interfaces.${netdev}.ip)" \
             "$(facter networking.interfaces.${netdev}.mac)"
    fi
  done
}

show_bluetooth() {
  local btdev btdev_str btmac colorvalue colorized_value

  btdev=$(hcitool dev | awk 'NR == 2 { print $1 }')
  btmac=$(hcitool dev | awk 'NR == 2 { print $2 }')

  btdev_str=''
  if [ -z "$btdev" ]; then
    btmac='not present'
    colorvalue=0
  else
    btmac="MAC ${btmac}"
    colorvalue=100
    btdev_str=" $btdev"
  fi

  colorized_value=$(colorize bluetooth 1 1 1 "$colorvalue" "$btmac")
  printf "bluetooth%-7s %s" "${btdev_str}:" "$colorized_value"
}

show_battery_status() {
  local battery_capacity battery_color_value colorized_battery_status \
        battery_status other_battery_info upower_output

  upower_output=$(upower --show-info \
                         /org/freedesktop/UPower/devices/battery_BAT0 \
                    2>/dev/null || true)

  battery_capacity=$(printf "%s" "$upower_output" \
    | awk '$1 == "capacity:" { sub(/%$/, "", $2); printf "%d\n", $2 }')

  battery_serial=$(printf %s "$upower_output" \
                     | awk '$1 == "serial:" { print $2, $3 }')

  other_battery_info=$(printf "%s\n" "$upower_output" \
    | awk '
        $1 == "energy:"             { energy             = $2 $3 }
        $1 == "energy-full:"        { energy_full        = $2 $3 }
        $1 == "energy-full-design:" { energy_full_design = $2 $3 }
        END {
          printf "%s/%s (design capacity: %s)", energy, energy_full,
            energy_full_design
        }
      ')

  if printf %s "$battery_serial" | grep -q '00001 2000/01/31'; then
    battery_color_value=0
    other_battery_info="${other_battery_info} | MISSING?"
  else
    battery_color_value="$battery_capacity"
  fi

  battery_status="${battery_capacity}% (capacity) | ${other_battery_info}"

  colorized_battery_status=$(colorize battery 70 80 90 "$battery_color_value" \
                                      "$battery_status")

  printf "battery:         %s\n" "$colorized_battery_status"
}

windows_license() {
  local machine_build_id windows_product_key

  windows_product_key=$(
    strings /sys/firmware/acpi/tables/MSDM 2>/dev/null \
      | awk '$1 ~ /^[0-9A-Z]{5}-[0-9A-Z]{5}-[0-9A-Z]{5}-[0-9A-Z]{5}-[0-9A-Z]{5}$/')
  if [ -z "$windows_product_key" ]; then
    colorize license 1 1 1 0 'No license'
    return 0
  fi

  machine_build_id=$(strings /sys/firmware/dmi/tables/DMI 2>/dev/null \
                       | grep '^BUILDID')
  if [ -n "$machine_build_id" ]; then
    machine_build_id=" (${machine_build_id})"
  fi

  printf %s "${windows_product_key}${machine_build_id}"
}

get_cpu_value() {
  # XXX This is a very crude way of evaluating processor "goodness".
  # XXX For example it does not consider AMD CPUs at all.
  case "$1" in
    *i3-*) echo 1 ;;
    *i5-*) echo 2 ;;
    *i7-*) echo 3 ;;
    *)     echo 0 ;;
  esac
}

darkdm_benchmark() {
  if [ ! -x /usr/local/sbin/puavo-benchmark ]; then
    echo 'puavo-benchmark is not available on this host' >&2
    return 1
  fi
  /usr/local/sbin/puavo-benchmark || return 1
  read -p '> press ENTER to continue ' answer
}

darkdm_common_systeminfo()
{
  local colorized_cpu colorized_display_resolution colorized_memory \
        colorized_product_name cpu cpu_text display_resolution \
        display_touch display_safety horz_resolution product_name sku_number this_image \
        this_release vert_resolution

  this_image=$(cat /etc/puavo-image/name)
  this_release=$(cat /etc/puavo-image/release)

  colorized_memory=$(colorize memory 4183116800 8366233600 16732467200 \
                              "$(facter memory.system.total_bytes)"    \
                              "$(facter memory.system.total)")
  cpu=$(facter processors.models.1)
  cpu_text="$(facter processors.count) CPUs / ${cpu}"
  colorized_cpu=$(colorize cpu 1 2 3 "$(get_cpu_value "$cpu")" "$cpu_text")

  colorized_display_resolution=''
  if [ -e /sys/class/graphics/fb0/virtual_size ]; then
    horz_resolution=$(awk -F, '$1 ~ /^[0-9]+$/ { print $1 }' \
                                /sys/class/graphics/fb0/virtual_size) || true
    vert_resolution=$(awk -F, '$2 ~ /^[0-9]+$/ { print $2 }' \
                        /sys/class/graphics/fb0/virtual_size) || true
    if [ -n "$horz_resolution" -a -n "$vert_resolution" ]; then
      colorized_display_resolution=$(colorize resolution 1280 1440 1920 \
        "$horz_resolution" "${horz_resolution}x${vert_resolution} (resolution)")
    fi
  fi
  if [ -z "$colorized_display_resolution" ]; then
    colorized_display_resolution='? (resolution)'
  fi

  display_touch=""
  if [ "$(udevadm info --export-db | grep "ID_INPUT_TOUCHSCREEN=1")" ]; then
    display_touch=$(colorize touch 1 50 100 "1" "|| TOUCH")
  fi

  display_safety=""
  if [ $(inspect_efivars --displaytype) = "1" ]; then
    display_safety=$(colorize safety 1 50 100 "1" "|| SAFETY")
  fi

  sku_number=$(cat /sys/devices/virtual/dmi/id/product_sku 2>/dev/null || true)
  if [ -z "$sku_number" ]; then
    sku_number='?'
  fi

  product_name=$(facter dmi.product.name) || true
  colorized_product_name=$(colorize product_name 0 0 0 "$product_name" \
                                    "$product_name")

  if [ -n "$image_server" ]; then
    echo "bootserver: ${image_server%:*}"
  fi

  cat <<EOF
image:      ${this_image}
release:    ${this_release}

bios:            $(facter dmi.bios.vendor) / $(check_bios_version) $(firmwaremode)
product:         $(facter dmi.manufacturer) / ${colorized_product_name}
cpu:             ${colorized_cpu}
memory:          ${colorized_memory} (total of $(dmidecode -t 17 | awk '/\tSize: [[:digit:]]{1,3}/ {print $0}' | wc --lines) modules)
$(list_disks)
display:         ${colorized_display_resolution} ${display_touch} ${display_safety}
$(list_networks)
$(show_bluetooth)
$(show_battery_status)
serial numbers:  $(facter dmi.product.serial_number) (machine) / $(facter dmi.board.serial_number) (board) / ${sku_number} (SKU)
Windows license: $(windows_license)
${device_identifier}
EOF
}

darkdm_short_systeminfo()
{
  printf "%s\n" "$common_systeminfo"
  cat <<EOF
some PCI devices:
$(lspci | awk '$2 == "Network" || $2 == "VGA"' | sed 's/^/  /')
EOF
}

darkdm_long_systeminfo()
{
  printf "%s\n" "$common_systeminfo"
  cat <<EOF

PCI devices:
$(lspci | sed 's/^/  /')

USB devices:
$(lsusb | sed 's/^/  /')
EOF
}

darkdm_send_sysinfo()
{
  if [ ! -x /usr/sbin/puavo-send-sysinfo-to-puavo ]; then
    return 0
  fi

  echo -n 'Sending system information to Puavo... '
  if timeout -k 3 30 /usr/sbin/puavo-send-sysinfo-to-puavo >/dev/null 2>&1; then
    echo 'OK.'
    return 0
  fi

  echo 'FAILED.'
  echo 'Try /usr/sbin/puavo-send-sysinfo-to-puavo if you want to know why.'
  return 1
}

darkdm_reboot()
{
  reboot
}

darkdm_reboot_to_firmware_setup()
{
  systemctl reboot --firmware-setup
}

darkdm_install()
{
  if ! /usr/sbin/puavo-install "$@"; then
    # give some time for users to read the error message, if there is any
    echo
    sleep 2

    # Try sending sysinfo anyway, we may have a successful registration and
    # partial installation done.
    darkdm_send_sysinfo || true

    return 1
  fi

  darkdm_send_sysinfo || true

  for i in $(seq 5 -1 1); do
    echo -n -e "\rRebooting in ${i}s. (Press Ctrl-C to cancel) "
    sleep 1 || return 1
  done
  echo -e "\rRebooting now!                               "

  darkdm_reboot
}

darkdm_print_help()
{
  cat <<'EOF'

Puavo OS Command Shell

Commands:
  benchmark              run some benchmarks (if available)
  bios                   configure BIOS (only on some supported machines)
  customdescription (cd) send a custom device description to inventory backend
  erase                  secure erase a disk (only on SSD's that support it)
  expert-install         install Puavo OS with more detailed questions
  expert-preinstall      install without registering, expert mode
  help                   show more detailed help
  info                   show machine information
  install                install Puavo OS to this device and reboot
  kbd [layout]           set keyboard layout
  make-install-disk      make a bootable usb flash drive or some such
  mount                  mount puavo filesystems (if host is already installed)
  nmtui                  configure network (not available on netboot devices)
  poweroff               poweroff the system
  preinstall             install without registering
  preseed-install (pi)   install with a preseed
  preseed-preinstall     install without registering but with a preseed
  reboot                 reboot the system
  reboot-to-firmware     reboot to firmware setup
  restoredisk            restore from bootserver (only on netboot devices)
  savedisk               save to bootserver (only on netboot devices)
  shell                  spawn a shell
  test                   run some hardware tests
  update                 update the local Puavo OS installation and reboot
EOF
}

darkdm_print_short_help() {
  darkdm_print_help \
    | awk '$1 !~ /benchmark|bios|customdescription|erase|expert|mount|preinstall|reboot|savedisk/'
}

darkdm_shell()
{
  /bin/bash
}

darkdm_mount()
{
  local old_puavo_hosttype puavo_hosttype

  if mountpoint -q /images && mountpoint -q /state; then
    return 0
  fi

  old_puavo_hosttype=$(puavo-conf puavo.hosttype)

  if ! {
    mkdir -p /state                                      \
      && mount /dev/mapper/puavo-state /state            \
      && read puavo_hosttype < /state/etc/puavo/hosttype \
      && puavo-conf puavo.hosttype "$puavo_hosttype"     \
      && mount /dev/mapper/puavo-images /images          \
      && /etc/puavo-conf/scripts/mount_local_partitions  \
      && /etc/puavo-conf/scripts/setup_state_partition
  }; then
    echo 'Could not setup host state for an update' >&2
    puavo-conf puavo.hosttype "$old_puavo_hosttype"
    return 1
  fi
}

darkdm_update()
{
  local image_name update_status

  update_status=0

  if ! darkdm_mount; then
    echo 'Could not mount /images and /state, aborting update.' >&2
    return 1
  fi

  if grep -qw puavo.hosttype=unregistered /proc/cmdline; then
    # We have booted as unregistered, which means that we probably do
    # not have booted the correct image (this can happen in case this
    # is registered to a different organisation than the bootserver and
    # the bootserver does not know about this host).
    # Update in a "normal way".
    if ! /usr/lib/puavo-ltsp-install/update-configuration; then
      echo 'Configuration update errors, yet continuing...' >&2
      update_status=1
    fi
    /usr/lib/puavo-ltsp-install/update-images false || update_status=1
  elif image_name=$(cat /etc/puavo-image/name 2>/dev/null); then
    if mountpoint -q /installimages; then
      /usr/sbin/puavo-install-and-update-ltspimages \
        --install-from-file "/installimages/${image_name}" "$image_name" \
          || update_status=1
    elif [ -e /run/puavo/nbd-server ]; then
      /usr/sbin/puavo-install-and-update-ltspimages \
        --install-from-nbd /dev/nbd0 "$image_name" || update_status=1
    else
      echo 'Can not find an update image, FAILED.' >&2
      update_status=1
    fi
  else
    update_status=1
  fi

  if ! /usr/lib/puavo-ltsp-install/update-configuration >/dev/null 2>&1; then
    echo 'Configuration update FAILED!' >&2
    update_status=1
  else
    echo 'Configuration update OK.'
  fi

  darkdm_send_sysinfo || true

  if [ "$update_status" -ne 0 ]; then
    return "$update_status"
  fi

  darkdm_reboot
}

darkdm_poweroff()
{
  poweroff
}

list_available_keyboard_layouts() {
  awk '
    /^! layout$/,/custom/ {
      if ($1 == "!" || $1 == "custom") { next }
      print $1
    }' /usr/share/X11/xkb/rules/base.lst \
    | sort
}

darkdm_kbd() {
  local new_layout possible_layouts
  new_layout=$1

  if [ -z "$new_layout" ]; then
    possible_layouts="$(list_available_keyboard_layouts \
                          | fmt -w 70 | sed 's/^/  /')"

    cat <<EOF
Choose from available layouts:
${possible_layouts}
EOF
    read -p 'Choose keyboard layout: ' new_layout
  fi

  if [ -z "$new_layout" ]; then
    echo 'Not changing keyboard layout'
  elif ckbcomp "$new_layout" 2>/dev/null | loadkeys -; then
    echo "Changed keyboard layout to \"${new_layout}\""
  else
    echo "Could not change keyboard layout" >&2
  fi

  sleep 0.5
  echo
}

darkdm_menu()
{
  local command forced initial_command prompt

  initial_command=$1
  forced=$2

  show_helpinfo=true

  while true; do
    if $show_helpinfo; then
      printf "%s\n" "$short_systeminfo"
      darkdm_print_short_help
    fi
    show_helpinfo=true

    if [ "$forced" = '--show-unforced-warning' ]; then
      cat <<-EOF >&2

	  !! Attempted to force preseed installation operation even though !!
	  !! this host already has a Puavo OS installation, unforced.      !!
	  !! (You can still install with preseed by pressing ENTER.)       !!

	EOF
    fi

    if [ -n "$initial_command" ]; then
      prompt="[${initial_command}]> "
    else
      prompt='> '
    fi

    if [ "$forced" = '--force' ]; then
      command="$initial_command"
    else
      read -e -p "$prompt" command command_args || return 1
      if [ -z "$command" -a -n "$initial_command" ]; then
        command="$initial_command"
      fi
    fi

    case "$command" in
      benchmark|be|ben|bench)
        darkdm_benchmark || return 1
        ;;
      bios|b|bi|bio)
        puavo-bios-config || return 1
        ;;
      crypt-install)
        darkdm_install crypt-install || return 1
        ;;
      crypt-preinstall)
        darkdm_install crypt-preinstall || return 1
        ;;
      customdescription|cd)
        # try sending a custom description to inventory system
        read -e -p "Custom description? " customdesc || return 1
        curl --cacert /etc/puavo-conf/rootca.pem \
          -d "$(jq -n --arg sn "$(facter dmi.product.serial_number)" --arg cd "$customdesc" '.serialnumber = $sn | .customdescription = $cd')" \
          --fail -H "Content-Type: application/json" --max-time 5 --silent -X POST "$deviceid_url" || echo "Send failed."
        ;;
      erase)
        puavo-disk-erase || return 1
        ;;
      expert-install)
        darkdm_install expert-install || return 1
        ;;
      expert-preinstall)
        darkdm_install expert-preinstall || return 1
        ;;
      help|"?")
        show_helpinfo=false
        darkdm_print_help
        ;;
      info)
        if [ -z "$long_systeminfo" ]; then
          long_systeminfo=$(darkdm_long_systeminfo)
        fi
        printf "%s\n" "$long_systeminfo"
        show_helpinfo=false
        ;;
      install)
        darkdm_install || return 1
        ;;
      kbd)
        darkdm_kbd "$command_args" || return 1
        ;;
      make-install-disk)
        puavo-make-install-disk || return 1
        ;;
      mount)
        darkdm_mount || return 1
        ;;
      nmtui)
        if [ ! -x /usr/bin/nmtui ]; then
          echo 'nmtui not available' >&2
          return 1
        else
          nmtui || return 1
        fi
        ;;
      poweroff)
        darkdm_poweroff || return 1
        ;;
      preinstall)
        darkdm_install preinstall || return 1
        ;;
      preseed-preinstall)
        darkdm_install preseed-preinstall || return 1
        ;;
      preseed-install|pi)
        darkdm_install preseed-install || return 1
        ;;
      reboot)
        darkdm_reboot || return 1
        ;;
      reboot-to-firmware)
        darkdm_reboot_to_firmware_setup || return 1
        ;;
      restoredisk|r)
        # try running restoredisk and notify inventory system if successful
        puavo-disk-clone restoredisk || return 1
        curl --cacert /etc/puavo-conf/rootca.pem \
          -d "$(jq -n --arg sn "$(facter dmi.product.serial_number)" '.serialnumber = $sn | .restoredisked = true')" \
          --fail -H "Content-Type: application/json" --max-time 5 --silent -X POST "$deviceid_url" || true
        ;;
      savedisk)
        puavo-disk-clone savedisk || return 1
        ;;
      shell)
        darkdm_shell || return 1
        ;;
      test|t)
        # In case the going gets weird during puavo-test-hardware, just notify inventory
        # system beforehand that tests are starting. Result reporting might come later.
        curl --cacert /etc/puavo-conf/rootca.pem \
          -d "$(jq -n --arg sn "$(facter dmi.product.serial_number)" '.serialnumber = $sn | .systemtested = true')" \
          --fail -H "Content-Type: application/json" --max-time 5 --silent -X POST "$deviceid_url" || true

        # Keyboard test makes tmux exit prematurely in ttys (not under X)
        # unless run outside tmux, this is why we run tests with openvt.
        # Actually even with this tmux does exit without a warning, but
        # it will be restarted after this has exited, thus it is not
        # a big issue.
        openvt -s -w /usr/sbin/puavo-test-hardware || return 1
        ;;
      update)
        darkdm_update || return 1
        ;;
      *)
        echo "Error: invalid command '${command}'." >&2
        echo
        sleep 1
        ;;
    esac
  done
}

check_forced_operation() {
  local forced_operation image_server

  if [ -z "$preseed_json" ]; then
    return 1
  fi

  forced_operation=$(printf %s "$preseed_json" | jq -r '.["force-operation"]')
  if [ "$forced_operation" = 'null' ]; then forced_operation=''; fi
  if [ "$forced_operation" = '' ]; then return 1; fi

  printf "%s" "$forced_operation"
}

darkdm_main()
{
  local forced
  local hosttype
  local initial_command

  hosttype=$(cat /etc/puavo/hosttype)
  case "$hosttype" in
    laptop|wirelessaccesspoint)
      initial_command='update'
      ;;
    unregistered)
      initial_command='install'
      ;;
    *)
      initial_command=
      ;;
  esac

  forced=''
  if forced_operation=$(check_forced_operation 2>/dev/null); then
    initial_command="$forced_operation"
    forced='--force'

    case "$initial_command" in
      preseed-install|preseed-preinstall)
        if vgs puavo >/dev/null 2>&1; then
          forced='--show-unforced-warning'
        fi
        ;;
    esac
  fi

  darkdm_menu "$initial_command" "$forced" || return 1
}

# get preseed, we might need it
echo -n 'Getting preseed and device identifier (if server can be found)... '
preseed_json=''
sysinfo_json=''
device_identifier=''
if image_server=$(/usr/lib/puavo-ltsp-client/lookup-image-server-by-dns); then
  preseed_url="https://${image_server}/preseeds/index.json"
  if ! preseed_json=$(curl --cacert /etc/puavo-conf/rootca.pem --fail \
                           --max-time 5 --silent "$preseed_url"); then
    preseed_json=''
  fi
  deviceid_url="https://${image_server}/deviceid"
  if sysinfo_json=$(
    dbus-send --system --dest=org.puavo.client.systeminfocollectordaemon \
              --print-reply=literal /systeminfocollector \
              org.puavo.client.systeminfocollector.CollectSysinfo \
              | jq --arg nresolution "$(cat /sys/class/graphics/fb0/virtual_size)" \
                --arg hastouchscreen "$(udevadm info --export-db | grep -c ID_INPUT_TOUCHSCREEN=1)" \
                --arg hassafety "$(inspect_efivars --displaytype)" \
                --arg hasbacklight "$(inspect_efivars --kblight)" \
              '. + {"resolution": $nresolution, "touchscreen": $hastouchscreen, "safety": $hassafety, "keyboard_backlight": $hasbacklight}' | jq '{"hw_info": .}'); then
        if ! device_identifier=$(curl --cacert /etc/puavo-conf/rootca.pem  \
                            -d "${sysinfo_json}" --fail  -H "Content-Type: application/json" --max-time 5 --silent \
                            -X POST "$deviceid_url"); then
            device_identifier=''
        fi
  else
    device_identifier='-'
  fi
fi
if [ -n "$preseed_json" ]; then
  echo -n 'Preseed OK. '
else
  echo -n 'Preseed failed. '
fi

if [ "$device_identifier" == "n/a" ]; then
  echo 'Device backend not enabled.'
  device_identifier=''
elif [ "$device_identifier" == "-" ]; then
  echo 'Error during device data gathering.'
  device_identifier=''
elif [ -n "$device_identifier" ]; then
  echo 'Device identifier OK.'
  devidvalue=-2
  if [[ $device_identifier =~ [0-9] ]]; then
    devidvalue=$device_identifier
  fi
  device_identifier="Device id:       $(colorize deviceid 0 -1 1 "$devidvalue" "$device_identifier")"
else
  echo 'Server does not support device backend.'
  device_identifier=''
fi

echo 'Gathering system information...'
echo
common_systeminfo=$(darkdm_common_systeminfo 2>/dev/null || true)
short_systeminfo=$(darkdm_short_systeminfo 2>/dev/null || true)
long_systeminfo=''

while true; do
  if darkdm_main; then
    echo 'Operation was a success.'
  else
    echo 'Operation failed.'
  fi

  echo 'Press ENTER to continue.'
  read _
done
