#!/bin/bash -efu

. altboot-sh-functions
. scandev-sh-functions

check_parameter ALTBOOT_LOCALDEV

devspec=

b_a()
{
	get_bootarg LOCALDEV "$1"
}

b_a method
b_a disk
b_a part
b_a fuid
b_a uuid
b_a label
b_a squash
b_a directory
b_a timeout
b_a options


reset_devspec()
{
	[ -z "$devspec" ] ||
		__scandev_last=
	devspec=
	label=
	uuid=
	disk=
	part=

	debug "the device specification has been reset"
}

is_target_found()
{
	local rc=0

	[ -n "$target" ] ||
		return 1
	enter "is_target_found"

	is_target_found_inner || rc="$?"
	unset _check_files

	# Reset target devices
	if [ "$rc" != 0 ]; then
		target=
		whole=
	fi

	leave "is_target_found"
	return $rc
}

is_target_found_inner()
{
	local image rc=0 opts="${options-}" mp="$destdir"
	local cdrom_opts="-t iso9660 -o ro${opts:+,$opts}"
	local squash_err="however the specified squash file not found:"
	local fuid_err="however the specified fuid file not found:"

	_check_files()
	{
		if [ -n "$squash" ] && [ ! -s "${mp}${directory}/$squash" ]; then
			message "$squash_err '$directory/$squash'"
			rc=1
		fi

		if [ -n "$fuid" ] && [ ! -f "${mp}${directory}/$fuid" ]; then
			message "$fuid_err '$directory/$fuid'"
			rc=1
		fi

		if [ "$rc" != 0 ]; then
			message "$target will be unmounted"
			run umount -fl -- "$mp" ||:
		fi

		return $rc
	}

	[ -z "$ALTBOOT_OLDROOT" ] || [ -z "$OEM_CDROOT" ] ||
		mp="$OEM_CDROOT"
	run mkdir -p -- "$mp"

	if [ "$method" = cdrom ]; then
		message "mounting the original target device as ISO-9660: $target"

		if ! run mount $cdrom_opts -- "$target" "$mp"; then
			message "can't mount the target CD/DVD/Pendrive: $target"
			return 1
		fi

		directory=
		_check_files || return 1

		if [ -n "$ALTBOOT_OLDROOT" ]; then
			[ -z "$whole" ] || mediacheck "$whole"
			stage2_setenv DEVICE "${target#/dev/}"
			stage2_setenv METHOD "cdrom"
			stage2_setenv PREFIX "/"
			bypass_results "$mp"
		fi

		IM_update_bootsplash "found_media"

		return 0
	fi

	[ -n "$whole" ] && opts="$cdrom_opts" || opts="-o ro${opts:+,$opts}"
	debug "mounting the original target device as a disk or partition: $target"

	if ! run mount $opts -- "$target" "$mp"; then
		message "can't mount the target disk or partition: $target"
		return 1
	fi

	message "$target has been mounted successfully"

	if [ -z "$directory" ]; then
		message "assuming disk or partition without a 'directory' component"

		_check_files || return 1

		if [ -n "$ALTBOOT_OLDROOT" ]; then
			[ -z "$whole" ] || mediacheck "$whole"
			stage2_setenv DEVICE "${target#/dev/}"
			stage2_setenv METHOD "disk"
			stage2_setenv PREFIX "/"
			bypass_results "$mp"
		fi

		IM_update_bootsplash "found_media"

		return 0
	fi

	if [ -d "${mp}${directory}" ]; then
		message "assuming a directory on disk, the relative path: '$directory'"

		_check_files ||
			return 1
		image="$datadir"
		[ -z "$ALTBOOT_OLDROOT" ] ||
			image="$BC_ROOT"
		run mkdir -p -- "$image"

		if ! run mount --move -- "$mp" "$image"; then
			message "$target will be unmounted"
			run umount -fl -- "$mp" ||:
			return 1
		elif ! run mount --bind -- "${image}${directory}" "$mp"; then
			message "$target will be unmounted"
			run umount -fl -- "$image" ||:
			return 1
		fi

		if [ -n "$ALTBOOT_OLDROOT" ]; then
			stage2_setenv DEVICE "${target#/dev/}"
			stage2_setenv METHOD "disk"
			stage2_setenv PREFIX "$directory"
			stage2_setenv PIGGYBACK 1
			bypass_results "$mp"
		fi

		IM_update_bootsplash "found_media"

		return 0
	fi

	if [ ! -s "${mp}${directory}" ]; then
		message "however the specified directory or ISO-image not found: '$directory'"
		run umount -fl -- "$mp" ||:
		return 1
	fi

	message "assuming an ISO-image on the disk, the relative path: '$directory'"

	[ -z "$ALTBOOT_OLDROOT" ] ||
		mediacheck "${mp}${directory}"
	image="$datadir"
	[ -z "$ALTBOOT_OLDROOT" ] ||
		image="$BC_ROOT"
	run mkdir -p -- "$image"

	local loopdev=

	if run mount --move -- "$mp" "$image"; then
		lomount loopdev "${image}${directory}" "$mp" || rc=1
	else
		image="$mp"
		rc=1
	fi

	if [ "$rc" = 0 ] && [ -n "$squash" ] && [ ! -s "$mp/$squash" ]; then
		message "$squash_err '/$squash'"
		rc=1
	fi

	if [ "$rc" = 0 ] && [ -n "$fuid" ] && [ ! -f "$mp/$fuid" ]; then
		message "$fuid_err '/$fuid'"
		rc=1
	fi

	if [ "$rc" != 0 ]; then
		message "$target will be unmounted"
		[ "$mp" = "$image" ] ||
			run umount -fl -- "$mp" ||:
		[ -z "$loopdev" ] ||
			run losetup -d -- "$loopdev" ||:
		run umount -fl -- "$image" ||:
		return 1
	fi

	if [ -n "$ALTBOOT_OLDROOT" ]; then
		stage2_setenv DEVICE "${target#/dev/}"
		stage2_setenv METHOD "disk"
		stage2_setenv PREFIX "$directory"
		stage2_setenv PIGGYBACK 1
		bypass_results "$mp"
	fi

	IM_update_bootsplash "found_media"
}

