#!/bin/sh
# SPDX-License-Identifier: GPL-3.0+
# Copyright 2022-2025 Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
# Copyright 2023 Lukas F. Hartmann <lukas@mntre.com>

set -eu

usage() {
  echo "Download and flash the latest sysimage-v5 to eMMC." >&2
  echo "Calls reform-flash-uboot to install u-boot on eMMC, grows the root" >&2
  echo "filesystem and adjusts filesystem labels." >&2
  echo "Calls reform-boot-config to set up /boot on eMMC to load the rootfs from eMMC." >&2
  echo >&2
  echo "Usage: $0 [--help] [--force] [--mirror={mntre.com,reform.debian.net}]" >&2
  echo >&2
  echo "Options:" >&2
  echo "  --help           Display this help and exit." >&2
  echo "  --mirror=MIRROR  Force MIRROR, either mntre.com (default) or reform.debian.net" >&2
  echo "  -f, --force      No user interaction, overwriting data without confirmation" >&2
  echo "                   and flash to devices marked as 'warn'" >&2
}

FORCE=false
MIRROR="mntre.com"
while getopts :h-: OPTCHAR; do
  case "$OPTCHAR" in
    h)
      usage
      exit 0
      ;;
    -)
      case "$OPTARG" in
        help)
          usage
          exit 0
          ;;
        force) FORCE=true ;;
        mirror)
          if [ "$OPTIND" -gt "$#" ]; then
            echo "E: missing argument for --mirror" >&2
            exit 1
          fi
          MIRROR="$(nth_arg "$OPTIND" "$@")"
          OPTIND=$((OPTIND + 1))
          ;;
        mirror=*)
          MIRROR="${OPTARG#*=}"
          ;;
        *)
          echo "E: unrecognized option: --$OPTARG" >&2
          exit 1
          ;;
      esac
      ;;
    :)
      echo "E: missing argument for -$OPTARG" >&2
      exit 1
      ;;
    '?')
      echo "E: unrecognized option -$OPTARG" >&2
      exit 1
      ;;
    *)
      echo "E: error parsing options" >&2
      exit 1
      ;;
  esac
done
shift "$((OPTIND - 1))"

if [ "$#" -gt 0 ]; then
  usage
  exit 1
fi

if [ "$MIRROR" != "mntre.com" ] && [ "$MIRROR" != "reform.debian.net" ]; then
  echo "E: invalid value for --mirror: $MIRROR -- only mntre.com and reform.debian.net are supported" >&2
  exit 1
fi

# shellcheck source=/dev/null
if [ -e "./machines/$(cat /proc/device-tree/model).conf" ]; then
  . "./machines/$(cat /proc/device-tree/model).conf"
elif [ -e "/usr/share/reform-tools/machines/$(cat /proc/device-tree/model).conf" ]; then
  . "/usr/share/reform-tools/machines/$(cat /proc/device-tree/model).conf"
else
  echo "E: unable to find config for $(cat /proc/device-tree/model)" >&2
  exit 1
fi

case $EMMC_USE in
  false)
    echo "E: writing to eMMC not supported on $(cat /proc/device-tree/model)" >&2
    exit 1
    ;;
  warn)
    echo "W: Using eMMC on $(cat /proc/device-tree/model) is not without risk." >&2
    echo "W: For example, flashing the wrong u-boot or if the flashing process goes wrong, it is" >&2
    echo "W: possible to soft-brick your board. Restoring it might need some extra hardware." >&2
    echo "W: Please only proceed if you are sure that the benefits outweigh the risks for you." >&2
    if [ "$FORCE" = true ]; then
      echo "Proceeding without user interaction because of --force" >&2
      response="y"
    else
      printf "Are you sure you want to proceed? [y/N] "
      read -r response
    fi

    if [ "$response" != "y" ]; then
      echo "Exiting."
      exit
    fi
    ;;
esac

if [ "$EMMC_BOOT" != false ]; then
  if [ "$EMMC_BOOT" = warn ]; then
    echo "W: Flashing u-boot to eMMC on $(cat /proc/device-tree/model) is not without risk." >&2
    echo "W: If you flash the wrong u-boot or if the flashing process goes wrong, it is" >&2
    echo "W: possible to soft-brick your board. Restoring it might need some extra hardware." >&2
    echo "W: Please only proceed if you are sure that the benefits outweigh the risks for you." >&2
    if [ "$FORCE" = true ]; then
      echo "Proceeding without user interaction because of --force" >&2
      response="y"
    else
      printf "Are you sure you want to proceed? [y/N] "
      read -r response
    fi

    if [ "$response" != "y" ]; then
      echo "Exiting."
      exit
    fi
  fi
  if [ ! -e "/sys/class/block/${DEV_MMC}boot0/force_ro" ]; then
    echo "/sys/class/block/${DEV_MMC}boot0/force_ro doesn't exist" >&2
    exit 1
  fi

  if [ ! -w "/sys/class/block/${DEV_MMC}boot0/force_ro" ]; then
    echo "/sys/class/block/${DEV_MMC}boot0/force_ro is not writable" >&2
    exit 1
  fi

  if [ ! -e /boot/flash.bin ]; then
    echo "/boot/flash.bin doesn't exist" >&2
    if [ "$FORCE" = true ]; then
      echo "Running reform-flash-uboot without user interaction because of --force" >&2
      response="y"
    else
      printf "Should reform-flash-uboot be run to download it? [y/N] "
      read -r response
    fi

    if [ "$response" != "y" ]; then
      echo "Exiting."
      exit 1
    fi
    reform-flash-uboot
  fi

  for p in $(lsblk --list --noheadings --output=NAME "/dev/$DEV_MMC") "${DEV_MMC}boot0" "${DEV_MMC}boot1"; do
    [ -b "/dev/$p" ] || continue
    if [ -n "$(lsblk --nodeps --noheadings --output=MOUNTPOINT "/dev/$p")" ]; then
      echo "/dev/$p is still in use" >&2
      exit 1
    fi
  done

  echo "WARNING: This overwrites the bootloader on the eMMC rescue disk with the" >&2
  echo "backup stored as /boot/flash.bin!" >&2

  if [ "$FORCE" = true ]; then
    echo "Proceeding without user interaction because of --force" >&2
    response="y"
  else
    printf "Are you sure you want to proceed? [y/N] "
    read -r response
  fi

  if [ "$response" != "y" ]; then
    echo "Exiting."
    exit
  fi

  if [ "$FORCE" = true ]; then
    reform-flash-uboot --force --offline emmc
  else
    reform-flash-uboot --offline emmc
  fi

  echo "Bootloader was successfully written to /dev/${DEV_MMC}boot0." >&2
  echo "" >&2
