#!/bin/sh

readonly MAKE=${MAKE:-make}
readonly PSVN=${PSVN:-svn}
readonly SRCDIR=${SRCDIR:-/usr/src}
readonly XDG_CONFIG_HOME="${XDG_CONFIG_HOME:-${HOME}/.config}"
readonly CONF_FILE="$XDG_CONFIG_HOME/upgrade-base"

readonly tcols=$(stty size | cut -d" " -f2)
readonly tlines=$(stty size | cut -d" " -f1)

[ $tcols -lt 80 ] && dwidth=$((tcols-4)) || dwidth=76

skip_tree_update=0	# don't update system sources before build

ask_yesno() {
	local answer

	while true; do
		read -r -p "$1 (y/n)? [y] " answer
		[ "$answer" ] || answer="y"

		case $answer in
		[Nn])
			return 1 ;;
		[Yy])
			return 0
		esac
	done
}

delete_old_files() {
	local file items tmpfile=$(mktemp -t "${0##*/}") to_remove tstr type

	[ "$tmpfile" ] || return

	for type in libs files dirs; do
		$MAKE check-old-$type | grep '^/' > "$tmpfile"
		tstr=$(echo $type | cut -b1)
		for file in $(cat "$tmpfile"); do
			items="$items \"$file\" $tstr on"
		done
	done
	if [ "$items" ]; then
		echo "--stdout --checklist \"Choose old files to delete\" \
			$((tlines-3)) $dwidth $((tlines-5)) \
			$items" > "$tmpfile"
		to_remove=$(dialog --file "$tmpfile")
		dialog --clear
		for file in $to_remove; do
			$SUDO rm -rf "$file"
		done
	fi
	rm "$tmpfile"
}

display_usage() {
	<< EOF >&2 cat
Usage: ${0##*/} -h
       ${0##*/} [-su]

    -h - show this help
    -s - reset status and begin from start
    -u - skip system source tree update before build

EOF
	exit 1
}

# $1 make target
# $2 state status afterwards
exec_cmd() {
	if ! $SUDO env PORTSDIR="$PORTSDIR" PORTS_MODULES="$ports_modules" $1; then
		echo "===> '$1' failed" >&2
		exit 1
	fi
	save_state "$2"
}

last_updating_entry() {
	if [ -f "$SRCDIR/UPDATING" ]; then
		grep -E -m 1 '^[0-9]{8}\:' "$SRCDIR/UPDATING" | sed 's|:||'
	else
		echo "0"
	fi
}

main_loop() {
	local l_entry n_entry state=$(cat "$CONF_FILE" 2>/dev/null)

	case $state in
	"")
		if [ $skip_tree_update -eq 0 ]; then
			l_entry=$(last_updating_entry)
			if ! $SUDO $PSVN update; then
				echo "===> Updating of source tree failed" >&2
				exit 1
			fi

			n_entry=$(last_updating_entry)
			if [ $n_entry -gt $l_entry ]; then
				printf "===> New entries since last upgrade in %s/UPDATING file:\n\n" "$SRCDIR"
				awk -F ':' -v entry="$l_entry" '
				/^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]:/ {
					if (entry == $1)
						exit;
					else
						new_entry=1;
				}
				new_entry { print $0 }' "$SRCDIR/UPDATING"

				if ! ask_yesno "Continue with upgrade"; then
					save_state "REPO_UPDATED"
					exit 0
				fi
			fi
		fi

		save_state "REPO_UPDATED" ;;
	"REPO_UPDATED")
		exec_cmd "$MAKE buildworld $make_args" "WORLD_BUILT" ;;
	"WORLD_BUILT")
		exec_cmd "$MAKE buildkernel $make_args" "KERNEL_BUILT" ;;
	"KERNEL_BUILT")
		exec_cmd "$MAKE installkernel $make_args" "KERNEL_INSTALLED"
		printf "\n===> You need to reboot and run %s again to finish installation.\n\n" "${0##*/}"
		reboot_ask ;;
	"KERNEL_INSTALLED")
		exec_cmd "mergemaster -p" "MERGEMASTER_INIT"  ;;
	"MERGEMASTER_INIT")
		exec_cmd "$MAKE installworld $make_args" "WORLD_INSTALLED" ;;
	"WORLD_INSTALLED")
		exec_cmd "mergemaster -Fui" "MERGEMASTER_DONE" ;;
	"MERGEMASTER_DONE")
		if ask_yesno "Delete old files from system"; then
			echo "===> Checking for old files..."
			delete_old_files
		fi
		save_state ""
		printf "\n===> You need to reboot to complete upgrade.\n\n"
		reboot_ask ;;
	*)
		echo "===> Garbage in $CONF_FILE, try upgrade-base -s" >&2
		exit 1
	esac
	main_loop
}

reboot_ask() {
	ask_yesno "Reboot now" && $SUDO reboot
	exit 0
}

# $1 state
save_state() {
	[ -d "$XDG_CONFIG_HOME" ] || mkdir "$XDG_CONFIG_HOME"
	echo "$1" > "$CONF_FILE"
}

while getopts "hsu" option; do
	case $option in
	s)
		echo > "$CONF_FILE" ;;
	u)
		skip_tree_update=1 ;;
	*)
		display_usage
	esac
done

if [ $(id -u) -ne 0 ]; then
	if ! which sudo >/dev/null; then
		echo "===> sudo not found" >&2
		echo "===> This script requires root privileges or properly configured sudo." >&2
		exit 1
	fi
	SUDO="sudo"
fi

if [ ! -d "$SRCDIR" ]; then
	echo "===> System source directory not found: $SRCDIR" >&2
	echo "===> Try setting SRCDIR environment variable" >&2
	exit 1
fi

jobs=$(sysctl -n hw.ncpu)
[ "$jobs" ] && make_args="$make_args -j$jobs"

ports_modules=$(pkg which -qo /boot/modules/* | sort -u | tr '\n' ' ')
if [ "$ports_modules" ]; then
	export PORTSDIR=${PORTSDIR:-/usr/ports}
	if [ ! -d "$PORTSDIR" ]; then
		echo "===> Ports directory not found: $PORTSDIR" >&2
		echo "===> Try setting PORTSDIR environment variable" >&2
		exit 1
	fi
fi
cd "$SRCDIR" || exit 1
main_loop
