# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: Copyright 2023-2024 SUSE LLC
# SPDX-FileCopyrightText: Copyright 2023-2024 Richard Brown

log(){
	echo "[${tik_module}][$(date +"%Y%m%d-%T")][LOG] $*" 1>&2
}

warn() {
	echo "[${tik_module}][$(date +"%Y%m%d-%T")][WARN] $*" 1>&2
	d --warning --text="$*"
}

error() {
	echo "[${tik_module}][$(date +"%Y%m%d-%T")][ERROR] $*" 1>&2
	d --error --text "$*"
	exit 1
}

d(){
	while true
	do
		retval=0
		result="$(zenity "$@")" || retval=$?
		log "[zenity][${retval}][${result}] $@"
		case $retval in
		  0)
			return 0
			;;
		  1|255)
		    zenity --question --text="Do you really want to quit?" && exit 1
			;;
		esac
	done
}

# variant of privileged run (prun) function that doesn't require the pkexec call to return 0
prun-opt() {
	if [ "${debug}" == "1" ]; then
		log "[pkexec-noexec] $@"
	else
		retval=0
		pkexec "$@"
		retval=$?
		log "[pkexec][${retval}] $@"
	fi
}

# Most commonly used prun function, which requires the called command to work
prun() {
	prun-opt "$@"
	if [ "${retval}" != "0" ]; then
		error "Command <tt>$@</tt> FAILED"
	fi
}

