# @description Builds Radxa's `rkdeveloptool` and flashes the freshly built image to a connected Rockchip device over USB. Enables `rkbin-tools` for the `ROCKUSB_BLOB` SPL loader, waits for the board, switches Maskrom to Loader mode, writes the image (`wl`), then resets it. Cannot run under Docker.

#
# SPDX-License-Identifier: GPL-2.0
# Copyright (c) 2023 Ricardo Pardini <ricardo@pardini.net>
# This file is a part of the Armbian Build Framework https://github.com/armbian/build/
#

# This adds the required host-side dependencies, clones and builds rkdeveloptool.
# When build is done, it enters a loop to wait for the device to be connected.
# When the device is connected, it check if device is in Maskrom or Loader mode.
# If in Markrom mode: use the ROCKUSB_BLOB to init Loader mode.
# If in Loader mode: it flashes the device with the produced image.
# It then resets the device via rkdeveloptool rd.

enable_extension "rkbin-tools" # which brings in the needed loader binaries for Maskrom -> Loader mode

function add_host_dependencies__rkdevflash() {
	display_alert "Preparing rkdevflash host-side dependencies" "${EXTENSION}" "info"
	EXTRA_BUILD_DEPS+=("build-tools::libudev-dev" "build-tools::libusb-1.0-0-dev" "native-toolchain::dh-autoreconf" "native-toolchain::build-essential")
}

function extension_finish_config__900_rkdevflash() {
	display_alert "Preparing rkdevflash extension" "${EXTENSION}" "info"
	declare -g -r rkdeveloptool_dir="${SRC}/cache/sources/rkdeveloptool"
	declare -g -r rkdeveloptool_bin_path="${rkdeveloptool_dir}/rkdeveloptool"

	# if under docker, exit_with_error; we can't get at the USB needed for rkdeveloptool.
	if [[ "${ARMBIAN_RUNNING_IN_CONTAINER}" == "yes" ]]; then
		exit_with_error "rkdevflash: running under Docker is not supported. rkdeveloptool requires direct access to the host USB devices."
	fi

	# Determine the SPL Loader to be used. This could be better done in board/family file config.
	declare -g rkdeveloptool_spl_loader_blob="${ROCKUSB_BLOB:-"undetermined"}"
	if [[ "${rkdeveloptool_spl_loader_blob}" == "undetermined"* ]]; then
		exit_with_error "rkdevflash: ROCKUSB_BLOB is unset, unsupported LINUXFAMILY '${LINUXFAMILY}'?"
	fi
	declare -g -r rkdeveloptool_spl_loader_blob="${rkdeveloptool_spl_loader_blob}"
	declare -g -r rkdeveloptool_spl_loader_blob_path="${SRC}/cache/sources/rkbin-tools/${rkdeveloptool_spl_loader_blob}"

}

function host_dependencies_ready__rkdevflash() {
	display_alert "Preparing rkdevflash for usage" "${EXTENSION}" "info"

	if [[ ! -f "${rkdeveloptool_bin_path}" ]]; then
		display_alert "rkdeveloptool not found, building it" "${EXTENSION}" "info"
		build_rkdeveloptool
	fi

	fetch_sources_tools__rkbin_tools # explicit call to extension method; we need the rkbins early.

	if [[ ! -f "${rkdeveloptool_spl_loader_blob_path}" ]]; then
		exit_with_error "rkdevflash: SPL loader blob not found: '${rkdeveloptool_spl_loader_blob_path}'"
	fi

	check_rkdeveloptool # logs the version of rkdeveloptool

	display_alert "rkdevflash using RockUSB loader blob" "${EXTENSION} :: ${rkdeveloptool_spl_loader_blob}" "info"

	declare rkdeveloptool_is_device_connected="no" rkdeveloptool_device_id="" rkdeveloptool_device_mode=""
	list_devices_rkdeveloptool # early listing of devices at the start of build; gives early recompense if you have a device connected.
}

function post_build_image_write__rkdevflash() {
	: "${built_image_file:?built_image_file is not set}" # check built_image_file is set
	display_alert "Starting flash process" "${EXTENSION} :: ${built_image_file}" "info"
	flash_image_rkdeveloptool "${built_image_file}"
}

function wait_for_connected_device_rkdeveloptool() {
	display_alert "Waiting for device to be connected" "${EXTENSION}" "info"
	rkdeveloptool_is_device_connected="no" rkdeveloptool_device_id="" rkdeveloptool_device_mode=""
	list_devices_rkdeveloptool

	declare -i counter=0
	while [[ "${rkdeveloptool_is_device_connected}" != "yes" ]]; do
		counter+=1
		display_alert "Waiting for rkdeveloptool device to be connected (loop ${counter})" "${EXTENSION}" "debug"
		sleep 1
		list_devices_rkdeveloptool
	done

}

