#
# lsblk
#
# -d, --nodeps      Do  not  print  holder  devices  or  slaves.
# -p, --paths       Print full device paths.
# -l, --list        Produce  output  in the form of a list.
# -n, --noheadings
# -x, --sort column

function get_uuid() {
  # arg $1: partition
  # arg $2: variable name to store UUID

  local -n ptr=$2  || return $?
  ptr="$(blkid -o value -s UUID "${1}")" || return $?
}

function get_partitions(){
  # arg $1: block-device
  # @post
  #   PARTITIONS (array with one partition per entry)

  # Remove first line of output (which is just the block device $1 itself)
  # with sed: sed 1d
  PARTITIONS="$(lsblk -pln -o name "${1}" | sed 1d)" || return $?

  newline_separated_to_array PARTITIONS PARTITIONS || return $?
}

function get_block_devices() {
  # @post
  #   BLOCK_DEVICES (array with one entry for each block device)

  # Get list of devices, one per line
  BLOCK_DEVICES="$(lsblk -dplnx size -o name | grep -Ev "boot|rpmb|loop" | tac)" || return $?

  newline_separated_to_array BLOCK_DEVICES  BLOCK_DEVICES || return $?
}

function get_block_devices_with_size() {
  # @post
  #   BLOCK_DEVICE_SIZES (array with two entries for each block device: device path and device size)

  # Get list of devices and their sizes, one pair per line
  # Use sed to remove multiple white spaces: sed 's|\s\s*| |'
  BLOCK_DEVICE_SIZES="$(lsblk -dplnx size -o name,size | grep -Ev "boot|rpmb|loop" | sed 's|\s\s*| |' | tac)"  || return $?

  newline_to_space BLOCK_DEVICE_SIZES || return $?
  space_separated_to_array BLOCK_DEVICE_SIZES BLOCK_DEVICE_SIZES || return $?
}


function partition() {
  # @pre
  #   TARGET_BLOCK_DEVICE
  #   BIOS_TYPE
  # @post
  #   BOOT_PART
  #   LUKS_PART

  # if BOOT_PART_SIZE not given, set to default value
  BOOT_PART_SIZE="${BOOT_PART_SIZE:='261'}"
  # if too small, print warning and exit
  if [ "${BOOT_PART_SIZE}" -lt "261" ]; then
    echo "BOOT_PART_SIZE should be larger than 260!";
    return 1;
  fi

  # As our data partition is encrypted,
  # we need a separate boot partition!
  case "${BIOS_TYPE}" in
	uefi)
	  # EFI boot partition
    #
    # Create a partition with fat32 as the file system type and set the
    # esp flag on it.
    sudo parted --script "${TARGET_BLOCK_DEVICE}" -- mklabel gpt \
      mkpart ESP fat32 2Mib "${BOOT_PART_SIZE}MiB" \
      set 1 esp on \
      mkpart primary "${BOOT_PART_SIZE}MiB" 100% || return $?

    get_partitions "${TARGET_BLOCK_DEVICE}" || return $?
    BOOT_PART="${PARTITIONS[0]}"
    LUKS_PART="${PARTITIONS[1]}"
		;;
	bios)
	  # > On a BIOS/GPT configuration, a BIOS boot partition is required. GRUB embeds its `core.img`
	  # > into this partition.
	  # > For parted set/activate the flag bios_grub on the partition.
	  #
	  # archwiki -> GRUB#GUID_Partition_Table_(GPT)_specific_instructions
	  # https://www.gnu.org/software/grub/manual/grub/html_node/BIOS-installation.html#BIOS-installation
	  sudo parted --script "${TARGET_BLOCK_DEVICE}" -- mklabel gpt \
      mkpart primary 1MiB 2MiB \
      set 1 bios_grub on \
      mkpart primary 2MiB "${BOOT_PART_SIZE}MiB" \
      mkpart primary "${BOOT_PART_SIZE}MiB" 100% || return $?

    get_partitions "${TARGET_BLOCK_DEVICE}" || return $?
    BOOT_PART="${PARTITIONS[2]}"
    LUKS_PART="${PARTITIONS[3]}"
		;;
	*)
		echo "Expected uefi or bios but got ${BIOS_TYPE} instead!"
		return 1
		;;
  esac

  echo "boot partition: ${BOOT_PART}"
  echo "luks partition: ${LUKS_PART}"
}

function format() {
  # @pre
  #   BIOS_TYPE
  #   BOOT_PART
  #   LUKS_PART
  #   LUKS_PWD
  #   FS
  # @post
  #   LUKS_PART_UUID
  #   DATA_PART (variable set with value /dev/mapper/$(basename "${LUKS_PART}")
  #   boot partition formatted
  #   luks partition formatted and opened at DATA_PART

  echo "Wiping old signatures from partitions ..."
  sudo wipefs "${BOOT_PART}" || return $?
  sudo wipefs "${LUKS_PART}" || return $?

  echo "Formatting boot partition ${BOOT_PART} ..."
  sudo mkfs.fat -F32 "${BOOT_PART}" || return $?

  # Note:
  #  FDE: GRUB does support LUKS2 since this commit: https://git.savannah.gnu.org/cgit/grub.git/commit/?id=365e0cc3e7e44151c14dd29514c2f870b49f9755
  #  -> Using "--type luks1" is no longer required.
  echo "Creating encrypted luks partition ${LUKS_PART} ..."
  printf "%s" "${LUKS_PWD}" | sudo cryptsetup luksFormat \
    --cipher aes-xts-plain64 --key-size 512 --hash sha512 \
    --iter-time 10000 --use-random "${LUKS_PART}" || return $?

  get_uuid "${LUKS_PART}" LUKS_PART_UUID || return $?

  local luks_name
  luks_name=$(basename "${LUKS_PART}")
  DATA_PART="/dev/mapper/${luks_name}"

  # open luks partition
  printf "%s" "${LUKS_PWD}" | sudo cryptsetup luksOpen "${LUKS_PART}" "${luks_name}" || return $?

  echo "Formatting the data partition ${DATA_PART} ..."
  case "${FS}" in
	BTRFS)
      sudo mkfs.btrfs "${DATA_PART}" || return $?
		;;
	EXT4)
	    # archwiki -> Ext4#Enabling_metadata_checksums
      sudo mkfs.ext4 -O metadata_csum "${DATA_PART}" || return $?
		;;
	F2FS)
      # archwiki -> F2FS#Creating_a_F2FS_file_system
      # - requires f2fs-tools
      # - compression: "-O compression" and when mounting the filesystem, specify compress_algorithm=(lzo|lz4|zstd|lzo-rle)
      sudo mkfs.f2fs -O extra_attr,inode_checksum,sb_checksum,compression "${DATA_PART}" || return $?
		;;
	*)
		echo "Filesystem ${FS} is not yet supported!"
		return 1
		;;
  esac
}
