#!/bin/bash

root_check() {
    [[ $EUID != 0 ]] && err "Please run as root."
}

args_check() {
    [[ $1 -lt $2 || $1 -gt $3 ]] && err "Please use the right amount of arguments (use -h for help)."
}

err() {
    printf "\e[31mError:\e[0m $1\n" 1>&2; exit 1
}

kernel_usage() {
    echo "Usage: mhwd-kernel [option]
    -h  --help              Show this help message
    -i  --install           Install a new kernel        [kernel(s)] [optional: rmc = remove current kernel]
    -l  --list              List all available kernels
    -li --listinstalled     List installed kernels
    -r  --remove            Remove a kernel             [kernel(s)]"
}

update_grub() {
    # Update grub configuration file
    echo ""
    echo ":: grub-mkconfig output:"
    grub-mkconfig -o /boot/grub/grub.cfg
    # workaround for https://git.savannah.gnu.org/cgit/grub.git/commit/?id=3cf2e848bc03c44d30bb87e583d12efe7e7ccf75
    # if grub is not updated in MBR/EFI dashes still won't work, hence we remove them
    sed -i -e '/cryptomount -u/ {s/-//g;s/ u/ -u/g}' /boot/grub/grub.cfg

    if [ $? != 0 ]; then
        echo "error: grub-mkconfig failed!"
        exit 1
    fi
}

kernel_install() {
    pkginstall=()
    rmc=0

    for kernel in "$@"; do
        [[ $kernel = "rmc" ]] && rmc=1 && continue
        [[ $kernel != linux[0-9][0-9]?([0-9]) && $kernel != linux[0-9][0-9]?([0-9])-rt && $kernel != "rmc" ]] && err "Invalid argument.\nPlease choose one of the $(kernel_repo)"
        [[ $current = $kernel ]] && err "You can't reinstall your current kernel. Please use 'pacman -Syu' instead to update."
        [[ -z $(pacman -Ssq "^$kernel$") ]] && err "Please make sure if the given kernel(s) exist(s).\n$(kernel_repo)"
        
        for pkg in $(pacman -Qqs "$current"); do
            pkg=${pkg//$current/$kernel}
            [[ -n $(pacman -Ssq "^$pkg$" | grep -E "^$pkg$|^$pkg-\S+$") ]] && pkginstall+=("$pkg")
        done
    done

    pacman -Sy

    outofdate="$(pacman -Qqu | tr '\n' ' ')"
    if [[ -n $outofdate ]]; then
        echo "The following packages are out of date, please update your system first: $outofdate" >&2
        read -p "Do you want to continue anyway? [y/N] " yesno
        [[ $yesno = [Yy]?([Ee][Ss]) ]] || exit 1
    fi

    pacman -S "${pkginstall[@]}"

    [[ $rmc = 1 && $? = 0 ]] && pacman -R $(pacman -Qqs $current)
    [[ $rmc = 1 && $? != 0 ]] && { echo ""; err "'rmc' aborted because the kernel failed to install or canceled on removal."; }

    [[ ! -e "/usr/bin/update-grub" && -e "/usr/bin/grub-mkconfig" ]] && update_grub
}

kernel_repo() {
    printf "\e[32mavailable kernels:\e[0m\n"
    pacman -Ssq "^linux[0-9][0-9]?([0-9])$" | while read -r; do echo "   * $REPLY"; done
    pacman -Ssq "^linux[0-9][0-9]?([0-9])-rt$" | while read -r; do echo "   * $REPLY"; done
}

kernel_list() {
    printf "\e[32mCurrently running:\e[0m $(uname -r) (${current})\n"
    echo "The following kernels are installed in your system:"
    pacman -Qqs "^linux[0-9][0-9]?([0-9])$" | while read -r; do echo "   * $REPLY"; done
    pacman -Qqs "^linux[0-9][0-9]?([0-9])-rt$" | while read -r; do echo "   * $REPLY"; done
}

kernel_remove() {
    pkgremove=()

    for kernel in "$@"; do
        [[ -z "$kernel" ]] && err "Invalid argument (use -h for help)."
        [[ $kernel != linux[0-9][0-9]?([0-9]) && $kernel != linux[0-9][0-9]?([0-9])-rt ]] && err "Please enter a valid kernel name.\n$(kernel_list)"
        [[ $current = $kernel ]] && err "You can't remove your current kernel."
        [[ -z $(pacman -Qqs "^$1$") ]] && err "Kernel not installed.\n$(kernel_list)"

        for pkg in $(pacman -Qqs "$kernel" | grep -E "^$kernel$|^$kernel-\S+$"); do
            pkgremove+=("$pkg")
        done
    done

    pacman -R "${pkgremove[@]}"

    [[ ! -e "/usr/bin/update-grub" && -e "/usr/bin/grub-mkconfig" ]] && update_grub
}

IFS=. read -r major minor _ <<< "$(uname -r)"
current="linux$major$minor"

case "$1" in
    -h | --help)            args_check $# 1 1
                            kernel_usage
                            exit 0;;
    -i | --install)         shift
                            root_check
                            kernel_install $@
                            exit 0 ;;
    -l | --list)            args_check $# 1 1
                            kernel_repo
                            exit 0 ;;
    -li| --listinstalled)   args_check $# 1 1
                            kernel_list
                            exit 0 ;;
    -r | --remove)          shift
                            root_check
                            kernel_remove $@
                            exit 0 ;;
    -*)                     err "Invalid argument (use -h for help)." ;;
    *)                      err "No arguments given (use -h for help)." ;;
esac
