#!/bin/bash
#
# Portions taken from the circle-stdlib project by Stephan Muehlstrasser

set -e

function usage()
{
	echo "Usage: $0 [ <option> ... ]"
	echo "Writes a build configuration for Circle to the file ./Config.mk."
	echo
	echo "Options:"
	echo "  -r <number>, --raspberrypi <number>"
	echo "                     Raspberry Pi model number (1, 2, 3, 4, 5, default: 1)"
	echo "  -p <string>, --prefix <string> "
	echo "                     Prefix of the toolchain commands"
	echo "                     (default: arm-none-eabi-, aarch64-none-elf- with -r 5)"
	echo "  --multicore        Allow multi-core applications"
	echo "  --realtime         Enable real time mode to improve IRQ latency"
	echo "  --keymap <country> Set default USB keymap (DE, ES, FR, IT, UK, US)"
	echo "  --qemu             Build for running under QEMU"
	echo "  --kernel-max-size <megabytes>"
	echo "                     Set maximum size of the kernel image (default: 2)"
	echo "  -d <option>, --define <option> "
	echo "                     Define additional system option"
	echo "  --c++14            Use C++14 standard for compiling (default C++17)"
	echo "  --c++20            Use C++20 standard for compiling (default C++17)"
	echo "  --clang            Build with Clang / LLVM"
	echo "  --kasan            Enable Kasan address sanitizer"
	echo "  -f, --force        Overwrite existing Config.mk file"
	echo "  -h, --help         Show this usage message"
	echo
	echo "Note: The architecture is determined from the toolchain."
}

# From the bash FAQ: How to use pathnames relative to the script
cd "${BASH_SOURCE%/*}" || exit 1
TOPDIR="$PWD"

RASPPI=1
PREFIX=arm-none-eabi-
MULTICORE=0
REALTIME=0
KEYMAP=
QEMU=0
KERNEL_MAX_SIZE_MB=2
CPP=17
CLANG=0
KASAN=0
DEFINE=
FORCE=0
SHIFT=0

while [ ${#} -gt 0 ]; do
	case "$1" in
		-r|--raspberrypi) RASPPI="$2" ; SHIFT=2;;
		-p|--prefix) PREFIX="$2" ; SHIFT=2;;
		--multicore) MULTICORE=1 ; SHIFT=1;;
		--realtime) REALTIME=1 ; SHIFT=1;;
		--keymap) KEYMAP="$2" ; SHIFT=2;;
		--qemu) QEMU=1 ; SHIFT=1;;
		--kernel-max-size) KERNEL_MAX_SIZE_MB="$2"; SHIFT=2;;
		--c++14) CPP=14 ; SHIFT=1;;
		--c++17) CPP=17 ; SHIFT=1;;
		--c++20) CPP=20 ; SHIFT=1;;
		--clang) CLANG=1 ; SHIFT=1;;
		--kasan) KASAN=1 ; SHIFT=1;;
		-d|--define) DEFINE="$DEFINE -D$2" ; SHIFT=2;;
		-f|--force) FORCE=1 ; SHIFT=1;;
		-h|--help) usage ; exit 0;;
		*) echo "Unknown argument: '$1'" >&2; usage >&2; exit 1;;
	esac
	if [ "${#}" -lt "${SHIFT}" ]; then
		echo "'$1' requires an argument" >&2
		usage >&2
		exit 1
	fi
	shift "${SHIFT}"
done

if [ $RASPPI -eq 5 ]
then
	if [ "$PREFIX" == "arm-none-eabi-" ]
	then
		PREFIX=aarch64-none-elf-
	fi
fi

: ${CC:="$PREFIX"gcc}

if [ ! -x "`which $CC 2> /dev/null`" ]
then
	echo "Error: Invalid toolchain prefix: ${PREFIX}" >&2
	exit 1
fi

# Try to determine 64-bit build from Target: output of gcc
AARCH=32
if [ "$($CC -v 2>&1 | grep '^Target:.*64')" != "" ]
then
	AARCH=64
	if [ $RASPPI -lt 3 ]
	then
		echo "Error: AArch64 requires Raspberry Pi 3 or 4!" >&2
		exit 1
	fi
fi

case "$RASPPI" in
	1|2|3|4|5) ;;
	*) echo "Error: Unknown Raspberry Pi target: ${RASPPI}" >&2; exit 1;;
esac

KERNEL_MAX_SIZE=$(printf "0x%X" $(( KERNEL_MAX_SIZE_MB * 0x100000 )))
KASAN_SHADOW_MAPPING_OFFSET=$(printf "0x%X" $(( KERNEL_MAX_SIZE + 0x700000 )))

CONFIG_FILE="$TOPDIR/Config.mk"
if [ -e "$CONFIG_FILE" ]
then
	if [ $FORCE -eq 0 ]
	then
		echo "Error: ${CONFIG_FILE} already exists!" >&2
		echo "You may use the option -f to overwrite it." >&2
		exit 1
	fi
fi

(
	if [ $CLANG -eq 1 ]
	then
		echo "CLANG = 1"
	fi
	if [ $AARCH -eq 32 ]
	then
		echo "PREFIX = ${PREFIX}"
	else
		echo "PREFIX64 = ${PREFIX}"
	fi
	echo "AARCH = $AARCH"
	echo "RASPPI = $RASPPI"
	if [ $MULTICORE -eq 1 ]
	then
		echo "DEFINE += -DARM_ALLOW_MULTI_CORE"
	fi
	if [ $REALTIME -eq 1 ]
	then
		echo "DEFINE += -DREALTIME"
	fi
	if [ "$KEYMAP" != "" ]
	then
		echo "DEFINE += -DDEFAULT_KEYMAP=\\\"${KEYMAP}\\\""
	fi
	if [ $QEMU -eq 1 ]
	then
		echo "DEFINE += -DNO_SDHOST"
		echo "DEFINE += -DNO_SCREEN_DMA_BURST_LENGTH"
		if [ $AARCH -eq 32 ]
		then
			echo "DEFINE += -DNO_PHYSICAL_COUNTER"
		fi
	fi
	if [ $KERNEL_MAX_SIZE_MB -ne 2 ]
	then
		DEFINE="$DEFINE -DKERNEL_MAX_SIZE=$KERNEL_MAX_SIZE"
	fi
	if [ "$DEFINE" != "" ]
	then
		echo "DEFINE +=$DEFINE"
	fi
	if [ $CPP -eq 14 ]
	then
		echo "STANDARD = -std=c++14 -Wno-aligned-new"
	elif [ $CPP -eq 20 ]
	then
		if [ $CLANG -eq 1 ]
		then
			echo "STANDARD = -std=c++20 -Wno-deprecated-volatile"
		else
			echo "STANDARD = -std=c++20 -Wno-volatile"
		fi
	fi
	if [ $KASAN -eq 1 ]
	then
		echo "KASAN_ENABLED = 1"
		if [ $KERNEL_MAX_SIZE_MB -ne 2 ]
		then
			echo "KASAN_SHADOW_MAPPING_OFFSET = $KASAN_SHADOW_MAPPING_OFFSET"
		fi
	fi
) > "$CONFIG_FILE"

echo "Configuration file successfully created:"
echo
cat "$CONFIG_FILE"
echo
echo "Now enter ./makeall to build the Circle libraries!"