function flash_image_rkdeveloptool() {
	if [[ ! -f "${1}" ]]; then
		exit_with_error "rkdevflash: Image file not found: '${1}'"
	fi

	display_alert "Flashing image" "${EXTENSION} :: ${1}" "info"

	declare rkdeveloptool_is_device_connected="no" rkdeveloptool_device_id="" rkdeveloptool_device_mode=""
	wait_for_connected_device_rkdeveloptool

	while [[ "${rkdeveloptool_device_mode}" != "Loader" ]]; do
		wait_for_connected_device_rkdeveloptool
		if [[ "${rkdeveloptool_device_mode}" == "Maskrom" ]]; then
			display_alert "Loading SPL RockUSB loader" "${EXTENSION} :: ${rkdeveloptool_spl_loader_blob}" "info"
			declare loader_worked="no"
			timeout 10 "${rkdeveloptool_bin_path}" db "${rkdeveloptool_spl_loader_blob_path}" && loader_worked="yes" || true
			if [[ "${loader_worked}" != "yes" ]]; then
				display_alert "rkdevflash: Failed to load SPL RockUSB loader. Timeout? Please reset the Rockchip device (again) into recovery..." "${EXTENSION}" "wrn"
				sleep 5
			else
				display_alert "RockUSB Loader deployed" "${EXTENSION}" "cachehit"
				break # break out of the loop. once loaded, the spl_loader does change stuff to Loader mode; only u-boot rockusb/"download mode" does that.
			fi
		fi
	done

	wait_for_connected_device_rkdeveloptool
	display_alert "Flashing image" "${EXTENSION} :: ${1}" "info"
	"${rkdeveloptool_bin_path}" wl 0x0 "${1}"
	display_alert "Restarting device after flash" "${EXTENSION}" "ext"
	"${rkdeveloptool_bin_path}" rd
}

function list_devices_rkdeveloptool() {
	rkdeveloptool_is_device_connected="no"  # outer scope
	rkdeveloptool_device_id="undetermined"  # outer scope
	rkdeveloptool_device_mode="not_present" # outer scope
	display_alert "Listing rkdeveloptool devices" "${EXTENSION}" "debug"
	if ! "${rkdeveloptool_bin_path}" ld &> /dev/null; then
		display_alert "No rkdeveloptool device found." "${EXTENSION}" ""
		display_alert "Use an USB-C cable to connect the Rockchip device to this host." "${EXTENSION}" ""
		display_alert "Power on the Rockchip device and put it in recovery mode." "${EXTENSION}" ""
		display_alert "For example, click & hold Recovery button and then click Reset button once." "${EXTENSION}" ""
		display_alert "This is try number ${counter:-"0"}" "${EXTENSION}" ""
	else
		# Some device is connected, run again and parse the id and mode.
		declare rkdeveloptool_ld_output
		rkdeveloptool_ld_output="$("${rkdeveloptool_bin_path}" ld || echo -n none)"
		if [[ "${rkdeveloptool_ld_output}" == *"none"* ]]; then
			return 0
		fi

		rkdeveloptool_is_device_connected="yes"

		# cleanup the output, rk messes it up so badly it hurts
		rkdeveloptool_ld_output="$(echo "${rkdeveloptool_ld_output}" | tr -d '\r')"              # cleanup
		rkdeveloptool_ld_output="$(echo "${rkdeveloptool_ld_output}" | head -1 | xargs echo -n)" # cleanup
		rkdeveloptool_device_id="$(echo "${rkdeveloptool_ld_output}" | cut -d' ' -f2)"           # 2/3 column
		rkdeveloptool_device_mode="$(echo "${rkdeveloptool_ld_output}" | cut -d' ' -f3)"         # 3/3 column

		display_alert "rkdeveloptool ld output" "${EXTENSION} :: '${rkdeveloptool_ld_output}'" "debug"
		display_alert "rkdeveloptool device detected" "${EXTENSION} :: device '${rkdeveloptool_device_id}'" "info"
		display_alert "rkdeveloptool device mode" "${EXTENSION} :: mode '${rkdeveloptool_device_mode}'" "info"
	fi

	return 0
}

function build_rkdeveloptool() {
	# Clone rkdeveloptool
	#fetch_from_repo "https://github.com/rockchip-linux/rkdeveloptool" "rkdeveloptool" "branch:master" # pristine rk
	fetch_from_repo "https://github.com/radxa/rkdeveloptool.git" "rkdeveloptool" "branch:master" # Radxa's fork has fixes

	# Build rkdeveloptool
	pushd "${rkdeveloptool_dir}" &> /dev/null || exit_with_error "Fail to cd to rkdeveloptool: ${rkdeveloptool_dir}"

	# Patch `-Werror` out of Makefile so it builds with a warning. It still works.
	# sed -i -e 's/-Werror //' Makefile.am || exit_with_error "Fail to patch Makefile.am" # Not needed with Radxa's fork

	run_host_command_logged pipetty aclocal
	run_host_command_logged pipetty autoreconf -i
	run_host_command_logged pipetty autoheader
	run_host_command_logged pipetty automake --add-missing
	run_host_command_logged pipetty ./configure
	run_host_command_logged pipetty make -j$(nproc)
	run_host_command_logged pipetty ls -la "${rkdeveloptool_bin_path}"

	popd &> /dev/null || exit_with_error "Fail to cd back to armbian-build"
}

function check_rkdeveloptool() {
	declare rkdevtool_version="undetermined"
	rkdevtool_version="$("${rkdeveloptool_bin_path}" --version)"
	rkdevtool_version="$(echo "${rkdevtool_version}" | tr -dc '[:print:]' | xargs echo -n)" # cleanup, rk emits a carriage return we don't want
	display_alert "rkdeveloptool version" "${EXTENSION} :: '${rkdevtool_version}'" ""

	# as a courtesy to the user, install a symlink into /usr/local/bin, so rkdeveloptool can be called by itself as well
	if [[ -f /usr/local/bin/rkdeveloptool ]]; then
		run_host_command_logged rm -f /usr/local/bin/rkdeveloptool
	fi
	run_host_command_logged sudo ln -sf "${rkdeveloptool_bin_path}" /usr/local/bin/rkdeveloptool
}