#!/bin/bash

## Authors:
##   Ajrat Makhmutov <rauty@altlinux.org>
##
## Copyright (C) 2024-2025  Basealt LLC
##
## This file is part of alterator-kopidel.
##
## alterator-kopidel is free software: you can redistribute it and/or modify it under the terms
## of the GNU General Public License as published by the Free Software Foundation,
## either version 3 of the License, or (at your option) any later version.
##
## alterator-kopidel is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
## without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
## See the GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License along with alterator-kopidel.
## If not, see <https://www.gnu.org/licenses/>.

. alterator-sh-functions

if [ "$(id -u)" -ne 0 ]; then
	echo "$(_ "Please run the kopidel as root")." >&2;
	exit 1
fi

. "${KOPIDEL_LIBDIR:-/usr/lib/alterator-kopidel}/steps"

while [[ $# -gt 0 ]]; do case "$1" in
	-X | --ignored-from)
		in_ignored_files="$2"
		shift # past argument
		shift # past value
	;;
	--xz)
		in_compress_copied='#t'
		compress_copied_option='--xz'
		shift # past argument
	;;
	-O | --oem-mode)
		in_oem_mode='#t'
		shift # past argument
	;;
	--grub-efi-removable)
		in_grub_efi_removable='#t'
		shift # past argument
	;;
	--list-workdirs)
		only_print_workdirs="#t"
		shift
	;;
	--list-exdrives)
		only_print_exdrives="#t"
		shift
	;;
	-v | --version)
		rpm -q alterator-kopidel --qf "%{version}\n"
		exit 0
	;;
	-s | --step)
		step="$2"
		shift # past argument
		shift # past value
	;;
	-h | --help)
		echo $(_ 'Creates a "razlivochniy obraz".')
		echo $(_ "Usage:")
		echo $(_ "kopidel [Options] workdir")
		echo $(_ "kopidel [Options] /dev/external_drive")
		echo
		echo $(_ '"Workdir" is the path to the working directory where razlivochniy.img is created.')
		echo $(_ 'Please check that there is enough disk space available.')
		echo $(_ "The workdir name should be alterator-kopidel-workdir.")
		echo
		echo $(_ "Options:")
		echo "	-X, --ignored-from   $(_ "The path to the file with the list of ignored files")."
		echo "	--xz                 $(_ "Compress the copied file system using xz")."
		echo "	-O, --oem-mode       $(_ "Installation in OEM mode")."
		echo "	-h, --help           $(_ "Display this message")."
		echo "	-v, --version        $(_ "Print version info and exit")."
		echo "	-s, --step           $(_ "Run a specific step instead of all of them (this is for testing only)")."
		echo "	--list-workdirs      $(_ "Display a list of possible working directories and exit")."
		echo "	--list-exdrives      $(_ "Display a list of possible external drives and exit")."
		if is_img_efi_bootable; then
		echo "	--grub-efi-removable $(_ "Install GRUB with the --removable flag")."
		fi
		echo
		echo $(_ "Please report any problems to <https://bugzilla.altlinux.org/>")
		exit 0
	;;
	' ' | '' ) shift ;;
	-* | --*)
		echo "$(_ "Unknown option:") $1"
		exit 1
	;;
	*)
		workdir_or_exdrive="$1"
		shift # past argument
	;;
esac; done

if [ -z "$workdir_or_exdrive" ] && ! test_bool "$only_print_workdirs" && ! test_bool "$only_print_exdrives"; then
	echo "$(_ "ERROR: the working directory or external drive is not specified.")" >&2
	exit 1
fi