boot_device_choice()
{
	enter "boot_device_choice"

	# Rewind one step back when user has cancel choice
	device_choice || altboot_restart
	reset_devspec

	if [ -z "$target" ]; then
		leave "boot_device_choice"
		return 1
	fi

	local rc=0

	if [ "$method" = cdrom ]; then
		is_target_found || rc="$?"
		leave "boot_device_choice"
		return $rc
	fi

	local title="[ Optional fields ]"
	local lnk="See the log for details."
	local text="Please enter the full path to an ISO-image"

	if [ -n "$squash" ]; then
		text="$text containing the squash $squash"
	elif [ -n "${OEM_DISTRIBUTION-}" ]; then
		text="$text containing $OEM_DISTRIBUTION"
	fi

	text="$text or leave this field blank to use the mounted partition"
	text="$text 'as is', or cancel to return to the previous step."

	while :; do
		if ! IM_form "$title" "$text" 3		\
			directory 128 "ISO-image"	\
			options    64 "Mount options"	\
			#
		then
			boot_device_choice || rc="$?"
			break
		fi

		if [ -n "${directory//[^[:space:]]/}" ] ||
		   [ -n "$directory" ] && [ "${directory:0:1}" != "/" ]
		then
			IM_errmsg "Invalid path: '$directory'!"
			continue
		fi

		directory="${directory//\"/\\\"}"
		options="${options//\"/\\\"}"

		if is_target_found; then
			rc=0
		elif [ -n "$directory" ]; then
			IM_errmsg "Can't use the ISO-image: '$directory'! $lnk"
			rc=1
		else
			IM_errmsg "Can't use the boot device: $target! $lnk"
			rc=1
		fi

		break
	done

	leave "boot_device_choice"
	return $rc
}

main_loop()
{
	local currtime=0
	local deadline=0

	_reset_deadline()
	{
		bc_seconds currtime
		deadline="$(( $currtime + ${1-$timeout} ))"
	}

	enter "main_loop"

	start_dev_scan
	_reset_deadline

	while :; do
		scan_devices && is_target_found && stop_dev_scan && break ||:

		whole=
		target=
		bc_seconds currtime
		[ "$currtime" -ge "$deadline" ] ||
			continue

		if [ -n "$NOASKUSER" ]
		then
			stop_dev_scan
			[ -n "$devspec" ] ||
				fatal "the device is not specified, dialogs are disabled"
			fatal "the specified device not found, dialogs are disabled"
		fi

		reset_devspec
		scan_devices ||:
		stop_dev_scan
		boot_device_choice && break ||:
		IM_errmsg "The boot device is not detected, try again!"
		debug "the device is not ready, move on to the next iteration"
		start_dev_scan
		_reset_deadline 20
	done

	unset _reset_deadline

	leave "main_loop"
}


# Entry point
debug "$PROG started ($(get_parameter ALTBOOT_LOCALDEV))"

[ -z "${disk}${part}${uuid}${label}" ] ||
	devspec=1
[ "$directory" != / ] ||
	directory=
label="${label//\\040/ }"
timeout="${timeout:-$BC_DEVICE_TIMEOUT}"
IM_start_output choice form errmsg ponder

# First, try to use the results of the previous step
if [ -z "$devspec" ] && [ -n "$prevdir" ] && [ ! -f "$altboot_auto" ]; then
	if [ -s "$prevdir/DEVNAME" ]; then
		read -r target <"$prevdir/DEVNAME" ||:
	elif [ -b "$prevdir/dev" ]; then
		target="$prevdir/dev"
	fi

	if [ -n "$target" ]; then
		devname2target "$target"
		debug_found_target
	fi

	if is_target_found; then
		debug "$PROG finished"
		exit 0
	fi
fi

# With method=auto (or without disk specification), the device is selected manually
if [[ -z "$devspec" && "$method" = disk ]] || [ -f "$altboot_auto" ]; then
	if [ -z "$NOASKUSER" ]; then
		start_dev_scan
		reset_devspec
		scan_devices ||:
		stop_dev_scan

		if boot_device_choice; then
			debug "$PROG finished"
			exit 0
		fi
	fi
fi

main_loop

debug "$PROG finished"