fi

echo "Do you want to download and install the latest sysimage-v5 to eMMC as well?" >&2
echo "This step needs a working internet connection and either wget or curl installed." >&2
echo "" >&2
echo "WARNING: This overwrites partitions on eMMC, deleting all data." >&2

if [ "$FORCE" = true ]; then
  echo "Proceeding without user interaction because of --force" >&2
  response="y"
else
  printf "Are you sure you want to proceed? [y/N] "
  read -r response
fi

if [ "$response" != "y" ]; then
  echo "Exiting."
  exit
fi

if [ "$MIRROR" = "mntre.com" ]; then
  URL="https://source.mnt.re/reform/reform-system-image/-/jobs/artifacts/main/raw/${SYSIMAGE}.img.gz?job=build"
  DECOMPRESSOR=gzip

  if grep --silent '^URIs: https://reform.debian.net/debian/\?$' /etc/apt/sources.list.d/reform*.sources 2>/dev/null; then
    echo "The current system image is configured to use the Debian stable mirror from" >&2
    echo "reform.debian.net. Do you want to flash the system image from reform.debian.net" >&2
    echo 'to your eMMC? If you answer anything but "y", the official MNT system image will' >&2
    echo "be downloaded and written to eMMC instead." >&2
    if [ "$FORCE" = true ]; then
      echo "Proceeding without user interaction because of --force" >&2
      response="y"
    else
      printf "Do you want to download and use the rescue system image from reform.debian.net? [y/N] "
      read -r response
    fi
    if [ "$response" = "y" ]; then
      # no need to use the backports kernel because a311d is not
      # allowed to write to emmc anyways
      URL="https://reform.debian.net/images/${SYSIMAGE}.img.xz"
      DECOMPRESSOR=xz
    fi
  fi
elif [ "$MIRROR" = "reform.debian.net" ]; then
  URL="https://reform.debian.net/images/${SYSIMAGE}.img.xz"
  DECOMPRESSOR=xz
else
  echo "E: unsupported value for --mirror: $MIRROR" >&2
  exit 1
fi

echo "Downloading $URL..."

# flash with bmaptool if available and use dd otherwise
if command -v bmaptool >/dev/null 2>&1; then
  case $URL in
    https://reform.debian.net/images/*)
      # force gpg signature verification when downloading from reform.d.n
      bmaptool copy \
        --fingerprint 3AC6EB840FA5CE3FF31BAD80EF93221F8A44FEB2 \
        --keyring /usr/share/keyrings/debian-keyring.gpg \
        "$URL" "/dev/$DEV_MMC"
      ;;
    *) bmaptool copy "$URL" "/dev/$DEV_MMC" ;;
  esac
else
  {
    if curl --version >/dev/null; then
      curl --silent --location "$URL"
    elif wget --version >/dev/null; then
      wget --quiet --output-document=- "$URL"
    else
      echo "need curl or wget" >&2
      exit 1
    fi
  } | "$DECOMPRESSOR" --decompress --to-stdout | dd of="/dev/$DEV_MMC" status=progress
fi

partprobe "/dev/$DEV_MMC"

# resize the second partition to fill the emmc
parted --script --machine "/dev/$DEV_MMC" resizepart 2 100%
partprobe "/dev/$DEV_MMC"
sync

# resize the filesystem
e2fsck -fy "/dev/${DEV_MMC}p2"
resize2fs -p "/dev/${DEV_MMC}p2"
e2fsck -fy "/dev/${DEV_MMC}p2"

# overwrite the partition label
e2label "/dev/${DEV_MMC}p1" reformemmcboot
e2label "/dev/${DEV_MMC}p2" reformemmcroot

# The rescue system image that we downloaded and wrote to the eMMC above is
# configured to use the rootfs on the SD-Card but we want the initramfs on eMMC
# to boot the rootfs on eMMC and not the rootfs from SD-Card.  Use
# reform-boot-config to update the /etc/fstab and initramfs to use the eMMC
#
# We use --no-copy-old-boot because by flashing the system image, the new /boot
# partition already has the correct contents and we don't want to use the /boot
# partition of the current system as that one might be wildly different.
if [ "$FORCE" = true ]; then
  reform-boot-config --force --no-copy-old-boot --emmc emmc
else
  reform-boot-config --no-copy-old-boot --emmc emmc
fi

case "$(cat /proc/device-tree/model)" in "MNT Reform 2" | "MNT Reform 2 HDMI")
  echo "If the SoM dip switch is turned off and no SD-Card is present, your" >&2
  echo "system will now boot from eMMC and load the rootfs from there as well." >&2
  echo "If you want to boot from eMMC but load the rootfs from elsewhere, run" >&2
  echo "reform-boot-config with the --emmc switch." >&2
  ;;
esac
