#!/bin/bash
set -e

## Environment overrides
# If set, look for BIOS files here rather than the default.  Used for vendoring newer updates in repair images.
JUPITER_BIOS_DIR=${JUPITER_BIOS_DIR-}

# If found, and --no-beta wasn't explicitly passed, use beta mode.  Set by e.g. foxnet.
BIOS_BETA_FILE=/run/jupiter-bios-use-beta

# BIOS versions that can be updated and to what
# 'default'      - specifies what BIOS this version should be updated to by default
# 'samsung'      - specifies what BIOS samsung-memory units should update to
# 'beta'         - if set, the version to update to with --beta or BIOS_BETA_FILE set
# 'samsung_beta' - if set, the version to update to with --beta or BIOS_BETA_FILE set for samsung units
# 'args'         - additional args this update should pass to the updater (not used currently)
# 'auto'         - whether or not to update with --auto
#
# - BIOS 19+ are EV2 bios, and can carry forward to the newest.
# - EV1-EV2 units no longer supported, BIOS before F7A0006 requires special steps

# Galileo: Early BIOS did not have failed-update failsafes in them or disable the powerbutton, so auto is false to throw up warnings.
#
#          Choice of 14 to resume auto-updates is arbitrary, fixes were scattered through early BIOSes, but having auto
#          mode on for the checkpoint BIOS transition is good.
#
#          BIOS <= 14 to BIOS >= 20 requires transitioning through the '14T20' or '20T14' BIOS for upgrade/downgrade.
#
#          For post-100 BIOS, disabled auto install to prompt the user from steam, due to the more intrusive update
#          process that turns the screen off for a bit.
BIOS_VERSION_MAP='{
                    "F7A0019":   { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0020": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0021": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0022": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0023": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0024": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0025": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0026": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0027": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0028": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0029": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0030": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0031": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0032": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0042": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0100": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0101": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7A0102": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0103": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0104": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0105": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0106": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0107": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0108": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0109": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0110": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0111": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0112": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0113": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0114": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0115": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0116": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0117": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0118": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0119": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0120": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0121": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7A0131": { "default": "F7A0133", "samsung": "F7A0133", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }

                    , "F7G0003R2":  { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0003":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0004":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0005":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0006":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0007":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0008":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0009":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0010":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0011":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0012":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0012T10": { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0013":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0014":    { "default": "F7G0014T20", "samsung": "F7G0014T20", "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0014T20": { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0020":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0021":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0022":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0023":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0024":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0025":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0026":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0027":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0028":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "true" }
                    , "F7G0100":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0101":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0101T24": { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0102T01": { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0103":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0104":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0105":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0107":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0109":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0110":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                    , "F7G0112":    { "default": "F7G0113",    "samsung": "F7G0113",    "beta": null, "samsung_beta": null, "args": "", "auto": "false" }
                  }'

# Bios to attempt to install
BIOS_FLASHER="/usr/share/jupiter_bios_updater/h2offt"
# Where the bioses (bi-ii?) live.  Can be overridden by JUPITER_BIOS_DIR in environment
BIOS_DIR="${JUPITER_BIOS_DIR:-/usr/share/jupiter_bios}"
# What to append to the bios name to get the file name
BIOS_SUFFIX="_sign.fd"
# Inhibit file. Do nothing if this exists.
BIOS_INHIBIT_FILE="/foxnet/bios/INHIBIT"

# Util
info() { echo >&2 "$*"; }
err()  { info "!! $*"; }
die() {
  local errstr
  if [[ $# -gt 0 ]]; then
    err "$*"
    errstr="Error: \"$*\""
  fi
  finish 1 false "${errstr:-unknown failure}"
}

finish() {
  local exitcode="$1"
  local changed="$2"
  local comment="${*:3}"
  [[ -z $comment ]] || info "$comment"
  if [[ -n $SALT_STATE ]]; then
    echo
    echo "changed=$changed comment='${comment//\'/}'"
  fi
  exit "$exitcode"
}

is_steamdeck() {
  local board_name="$(cat /sys/class/dmi/id/board_name || true)"
  local board_vendor="$(cat /sys/class/dmi/id/board_vendor || true)"

  [[ $board_vendor = "Valve" && ( $board_name = "Jupiter" || $board_name = "Galileo" ) ]]
}

##
## Args
##

checkmode=
beta=
automode=
forcereinstall=
# If --no-beta was passed, we don't want to auto-detect beta from /run/jupiter-bios-use-beta
explicit_no_beta=

usage() {
  echo >&2 "!! Usage: $0 [--beta] [check] [--auto] [--force]";
  exit 1
}

while [[ ${#@} -gt 0 ]]; do
  arg="$1"
  if [[ $arg = "check" && -z $checkmode ]]; then
    checkmode=1
  elif [[ $arg = "--no-beta" ]]; then
    beta=
    explicit_no_beta=1
  elif [[ $arg = "--beta" ]]; then
    beta=1
    explicit_no_beta=
  elif [[ $arg = "--auto" ]]; then
    automode=1
  elif [[ $arg = "--force" ]]; then
    forcereinstall=1
  else
    usage
  fi
  shift
done

# Check mode and for BIOS_BETA_FILE
actionstring="BIOS updates"
if [[ -z $explicit_no_beta && -z $beta && -e $BIOS_BETA_FILE ]]; then
  beta=1
  actionstring="BETA BIOS updates due to $BIOS_BETA_FILE"
elif [[ -n $beta ]]; then
  actionstring="BETA BIOS updates"
fi

##
## Main
##

# Die early if inhibited
[[ ! -f $BIOS_INHIBIT_FILE ]] || die "Bios updates inhibited, no action ($BIOS_INHIBIT_FILE)"

# Print action
modestring="Performing"
[[ -z $checkmode ]] || modestring="Checking for"
info "$modestring $actionstring"

# Check for EV2s which are no longer supported
product_serial="$(cat /sys/devices/virtual/dmi/id/product_serial || true)"
product_year="${product_serial:4:1}"
product_week="${product_serial:5:2}"
if ! is_steamdeck || [[ -z $product_serial ]] || [[ "$product_year" -eq "1" && "$product_week" -lt "33" ]]; then
  finish 0 false 'Skipping update due to unsupported hardware revision'
fi

# Check for samsung memory
memory_sns=($(dmidecode | awk '/^[^\t]/ {i=0}; /^Memory Device/ {i=1}; /\tPart Number:/ && i { print $3 }' | uniq))
[[ ${#memory_sns[@]} -eq 1 ]] || die "Couldn't identify this unit from DMI, unexpected memory serials: ${memory_sns[*]}"

# Samsung memory modules have S/Ns beginning with K3L
samsung=""
[[ ${memory_sns[0]:0:3} != K3L ]] || samsung=1

# Read & check curent version
currentversion="$(cat /sys/devices/virtual/dmi/id/bios_version || true)"
[[ -n $currentversion ]] || die "Failed to parse bios version from dmidecode"
echo >&2 "BIOS version: $currentversion"

# Helper to query the bios version map for currentversion+field (with needlessly wordy variable escaping since current
# version is an arbitrary string that has contained bad characters historically)
configfield() {
  local currentversion=$1
  local field=$2
  jq -r --arg currentversion "$currentversion" --arg field "$field" \
     '.[$currentversion] | .[$field] | select(type == "string")' <<< "$BIOS_VERSION_MAP"
}

# Check config table for desired version
if [[ -n $samsung ]]; then
  desiredversion=$(configfield "$currentversion" "samsung")
  desiredversion_beta=$(configfield "$currentversion" "samsung_beta")
else
  desiredversion=$(configfield "$currentversion" "default")
  desiredversion_beta=$(configfield "$currentversion" "beta")
fi

# If beta is set and the beta value for this bios is not null, use it.
[[ -z $beta || -z $desiredversion_beta ]] || desiredversion=$desiredversion_beta

# Args aren't differentiated by samsung/beta/etc currently
desiredargs=$(configfield "$currentversion" "args")

echo >&2 "Desired version: ${desiredversion:-<none>}"

# No updates configured for the current version
if [[ -z $desiredversion ]]; then
  # did the user request a reinstall
  if [[ $forcereinstall = 1 && -f $BIOS_DIR/$currentversion$BIOS_SUFFIX ]]; then
    desiredversion=$currentversion
  else
    finish 0 no 'No updates configured for this bios'
  fi
fi

# Skip if --auto was specified but "auto": "false" for the current version
if [[ -n $automode && $(configfield "$currentversion" "auto") != "true" ]]; then
  finish 0 no "Auto-updates for $currentversion are disabled"
fi

# Make sure we have the files to install this version
biosfile="$BIOS_DIR/$desiredversion$BIOS_SUFFIX"
[[ -x $BIOS_FLASHER ]] || die "Could not find or execute flasher at \"$BIOS_FLASHER\""
[[ -f "$biosfile" ]] || die "Could not find expected bios at: $biosfile"

# Update needed
if [[ -n $checkmode ]]; then
  echo "BIOS $desiredversion"
  finish 7 yes "Updates available, not applying in check mode"
fi

# In --auto mode, skip operating on OOBE images, favoring the provided BIOS and letting the first update occur on the
# day1 image.
if [[ -n $automode && -e /etc/steamos-oobe-image ]]; then
  finish 0 no "Update available, not applying in OOBE image in --auto mode.  Invoke directly to force update."
fi

# Abort if --auto and battery < 20%
# (for non-auto updates we leave this up to the UI)
if [[ -n $automode && $(( "$(cat /sys/class/power_supply/BAT1/capacity)" )) -lt 20 ]]; then
  finish 2 no "Cannot attempt bios update on <= 20% battery"
fi

# Don't retry auto-updates more than once per 24 hours
if [[ -n $automode ]]; then
  last_auto_attempt_path="/var/lib/jupiter-biosupdate/last_auto_attempt_$desiredversion"
  last_auto_attempt=$(cat "$last_auto_attempt_path" 2> /dev/null || echo "")
  now=$(date +%s)

  if [[ "$now" -lt $(( "$last_auto_attempt" + 60 * 60 * 24 )) ]]; then
    finish 0 no "Auto-update to $desiredversion was attempted within the last 24 hours, skipping for now"
  fi

  mkdir -p "$(dirname "$last_auto_attempt_path")"
  echo "$now" > "$last_auto_attempt_path"
fi

# Run with -N outside --auto mode
if [[ -z $automode ]]; then
  desiredargs="-N $desiredargs"
fi

# Try to flash
#   Flasher is cwd-sensitive
cd "$(dirname "$BIOS_FLASHER")"

# shellcheck disable=SC2206 # desiredargs expanded on purpose
cmd=("$BIOS_FLASHER" "$biosfile" -all -AC $desiredargs)
info "Running: " "${cmd[@]@Q}"
ret=0
"${cmd[@]}" || ret=$?

if [[ $ret = 0 ]]; then
  finish 0 yes "Applied bios update to version $desiredversion"
elif [[ $ret = 194 ]]; then
  ESP="$(bootctl -p)"
  mv "$ESP/EFI/EFI/Insyde" "$ESP/EFI/Insyde"
  finish 0 yes "Will apply bios update to version $desiredversion after reboot"
else
  die "Attempted to flash bios but failed"
fi