if [[ "$workdir_or_exdrive" == /dev/* ]]; then
	in_exdrive="$workdir_or_exdrive"
else
	in_workdir="$workdir_or_exdrive"
fi

if ! "$KOPIDEL_LIBEXECDIR/update-ignored-files.sh" "$in_ignored_files" 2>/dev/null; then
	echo "$(_ "An unreadable custom list of ignored files is specified")." >&2
	exit 1
fi

if [ -n "$in_workdir" ] || test_bool "$only_print_workdirs"; then
	if ! output="$("$KOPIDEL_LIBEXECDIR/workdirs-list.sh" "$compress_copied_option" 2>/dev/null)"; then
		echo "$(_ "There are no partitions available with enough free space. Required space:") $output." >&2
		exit 1
	fi

	if test_bool "$only_print_workdirs"; then
		echo $(_ "List of possible working directories:")
		echo "$output"
		exit 0
	fi

	if ! echo "$output" | grep -q "^$(realpath "$in_workdir") "; then
		echo "$(_ "ERROR: The working directory is not in the list of possible directories:")" >&2
		echo "$output" >&2
		exit 1
	fi
fi

if [ -n "$in_exdrive" ] || test_bool "$only_print_exdrives"; then
	if ! output="$("$KOPIDEL_LIBEXECDIR/exdrives-list.sh" "$compress_copied_option" 2>/dev/null)"; then
		echo "$(_ "There are no external drives available with enough space. Required space:") $output." >&2
		exit 1
	fi

	if test_bool "$only_print_exdrives"; then
		echo $(_ "List of possible external drives:")
		echo "$output"
		exit 0
	fi

	if ! echo "$output" | grep -q "^$(realpath "$in_exdrive") "; then
		echo "$(_ "ERROR: The external drive is not in the list of possible devices:")" >&2
		echo "$output" >&2
		exit 1
	fi
fi

draw_progress_bar_handler() (
	gray_background_color='\e[48;5;235m123'
	yellow_bold_color='\033[1;33m'
	default_color='\033[0m'

	#\U2588 - 1, \U2589 - 7/8, \U258A - 6/8, \U258B - 5/8, \U258C - 4/8, \U258D - 3/8, \U258E - 2/8, \U258F - 1/8
	fullblock="$(echo -e '\u2588')"
	utf_ends_blocks=( F E D C B A 9 8 )

	while read -r percentage_line; do
		if [[ "$percentage_line" != "step_progress: "* ]]; then
			continue
		fi

		percentage="$(echo "$percentage_line" | sed 's/^step_progress: //')"

		columns="$(tput cols)"

		bar_main=""
		bar_end=" $percentage%"

		# 1 - space, 3 - percentage number, 1 - perncentage symbol
		bar_length="$(( columns - 1 - 3 - 1 ))"
		bar_one_length="$( echo "scale=3; $bar_length / 100" | bc )"

		if [ "$percentage" -ge 100 ]; then
			bar_main="$(printf "%${bar_length}s" | sed "s/ /$fullblock/g")"
		else
			bar_end=" $bar_end"
			full_blocks_count="$(echo "$bar_one_length * $percentage / 1" | bc)"
			bar_main="$(printf "%${full_blocks_count}s" | sed "s/ /$fullblock/g")"
			bar_main+="$(echo -e "\u258${utf_ends_blocks[$(echo "$bar_one_length * $percentage % 1 / 0.125" | bc)]}")"
			bar_main+="$(printf "%$(( bar_length - full_blocks_count - 1 ))s")"
		fi

		echo -e -n "\r$yellow_bold_color$yellow_background_color$bar_main$default_color$bar_end"
	done
)

step_num=0
run_step_cli_wrapper_bar() {
	(( step_num+=1 ))
	echo "[$(date)] $(_ "Step") $step_num/$step_count: ${STEPS["$1"]}."
	run_step "$1" | draw_progress_bar_handler
	rtrn="${PIPESTATUS[0]}"
	echo
	return "$rtrn"
}

create_razlivochniy_obraz_pid=""
terminate_creating_razlivochniy_obraz() (
	if [ "$KOPIDEL_DEBUG" == 1 ]; then
		echo "terminate_creating_razlivochniy_obraz: $create_razlivochniy_obraz_pid" >&2
	fi

	# Do not type on a line where there is already a ^C.
	echo ""

	if [ -n "$create_razlivochniy_obraz_pid" ] && kill -0 "$create_razlivochniy_obraz_pid"; then
		echo "[$(date)] $(_ "User requested termination of the creation of the razlivochniy image")." >&2

		if [ "$KOPIDEL_DEBUG" == 1 ]; then
			echo "Terminating create_razlivochniy_obraz_pid $create_razlivochniy_obraz_pid" >&2
			pstree "$create_razlivochniy_obraz_pid" >&2
		fi

		if ! ps -o cmd --no-headers fp "$create_razlivochniy_obraz_pid" | grep -q kopidel &>/dev/null; then
			echo "terminate_creating_razlivochniy_obraz: CRITICAL ERROR: TRIED TO KILL NOT KOPIDEL CMD: $(ps -o cmd --no-headers fp "$create_razlivochniy_obraz_pid")" >&2
			return 1
		fi
		kill -- "-$create_razlivochniy_obraz_pid"
		sleep 0.5
		if kill -0 "$create_razlivochniy_obraz_pid" 2>/dev/null; then
			echo "Process $create_razlivochniy_obraz_pid ignored SIGTERM" >&2
		else
			echo "[$(date)] $(_ "The creation of the razlivochniy image has been successfully terminated")." >&2
		fi
		create_razlivochniy_obraz_pid=""
	fi
	run_step umount_razlivochniy >/dev/null
	exit 1
)

trap "tput cnorm" EXIT
tput civis # hide cursor
trap "terminate_creating_razlivochniy_obraz" INT TERM

if [ -n "$step" ]; then
	if [[ -v STEPS["$step"] ]] || [[ "$KOPIDEL_DEBUG" == 1 && " ${SUBSTEPS[*]} " =~ " $step " ]]; then
		step_count=1
		run_step_cli_wrapper_bar "$step" || exit 1
		echo "[$(date)] $(_ "The step was completed successfully")."
	else
		echo "$(_ "There is no such step:")"" $step." >&2
		echo "$(_ "Steps list:")" "${!STEPS[@]}"
		if [ "$KOPIDEL_DEBUG" == 1 ]; then
			echo "$(_ "Substeps list:")"" ${SUBSTEPS[*]}."
		fi
		exit 1
	fi
else
	if [[ $- == *m* ]]; then
		original_monitor_state="enabled"
	else
		original_monitor_state="disabled"
	fi
	set -m

	( if [ -n "$in_workdir" ] && test_bool "$in_compress_copied"; then
		step_count=9
		run_step_cli_wrapper_bar prepare_workdir                || exit 1
		run_step_cli_wrapper_bar create_copied_fs               || exit 1
		run_step_cli_wrapper_bar create_disk_partition_info     || exit 1
		run_step_cli_wrapper_bar create_install_scripts         || exit 1
		run_step_cli_wrapper_bar save_ready_to_use_image        || exit 1
		run_step_cli_wrapper_bar create_razlivochniy_mountpoint || exit 1
		run_step_cli_wrapper_bar use_ready_to_use_image         || exit 1
		run_step_cli_wrapper_bar install_grub                   || exit 1
		run_step_cli_wrapper_bar umount_razlivochniy            || exit 1
	elif { [ -n "$in_workdir" ] && ! test_bool "$in_compress_copied"; } || [ -n "$in_exdrive" ]; then
		step_count=7
		run_step_cli_wrapper_bar create_razlivochniy_mountpoint || exit 1
		run_step_cli_wrapper_bar prepare_workdir                || exit 1
		run_step_cli_wrapper_bar create_copied_fs               || exit 1
		run_step_cli_wrapper_bar create_disk_partition_info     || exit 1
		run_step_cli_wrapper_bar create_install_scripts         || exit 1
		run_step_cli_wrapper_bar install_grub                   || exit 1
		run_step_cli_wrapper_bar umount_razlivochniy            || exit 1
	fi ) &
	create_razlivochniy_obraz_pid="$!"

	if [ "$original_monitor_state" == "disabled" ]; then
		set +m
	fi

	if wait "$create_razlivochniy_obraz_pid"; then
		echo "[$(date)] $(_ "Successfully created a razlivochniy image")."
	else
		echo "[$(date)] $(_ "Failed to create a razlivochniy image")."
	fi
fi

tput cnorm # restore cursor
