#!/usr/bin/env sh
set -eu

: ${CPPFLAGS=}
: ${CXXFLAGS=}
: ${LDFLAGS=}
: ${LLVM_CONFIG=}

CXXFLAGS="$CXXFLAGS -std=c++14"
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
LDFLAGS="$LDFLAGS -pthread -lm"
OS_ARCH="$(uname -m)"
OS_NAME="$(uname -s)"

if [ -d ".git" ] && [ -n "$(command -v git)" ]; then
	GIT_SHA=$(git show --pretty='%h' --no-patch --no-notes HEAD)
	GIT_DATE=$(git show "--pretty=%cd" "--date=format:%Y-%m" --no-patch --no-notes HEAD)
	CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
else
	GIT_DATE=$(date +"%Y-%m")
fi
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"dev-$GIT_DATE\""

error() {
	printf "ERROR: %s\n" "$1"
	exit 1
}

SUPPORTED_LLVM_VERSIONS="22 21 20 19 18 17 14"

# Brew advises people not to add llvm to their $PATH, so try and use brew to find it.
if [ -z "$LLVM_CONFIG" ] &&  [ -n "$(command -v brew)" ]; then
	for V in $SUPPORTED_LLVM_VERSIONS; do
		if [ -n "$(command -v $(brew --prefix llvm@$V)/bin/llvm-config)" ]; then
			LLVM_CONFIG="$(brew --prefix llvm@$V)/bin/llvm-config"
			break
		fi
	done
fi

if [ -z "$LLVM_CONFIG" ]; then
	DEFAULT_VERSION=""

	if [ -n "$(command -v llvm-config)" ]; then
		DEFAULT_VERSION=$(llvm-config --version | awk -F. '{print $1}')
	fi

	for V in $SUPPORTED_LLVM_VERSIONS; do
		if [ "$DEFAULT_VERSION" = "$V" ]; then
			LLVM_CONFIG="llvm-config"
			break
		# darwin, linux, openbsd
		elif [ -n "$(command -v "llvm-config-$V")" ]; then
			LLVM_CONFIG="llvm-config-$V"
			break
		# freebsd
		elif [ -n "$(command -v "llvm-config$V")" ]; then
			LLVM_CONFIG="llvm-config$V"
			break
		fi
	done

	if [ -z "$LLVM_CONFIG" ]; then
		error "No supported llvm-config command found. Set LLVM_CONFIG to proceed."
	fi
fi

if [ -x "$(which clang++)" ]; then
	: ${CXX="clang++"}
elif [ -x "$($LLVM_CONFIG --bindir)/clang++" ]; then
	: ${CXX=$($LLVM_CONFIG --bindir)/clang++}
else
	error "No clang++ command found. Set CXX to proceed."
fi

LLVM_VERSION="$($LLVM_CONFIG --version)"
LLVM_VERSION_MAJOR="$(echo $LLVM_VERSION | awk -F. '{print $1}')"
LLVM_VERSION_MINOR="$(echo $LLVM_VERSION | awk -F. '{print $2}')"
LLVM_VERSION_PATCH="$(echo $LLVM_VERSION | awk -F. '{print $3}')"

if [ $LLVM_VERSION_MAJOR -lt 14 ] || ([ $LLVM_VERSION_MAJOR -gt 14 ] && [ $LLVM_VERSION_MAJOR -lt 17 ]) || [ $LLVM_VERSION_MAJOR -gt 22 ]; then
	error "Invalid LLVM version $LLVM_VERSION: must be 14, 17, 18, 19, 20, 21 or 22"
fi

case "$OS_NAME" in
Darwin)
	darwin_sysroot=
	if [ $(which xcrun) ]; then
		darwin_sysroot="--sysroot $(xcrun --sdk macosx --show-sdk-path)"
	elif [[ -e "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" ]]; then
		darwin_sysroot="--sysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk"
	else
		echo "Warning: MacOSX.sdk not found."
	fi

	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags) ${darwin_sysroot}"
	LDFLAGS="$LDFLAGS -liconv -ldl -framework System -lLLVM"
	;;
FreeBSD)
	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
	LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
	;;
NetBSD)
	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
	LDFLAGS="$LDFLAGS -lstdc++ $($LLVM_CONFIG --libs core native --system-libs)"
	;;
Linux)
	CXXFLAGS="$CXXFLAGS $($LLVM_CONFIG --cxxflags --ldflags)"
	LDFLAGS="$LDFLAGS -lstdc++ -ldl $($LLVM_CONFIG --libs core native --system-libs --libfiles)"
	# Copy libLLVM*.so into current directory for linking
	# NOTE: This is needed by the Linux release pipeline!
	# cp $(readlink -f $($LLVM_CONFIG --libfiles)) ./
	LDFLAGS="$LDFLAGS -Wl,-rpath=\$ORIGIN"
	;;
OpenBSD)
	CXXFLAGS="$CXXFLAGS -I/usr/local/include $($LLVM_CONFIG --cxxflags --ldflags)"
	LDFLAGS="$LDFLAGS -lstdc++ -L/usr/local/lib -liconv"
	LDFLAGS="$LDFLAGS $($LLVM_CONFIG --libs core native --system-libs)"
	;;
*)
	error "Platform \"$OS_NAME\" unsupported"
	;;
esac

build_odin() {
	case $1 in
	debug)
		EXTRAFLAGS="-g"
		;;
	release)
		EXTRAFLAGS="-O3"
		;;
	release-native)
		if [ "$OS_ARCH" = "arm64" ] || [ "$OS_ARCH" = "aarch64" ]; then
			# Use preferred flag for Arm (ie arm64 / aarch64 / etc)
			EXTRAFLAGS="-O3 -mcpu=native"
		else
			# Use preferred flag for x86 / amd64
			EXTRAFLAGS="-O3 -march=native"
		fi
		;;
	nightly)
		EXTRAFLAGS="-DNIGHTLY -O3"
		;;
	*)
		error "Build mode \"$1\" unsupported!"
		;;
	esac

	set -x
	$CXX src/main.cpp src/libtommath.cpp $DISABLED_WARNINGS $CPPFLAGS $CXXFLAGS $EXTRAFLAGS $LDFLAGS -o odin
	set +x
}

run_demo() {
	./odin run examples/demo -vet -strict-style -- Hellope World
}

if [ $# -eq 0 ]; then
	build_odin debug
	run_demo

	: ${PROGRAM:=$0}
	printf "\nDebug compiler built. Note: run \"$PROGRAM release\" or \"$PROGRAM release-native\" if you want a faster, release mode compiler.\n"
elif [ $# -eq 1 ]; then
	case $1 in
	report)
		if [ ! -f "./odin" ]; then
			build_odin debug
			run_demo
		fi
		./odin report
		;;
	debug)
		build_odin debug
		run_demo
		;;
	*)
		build_odin $1
		;;
	esac
else
	error "Too many arguments!"
fi
