#!/bin/bash -efu

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

check_parameter ALTBOOT_LOCALDEV

target=
devspec=
devices=

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()
{
	devspec=
	label=
	uuid=
	disk=
	part=
}

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

	local rc=0 opts="${options-}"
	local image newdev mp="$destdir"
	local squash_err="however the specified squash file not found:"
	local fuid_err="however the specified fuid file not found:"

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

	if [ "$method" = cdrom ]; then
		opts="-t iso9660 -o ro${opts:+,$opts}"

		case "$target" in
		*[0-9])	newdev="${target}p1";;
		*)	newdev="${target}1";;
		esac

		if [ -b "$newdev" ] && run mount $opts -- "$newdev" "$mp"; then
			debug "CD-ROM target replaced to the first partition: $newdev"
			target="$newdev"
		else
			debug "mounting original target device as ISO-9660: $target"
			run mount $opts -- "$target" "$mp" || rc=1
		fi

		if [ "$rc" != 0 ]; then
			message "can't mount target CD/DVD/Pendrive: $target"
			leave "is_target_found"
			target=
			return 1
		fi

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

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

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

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

		IM_update_bootsplash "found_media"

		leave "is_target_found"
		return 0
	fi

	debug "mounting original target device as disk or partition: $target"

	if ! run mount -o "ro${opts:+,$opts}" -- "$target" "$mp"; then
		message "can't mount target disk or partition: $target"
		leave "is_target_found"
		target=
		return 1
	fi

	message "$target has been mounted successfully"

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

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

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

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

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

		IM_update_bootsplash "found_media"

		leave "is_target_found"
		return 0
	fi

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

		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" ||:
			leave "is_target_found"
			target=
			return 1
		fi

		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" ||:
			leave "is_target_found"
			target=
			return 1
		elif ! run mount --bind -- "${image}${directory}" "$mp"; then
			message "$target will be unmounted"
			run umount -fl -- "$image" ||:
			leave "is_target_found"
			target=
			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"

		leave "is_target_found"
		return 0
	fi

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

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

	image="$datadir"
	[ -z "$ALTBOOT_OLDROOT" ] ||
		image="$BC_ROOT"
	run mkdir -p -- "$image"
	newdev=

	if run mount --move -- "$mp" "$image"; then
		lomount newdev "${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 "$newdev" ] ||
			run losetup -d -- "$newdev" ||:
		run umount -fl -- "$image" ||:
		leave "is_target_found"
		target=
		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"

	leave "is_target_found"
}

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

	debug "TARGET: '$target'"

	local rc=0

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

	local title="[ Optional fields ]"

	local text="Please enter the full path to an ISO-image"
	[ -z "$squash" ] ||
		text="$text containing the squash $squash"
	text="$text or leave this field blank to use mounted partition"
	text="$text 'as is', or cancel to return step back."

	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
		else
			directory="${directory//\"/\\\"}"
			options="${options//\"/\\\"}"

			if is_target_found; then
				debug "TARGET: '$target'"
				break
			elif [ -n "$directory" ]; then
				IM_errmsg "Can't mount ISO-image: '$directory'!"
				rc=1
				break
			else
				IM_errmsg "Can't mount boot device: $target!"
				rc=1
				break
			fi
		fi
	done

	leave "boot_device_choice"
	return $rc
}

main_loop()
{
	enter "main_loop"

	local nsec=0

	start_scan

	while :; do
		debug "Idle before scan devices"
		sleep 1
		scan_devices

		if is_target_found; then
			debug "TARGET: '$target'"
			IM_ponder_stop
			break
		fi

		nsec=$((1 + $nsec))
		[ "$nsec" -ge "$timeout" ] ||
			continue
		IM_ponder_stop

		if [ -z "$NOASKUSER" ]; then
			boot_device_choice && break ||:
		elif [ -z "$devspec" ]; then
			fatal "device not specified, dialogs are disabled"
		else
			fatal "specified device not found, dialogs are disabled"
		fi

		IM_errmsg "Boot device not detected, try again!"
		debug "the device is not ready, move on to the next iteration"
		start_scan
		timeout=20
		nsec=0
	done

	leave "main_loop"
}


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

[ -z "${disk}${part}${uuid}${label}" ] ||
	devspec=1
[ "$directory" != / ] ||
	directory=

# At first, try to use previous step results
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 is_target_found; then
		debug "TARGET: '$target'"
		debug "$PROG finished"
		exit 0
	fi
fi

timeout="${timeout:-$BC_DEVICE_TIMEOUT}"
label="${label//\\040/ }"
IM_start_output choice form errmsg ponder

# With method=auto, the device is selected manually
if [ -z "$NOASKUSER" ] && [ -f "$altboot_auto" ]; then
	reset_devspec
	scan_devices

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

main_loop

debug "$PROG finished"
