#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0-only

set -e

args=()
package=0

process_preset() {
    if [[ -n "$pkgbase" && -e "$preset" ]]; then
        if ! cmp "$preset" > /dev/null 2>&1 <(sed "s|%PKGBASE%|${pkgbase}|g" /usr/share/mkinitcpio/hook.preset); then
            if [[ ! -e "$preset.pacsave" ]]; then
                # save the preset as pacsave
                mv -- "$preset" "$preset.pacsave" && return 0
            fi
        else
            # remove the preset
            rm -- "$preset" && return 0
        fi
    fi
}

read_preset() {
    local pkgbase="$1" p preset_image preset_uki preset_kver
    local unsorted_filelist=() unsorted_kernellist=()

    if [[ -v PRESETS ]]; then
        for p in "${PRESETS[@]}"; do
            declare -n preset_image="${p}_image" preset_uki="${p}_uki" preset_kver="${p}_kver"

            if [[ -v preset_image ]]; then
                unsorted_filelist+=("${preset_image}")
            elif [[ -v ALL_image ]]; then
                unsorted_filelist+=("${ALL_image}")
            fi
            if [[ -v preset_uki ]]; then
                unsorted_filelist+=("${preset_uki}")
            elif [[ -v ALL_uki ]]; then
                unsorted_filelist+=("${ALL_uki}")
            fi
            if [[ -v preset_kver ]]; then
                unsorted_filelist+=("${preset_kver}")
                unsorted_kernellist+=("${preset_kver}")
            elif [[ -v ALL_kver ]]; then
                unsorted_filelist+=("${ALL_kver}")
                unsorted_kernellist+=("${ALL_kver}")
            fi
        done
    else
        unsorted_filelist+=("/boot/vmlinuz-${pkgbase}"
                  "/boot/initramfs-${pkgbase}.img"
                  "/boot/initramfs-${pkgbase}-fallback.img"
                  "/efi/EFI/Linux/arch-${pkgbase}.efi"
                  "/efi/EFI/Linux/arch-${pkgbase}-fallback.efi")
        unsorted_kernellist+=("/boot/vmlinuz-${pkgbase}")
    fi

    # Deduplicate file lists
    filelist=()
    while IFS='' read -r -d '' arrayelement; do
        filelist+=("$arrayelement")
    done < <(printf '%s\0' "${unsorted_filelist[@]}" | sort -uz)
    kernellist=()
    while IFS='' read -r -d '' arrayelement; do
        kernellist+=("$arrayelement")
    done < <(printf '%s\0' "${unsorted_kernellist[@]}" | sort -uz)
}

install_kernel() {
    local pkgbase="$1"
    local kernelfile preset="/etc/mkinitcpio.d/${pkgbase}.preset"

    if [[ ! -e "$preset" ]]; then
        if [[ -e "$preset.pacsave" ]]; then
            # move the pacsave to the template
            mv -- "${preset}.pacsave" "$preset"
        else
            # create the preset from the template
            sed "s|%PKGBASE%|${pkgbase}|g" /usr/share/mkinitcpio/hook.preset \
                | install -Dm644 /dev/stdin "$preset"
        fi
    fi

    (
        # source the preset to get the kernel and image locations
        # shellcheck disable=SC1090
        [[ -s "$preset" ]] && . "$preset"
        read_preset "$pkgbase"

        # always install the kernel
        for kernelfile in "${kernellist[@]}"; do
            install -Dm644 -- "$line" "${kernelfile}"
        done
    )

    # compound args for each kernel
    args+=(-p "$pkgbase")
}

remove_kernel() {
    local pkgbase="$1"
    local preset="/etc/mkinitcpio.d/${pkgbase}.preset"

    # subshell to avoid namespace pollution
    (
        # source the preset to get the kernel and image locations
        # shellcheck disable=SC1090
        [[ -s "$preset" ]] && . "$preset"
        read_preset "$pkgbase"

        # access all the files to trigger any potential automounts
        stat -- /boot/ /efi/ "${filelist[@]}" &>/dev/null || :

        # remove the actual kernel and images for the package being removed
        rm -f -- "${filelist[@]}"
    ) || return
    # remove the preset
    process_preset "$pkgbase" "$preset"
}

while read -r line; do
    if [[ "$line" != */vmlinuz ]]; then
        # triggers when it's a change to usr/lib/initcpio/*
        package=1
        continue
    fi

    if ! read -r pkgbase > /dev/null 2>&1 < "${line%/vmlinuz}/pkgbase"; then
        # if the kernel has no pkgbase, we skip it
        continue
    fi

    case "$1" in
        install) install_kernel "$pkgbase";;
        remove) remove_kernel "$pkgbase";;
    esac
done

if (( package )) && compgen -G /etc/mkinitcpio.d/"*.preset" > /dev/null; then
    case "$1" in
        install)
            # change to use all presets
            args=(-P)
            ;;
        remove)
          shopt -s nullglob
          for preset in /etc/mkinitcpio.d/*.preset; do
              pkgbase=${preset##*/}
              pkgbase=${pkgbase%.preset}
              process_preset "$pkgbase" "$preset"
          done
          shopt -u nullglob
          ;;
    esac
fi

if [[ "$1" == "install" ]] && (( ${#args[@]} )); then
    mkinitcpio "${args[@]}"
fi