get_persistent_device_from_unix_node() {
	local unix_device=$1
	local schema=$2
	local node
	local persistent_name
	node=$(basename "${unix_device}")
	for persistent_name in /dev/disk/"${schema}"/*; do
	if [ "$(basename "$(readlink "${persistent_name}")")" = "${node}" ];then
	    if [[ ${persistent_name} =~ ^/dev/disk/"${schema}"/nvme-eui ]]; then
		# Filter out nvme-eui nodes as they are not descriptive to the user
		continue
	    fi
	    echo "${persistent_name}"
	    return
	fi
	done
	warn "Could not find <tt>${schema}</tt> representation of <tt>${node}</tt>. Using original device <tt>${unix_device}</tt>"
	echo "${unix_device}"
}

get_disk() {
    # Volume label for the tik install media must be set to "TIKINSTALL" to filter it out from the device list
    tik_volid="TIKINSTALL"
    local disk_id="by-id"
    local disk_size
    local disk_device
    local disk_device_by_id
    local disk_meta
    local disk_list
    local device_array
    local list_items
    local blk_opts="-p -n -r -o NAME,SIZE,TYPE"
    local message
    local blk_opts_plus_label="${blk_opts},LABEL"
    local tik_install_disk_part

    tik_install_disk_part=$(
        eval lsblk "${blk_opts_plus_label}" | \
        tr -s ' ' ":" | \
        grep ":${tik_volid}" | \
        cut -f1 -d:
    )

    for disk_meta in $(
        eval lsblk "${blk_opts}" | grep -E "disk|raid" | tr ' ' ":"
    );do
        disk_size=$(echo "${disk_meta}" | cut -f2 -d:)
        if [[ "${disk_size}" == "0B" ]]; then
            # ignore disks with no size, e.g. empty SD card readers
            continue
        fi
        disk_device="$(echo "${disk_meta}" | cut -f1 -d:)"
        if [[ "${tik_install_disk_part}" == "${disk_device}"* ]]; then
            # ignore install source device
            continue
        fi
        if [[ ${disk_device} =~ ^/dev/fd ]];then
            # ignore floppy disk devices
            continue
        fi
        if [[ ${disk_device} =~ ^/dev/zram ]];then
            # ignore zram devices
            continue
        fi
        disk_device_by_id=$(
            get_persistent_device_from_unix_node "${disk_device}" "${disk_id}"
        )
        if [ -n "${disk_device_by_id}" ];then
            disk_device=${disk_device_by_id}
        fi
        list_items="${list_items} $(basename ${disk_device}) ${disk_size}"
    done
    if [ -n "${TIK_INSTALL_DEVICE}" ];then
        # install device overwritten by config.
        local device=${TIK_INSTALL_DEVICE}
        local device_meta
        local device_size
        if [ ! -e "${device}" ];then
            local no_dev="Given device <tt>${device}</tt> does not exist."
            error "${no_dev}"
        fi
        if [ ! -b "${device}" ];then
            local no_block_dev="Given device <tt>${device}</tt> is not a block special."
            error "${no_block_dev}"
        fi
        device_meta=$(
            eval lsblk "${blk_opts}" "${device}" |\
            grep -E "disk|raid" | tr ' ' ":"
        )
        device_size=$(echo "${device_meta}" | cut -f2 -d:)
        list_items="$(basename ${device}) ${device_size}"
        message="tik installation device set to to: ${device}"
        log "${message}"
    fi
    if [ -z "${list_items}" ];then
        local no_device_text="No device(s) for installation found."
        error "${no_device_text}"
    fi
    disk_list=${list_items}
    if [ -n "${disk_list}" ];then
        local count=0
        local device_index=0
        for entry in ${disk_list};do
            if [ $((count % 2)) -eq 0 ];then
                device_array[${device_index}]=${entry}
                device_index=$((device_index + 1))
            fi
            count=$((count + 1))
        done
        if [ "${device_index}" -eq 1 ];then
            # one single disk device found, use it
            # Add back full path to it
            TIK_INSTALL_DEVICE="/dev/disk/${disk_id}/${device_array[0]}"

            # Fallback to unix device in case by-id does not exist
            # see get_persistent_device_from_unix_node, it does fallback like this.
            if [ ! -e "${TIK_INSTALL_DEVICE}" ]; then
                TIK_INSTALL_DEVICE="/dev/${device_array[0]}"
            fi
        else
            # manually select from storage list
            d --list --column=Disk --column=Size --width=1000 --height=340 --title="Select A Disk" --text="Select the disk to install the operating system to. <b>Make sure any important documents and files have been backed up.</b>\n" ${list_items}
            # Add back full path to it
            TIK_INSTALL_DEVICE="/dev/disk/${disk_id}/${result}"

            # Fallback to unix device in case by-id does not exist
            # see get_persistent_device_from_unix_node, it does fallback like this.
            if [ ! -e "${TIK_INSTALL_DEVICE}" ]; then
                TIK_INSTALL_DEVICE="/dev/${result}"
            fi
        fi
    fi
}

get_img() {
    local list_items
    local message
    local img_meta
    local img_item
    local img_list
    local img_array
    # Images are assumed to be named to the following standard
    # $ProductName.$Version.raw.xz
    # Any extraneous fields may confuse tik's detection, selection and presentation of the image to the user
    for img_meta in $(
        eval cd $TIK_IMG_DIR && (stat --printf="%n\t%s\n" *.raw.xz | tr '	' ":")
    );do
         img_filename="$(echo $img_meta | cut -f1 -d:)"
         img_size="$(echo $img_meta | cut -f2 -d:)"
         list_items="${list_items} ${img_filename} ${img_size}"
    done
    if [ -n "${TIK_INSTALL_IMAGE}" ];then
        # install image overwritten by config.
        local img=${TIK_INSTALL_IMAGE}
        local img_meta
        local img_size
        if [ ! -e "${img}" ];then
            local no_img="Given image <tt>${img}</tt> does not exist."
            error "${no_img}"
        fi
        if [ ! -s "${img}" ];then
            local empty_img="Given image <tt>${img}</tt> is empty."
            error "${empty_img}"
        fi
        img_meta=$(
            eval cd $TIK_IMG_DIR && (stat --printf="%n\t%s\n" $img | tr '	' ":")
        )
        img_filename="$(echo $img_meta | cut -f1 -d:)"
        img_size="$(echo $img_meta | cut -f2 -d:)"
        list_items="${list_items} ${img_filename} ${img_size}"
        message="tik installation image set to to: ${img}"
        log "${message}"
    fi
    if [ -z "${list_items}" ];then
        local no_image_text="No images(s) for installation found"
        error "${no_image_text}"
    fi
    img_list=${list_items}
    if [ -n "${img_list}" ];then
        local count=0
        local img_index=0
        for entry in ${img_list};do
            if [ $((count % 2)) -eq 0 ];then
                img_array[${img_index}]=${entry}
                img_index=$((img_index + 1))
            fi
            count=$((count + 1))
        done
        if [ "${img_index}" -eq 1 ];then
            # one single disk image found, use it
            TIK_INSTALL_IMAGE="${img_array[0]}"
        else
            # manually select from storage list
            d --list --column=Disk --column=Size --title="Select A Image" --text="Select the operating system image to install.\n" ${list_items}
            TIK_INSTALL_IMAGE="$result"
        fi
    fi
}

dump_image() {
    local image_source_files=$1
    local image_target=$2
    local image_source

    d --question --no-wrap --title="Begin Installation?" --text="Once the installation begins the changes to the selected disk are irreversible.\n\n<b>Proceeding will fully erase the disk.</b>\n\nContinue with installation?"

    (xzcat ${TIK_IMG_DIR}/${image_source_files} | pv -f -F "# %b copied in %t %r" | prun /usr/bin/dd of=${image_target} bs=64k) 2>&1 | d --progress --title="Installing ${TIK_OS_NAME}" --pulsate --auto-close --no-cancel --width=400
    prun /usr/bin/sync | d --progress --title="Syncing" --pulsate --auto-close --no-cancel --width=400
}

set_boot_target() {
	if [ "${debug}" == "1" ]; then
		log "[debug] Not setting EFI boot target"
	else
		# Cleanup any existing openSUSE boot entries
		prun-opt /usr/sbin/efibootmgr -B -L "openSUSE Boot Manager"
		prun /usr/sbin/efibootmgr -O
		# Currently assuming Aeon-like partition layout and shim name. This function will need extra intelligence to probe partitions for other image layouts
		prun /usr/sbin/efibootmgr -c -L "openSUSE Boot Manager" -d ${TIK_INSTALL_DEVICE} -l "\EFI\systemd\shim.efi" -p 2
		# Log to show the resulting eficonfig
		log "[efibootmgr] $(prun /usr/sbin/efibootmgr)"
	fi
}

load_modules() {
local module_dir
if [[ $2 = "custom" ]]; then
    module_dir=$TIK_CUSTOM_DIR/modules/$1
else
    module_dir=$tik_dir/modules/$1
fi
if [ -n "$(ls -A $module_dir)" ]; then
for f in $module_dir/*
    do
    	tik_module="$f"
    	log "[START] $module_dir/$f"
    	. $f
    	log "[STOP] $module_dir/$f"
    done
fi
tik_module="tik"
}
