#!/usr/bin/env bash

#  helper script: download u-boot
#
#	Copyright (C) 2021 Denis 'GNUtoo' Carikli  <GNUtoo@cyberdimension.org>
#	Copyright (C) 2022 Alper Nebi Yasak <alpernebiyasak@gmail.com>
#
#	This program 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.
#
#	This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#

[ "x${DEBUG+set}" = 'xset' ] && set -v
set -u -e

[[ -f build_error ]] && rm -f build_error

list_supported_boards() {
	for board in resources/u-boot/*; do
		if [ -d ${board} ]; then
			echo "${board#resources/u-boot/}"
		fi
	done
}

downloadfor() {
	board="${1}"

	# The loop will always exit, but this while loop is crafted
	# such that a tree referencing a tree that references another tree is possible
	# (and so on)
	while true; do
		ubrevision="undefined"
		ubtree="undefined"

		if [ ! -f "resources/u-boot/${board}/board.cfg" ]; then
			printf "ERROR: %s: board.cfg does not exist for '%s'\n" \
				"download/u-boot" "${board}"
			return 1
		fi

		if [ -f "resources/u-boot/${board}/seen" ]; then
			printf "ERROR: %s: logical loop; '%s' board.cfg refers to another tree, which ultimately refers back to '%s'.\n" \
				"download/u-boot" "${board}" "${board}"
			return 1
		fi

		# This is to override $ubrevision and $ubtree
		source "resources/u-boot/${board}/board.cfg" || touch build_error
		if [ -f build_error ]; then
			printf "ERROR: %s: problem sourcing %s/board.cfg\n" \
				"download/u-boot" "${board}"
			return 1
		fi
		touch "resources/u-boot/${board}/seen"

		if [ "${board}" != "${ubtree}" ]; then
			board="${ubtree}"
		else
			if [ "${ubtree}" = "undefined" ]; then
				printf "ERROR: %s: tree name undefined for '%s\n'" \
					"download/u-boot" "${board}"
				return 1
			fi

			if [ "${ubrevision}" = "undefined" ]; then
				printf "ERROR: %s: commit ID undefined for '%s'\n" \
					"download/u-boot" "${board}"
				return 1
			fi
			break
		fi
	done

	rm -f resources/u-boot/*/seen

	ubtree="u-boot/${ubtree}"
	if [ -d "${ubtree}" ]; then
	    printf \
		"REMARK: '%s' directory already exists. Skipping setup.\n" \
		"${ubtree}"
		if [ "${ubtree}" != "u-boot/${board}" ]; then
			printf "(for board: '${board}')\n"
		fi
		return 0
	fi

	if [ ! -d "u-boot" ]; then
		mkdir -p "u-boot"
	fi

	if [ ! -d "u-boot" ]; then
		printf \
			"ERROR: '%s' directory not created. Check file system permissions\n" \
			"u-boot"
		return 1
	fi

	uboot_dir="u-boot/u-boot"
	if [ ! -d "${uboot_dir}/.git" ] && [ -d "${uboot_dir}" ]; then
		rm -Rf "${uboot_dir}"
	fi

	if [ ! -d "${uboot_dir}" ]; then
		printf "Download u-boot from upstream:\n"
		git clone https://source.denx.de/u-boot/u-boot.git \
		    "${uboot_dir}" || \
		    rm -Rf "${uboot_dir}"
		if [ ! -d "${uboot_dir}" ]; then
			printf "WARNING: Upstream failed. Trying backup github repository:\n"
			git clone https://github.com/u-boot/u-boot.git \
				"${uboot_dir}" || \
				rm -Rf coreboot
		fi
		if [ ! -d "${uboot_dir}" ]; then
		    printf \
			"ERROR: %s: Problem with git-clone. Network issue?\n" \
			"download/u-boot"
			return 1
		fi
	fi

	git -C "${uboot_dir}" fetch origin "${ubrevision}" || touch build_error
	if [ -f build_error ]; then
	    printf \
		"ERROR: %s: Problem with git-fetch. Network issue?\n" \
		"download/u-boot"
		return 1
	fi

	cp -R "${uboot_dir}" "${ubtree}" || touch build_error
	if [ -f build_error ]; then
		printf "ERROR: %s: Unable to copy directory. Check file system permissions or free space.\n" \
			"download/u-boot"
		rm -Rf "${ubtree}/"
		return 1
	fi

	git -C "${ubtree}" reset --hard ${ubrevision} || \
	    touch build_error
	if [ -f build_error ]; then
	    printf \
		"ERROR: %s: Unable to reset to commit ID/tag '%s' for board '%s' on tree '%s'\n" \
		"download/u-boot" "${ubrevision}" "${board}" "${ubtree}"
		return 1
	fi

	git -C "${ubtree}" submodule update --init || touch build_error
	if [ -f build_error ]; then
		printf "ERROR: %s: Unable to update submodules for tree '%s'\n" \
			"${ubtree}"
		return 1
	fi

	for patch in resources/u-boot/${board}/patches/*.patch; do
		if [ ! -f "${patch}" ]; then
			continue
		fi

		git -C "${ubtree}" am "$(pwd)/${patch}" || touch build_error
		if [ -f build_error ]; then
			printf "ERROR: %s: Unable to apply patch '%s' for board '%s' on tree '%s'" \
				"download/u-boot" "${patch}" "${board}" "${ubtree}"
			git -C "${ubtree}" am --abort
			return 1
		fi
	done

	# extra.sh could be used to patch submodules, if you wanted to
	# It's impossible to predict what submodules will be available, and
	# it's rare that you'd want to patch them, so this is handled by
	# extra.sh on a per-board basis
	# In fact, extra.sh can be used for anything you want.
	if [ -f "resources/u-boot/${board}/extra.sh" ]; then
		"resources/u-boot/${board}/extra.sh" || touch build_error
		if [ -f build_error ]; then
			return 1
		fi
		return 0
	else
		return 0
	fi
}

strip_comments()
{
	file="$1"
	# Remove comments
	sed 's/#.*//' "${file}" | \
	    # Remove lines composed of whitespaces only
	    sed '/^\W\+$/d' | \
		# Remove empty lines
		sed '/^$/d'
}

usage()
{
	progname="./download u-boot"

	printf "Usage:\n"
	printf "\t%s [board]                        # %s\n" \
	       "${progname}" \
	       "Download u-boot for the given board"
	printf "\t%s --list-boards                  # %s\n" \
	       "${progname}" \
	       "List supported boards"
	printf "\t%s --help                         # %s\n" \
	       "${progname}" \
	       "Prints this help"
}

download_uboot_board()
{
	board="${1}"
	ubtree="u-boot/${board}"

	printf "Downloading u-boot "
	printf "and (if exist in build system) applying patches\n"
	downloadfor "${board}"

	rm -f "build_error"
	printf "\n\n"
}

if [ $# -eq 0 ] ; then
	for board in $(list_supported_boards); do
		download_uboot_board "${board}"
	done

	if [ "${deletegit}" = "true" ]; then
		rm -rf u-boot/u-boot/ u-boot/.git*
	fi

	exit 0
elif [ $# -eq 1 -a "$1" == "--help" ] ; then
	usage
	exit 0
elif [ $# -eq 1 -a "$1" == "--list-boards" ] ; then
	list_supported_boards
	exit 0
elif [ $# -eq 1 ] ; then
	for board in $(list_supported_boards) ; do
		if [ "$board" = "$1" ] ; then
			download_uboot_board "$1"
			exit 0
		fi
	done

	if [ "${deletegit}" = "true" ]; then
		rm -rf u-boot/u-boot/ u-boot/.git*
	fi

	printf "Error: Board '${1}' is not supported\n"

	exit 1
fi

exit 0