#! /usr/bin/env bash

# Set timezone to default
TZ=UTC
export TZ

targetmode='install'
if [ "$1" == '--cpio' ]; then
	targetmode='archive'

	shift
fi

pkg="$(echo "$1" | sed 's@/*$@@;s@^\.*/*@@')"

if [ -z "${pkg}" ]; then
	echo "Usage: build [--cpio] <package>" 2>&1

	exit 1
fi

function download() {
	if [ ! -e "${pkgarchive}" ]; then
		# Download
		## Cleanup
		rm -f src.new

		## Fetch file
		wget -O src.new "${url}" || exit 1

		## Verify signature
		src_sha256="$(openssl sha256 'src.new' | sed 's@^.*= @@')"
		if [ "${src_sha256}" != "${sha256}" ]; then
			echo "SHA256 mismatch:  Downloaded: ${src_sha256} != Expected: ${sha256}" >&2

			exit 1
		fi

		## Move file into place
		mv src.new "${pkgarchive}"
	fi
}

function extract() {
	local decompress

	# Decompress archive
	## Determine type of archive
	case "${url}" in
		*.tar.xz|*.tar.xz'?'*|*.txz)
			decompress='xz'
			;;
		*.tar.gz|*.tar.gz'?'*|*.tgz)
			decompress='gzip'
			;;
		*.tar.bz2|*.tar.bz2'?'*|*.tbz2)
			decompress='bzip2'
			;;
		*.zip|*.zip'?'*)
			decompress='unzip'
			;;
		*)
			echo "Unknown compression method: ${url}" >&2

			exit 1
			;;
	esac

	## Do decompression
	case "${decompress}" in
		unzip)
			unzip "${pkgarchive}" || die 'Unable to uncompress archive'
			;;
		*)
			"${decompress}" -dc "${pkgarchive}" | tar -xf - || die 'Unable to uncompress archive'
			;;
	esac
}

function verifyRequiredPackages() {
	local pkg pkgdomain pkgversion
	local pkgdir pkgconfigdir pkgfound

	for pkg in "${require[@]}"; do
		pkgdomain=''
		pkgversion=''
		pkgchanges=(CFLAGS LDFLAGS PATH PKG_CONFIG_PATH)

		case "${pkg}" in
			*:*)
				pkgchanges=($(echo "${pkg}" | cut -f 2 -d ':'))
				pkg="$(echo "${pkg}" | cut -f 1 -d ':')"
				;;
		esac

		case "${pkg}" in
			*/*@*)
				pkgdomain="$(echo "${pkg}" | cut -f 2 -d '@')"
				pkgversion="$(echo "${pkg}" | cut -f 2 -d '/' | cut -f 1 -d '@')"
				pkg="$(echo "${pkg}" | cut -f 1 -d '/')"
				;;
			*/*)
				pkgversion="$(echo "${pkg}" | cut -f 2 -d '/')"
				pkg="$(echo "${pkg}" | cut -f 1 -d '/')"
				;;
			*@*)
				pkgdomain="$(echo "${pkg}" | cut -f 2 -d '@')"
				pkg="$(echo "${pkg}" | cut -f 1 -d '@')"
				;;
		esac

		if [ -z "${pkgdomain}" ]; then
			pkgdomain="${domain}"
		fi

		pkgfound='0'
		for pkgdir in "/opt/appfs/${pkgdomain}/${pkg}/platform"/${pkgversion:-latest} "/opt/appfs/${pkgdomain}/${pkg}/platform"/${pkgversion:-*}; do
			pkgconfigdir="${pkgdir}/lib/pkgconfig"

			if [ ! -d "${pkgdir}" ]; then
				continue
			fi

			# If the package version was unspecified, fully resolve
			# the directory that we found
			if [ -z "${pkgversion}" ]; then
				pkgdir="$(readlink -f "${pkgdir}")"
			fi

			pkgfound='1'

			for pkgchange in "${pkgchanges[@]}"; do
				case "${pkgchange}" in
					CFLAGS)
						CFLAGS="${CFLAGS} -I${pkgdir}/include"
						CPPFLAGS="${CPPFLAGS} -I${pkgdir}/include"
						export CFLAGS CPPFLAGS
						;;
					LDFLAGS)
						LDFLAGS="${LDFLAGS} -L${pkgdir}/lib -Wl,-rpath,${pkgdir}/lib"
						export LDFLAGS
						;;
					PATH)
						PATH="${PATH}:${pkgdir}/bin"
						export PATH
						;;
					PKG_CONFIG_PATH)
						if [ -d "${pkgconfigdir}" ]; then
							PKG_CONFIG_PATH="${PKG_CONFIG_PATH}:${pkgconfigdir}"
							export PKG_CONFIG_PATH
						fi
						;;
				esac
			done
		done

		if [ "${pkgfound}" = '0' ]; then
			die "Package ${pkg}/${pkgversion:-*}@${pkgdomain} not found."
		fi
	done
}

function verifyPrerequisites() {
	:
}

function determineOsArch() {
	os="$(uname -s | dd conv=lcase 2>/dev/null)"
	arch="$(uname -m | dd conv=lcase 2>/dev/null)"

	case "${arch}" in
		i?86)
			arch='ix86'
			;;
	esac
}

function determinePrefix() {
	determineOsArch

	prefixsuffix="${pkg}/${os}-${arch}/${version}"
	prefix="/opt/appfs/${domain}/${prefixsuffix}"
	destdir="$(pwd)/INST"

	mkdir "${destdir}" || die
}

function preconfigure() {
	:
}

function postconfigure() {
	:
}

function configure_gcc() {
	local glibcdir linuxheadersdir
	local dynlinker
	local gcc_default_headers

	glibcdir="/opt/appfs/core.appfs.rkeene.org/glibc/platform/latest"
	glibcdir="$(readlink -f "${glibcdir}")"

	linuxheadersdir="/opt/appfs/core.appfs.rkeene.org/linux-headers/platform/latest"
	linuxheadersdir="$(readlink -f "${linuxheadersdir}")"

	dynlinker="$(ls "${glibcdir}"/lib/ld-linux*.so.* | tail -n 1)"

	if [ ! -f "${dynlinker}" ]; then
		die 'glibc directory is not available (appfs running/working?)'
	fi

	gcc_default_headers="$(echo '' | ${CPP:-cpp} -v 2>&1 | sed '/^End of search list/,$ d;0,/search starts here:$/ d' | grep '/gcc/' | sed 's@^ *@-I@' | tr $'\n' ' ')"

	CC="${CC:-gcc} -nostdinc ${gcc_default_headers} -I${glibcdir}/include"

	if ! echo " ${require[*]} " | grep ' linux-headers[/@: ]' >/dev/null; then
		CC="${CC} -I${linuxheadersdir}/include"
	fi

	BUILD_CC="${CC}"
	HOST_CC="${CC}"
	LDFLAGS="${LDFLAGS} -L${glibcdir}/lib -Wl,--rpath,${glibcdir}/lib -Wl,--dynamic-linker,${dynlinker}"
	PKG_CONFIG_LIBDIR="${glibcdir}/lib/pkgconfig"
	export CC BUILD_CC HOST_CC LDFLAGS PKG_CONFIG_LIBDIR
}

function configure() {
	configure_gcc
	./configure --prefix="${prefix}" --sysconfdir="${prefix}/etc" --libdir="${prefix}/lib" --localstatedir=/var "${configure_extra[@]}"
}

function prebuild() {
	:
}

function postbuild() {
	:
}

function build() {
	grep "DESTDIR" Makefile >/dev/null || die "Don't know how to build this software"

	make
}

function preinstall() {
	:
}

function postinstall() {
	:
}

function install() {
	make install DESTDIR="${destdir}"
}

function cleanup() {
	cd "${workdir}" || exit 1
	cd .. || exit 1
	rm -rf "${workdir}"
}

function die() {
	local message

	message="$1"

	if [ -n "${message}" ]; then
		echo "error: ${message}" >&2
	fi

	exit 1
}

scriptdir="$(cd "$(dirname "$(which "$0")")" && pwd)"
if [ -z "${scriptdir}" ]; then
	echo "Unable to locate script directory" >&2

	exit 1
fi

cd "${scriptdir}" || exit 1

if [ -f 'build.conf' ]; then
	. 'build.conf'
fi

if [ -d "pkgs/${pkg}" ]; then
	pkgdir="pkgs/${pkg}"
	pkgfile="${pkgdir}/info"
else
	pkgfile="pkgs/${pkg}"
	pkgdir="${pkgfile}"
fi

if [ ! -e "${pkgfile}" ]; then
	echo "Invalid package." >&2

	exit 1
fi

pkgdate="$(for artifact in $(find "${pkgdir}" -type f -print0 | xargs -n 1 -0 fossil finfo --limit 1 --width 0 2>/dev/null | grep '^[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] ' | sed 's@^[^ ]* \[@@;s@\].*@@' | sort -u); do fossil info "${artifact}" | awk '/^uuid:/{ print $3 $4 }' | sed 's@[-:]@@g;s@..$@\.&@'; done | sort -n | tail -n 1)"
if [ -z "${pkgdate}" ]; then
	pkgdate="$(find "${pkgdir}" -type f -printf '%TY%Tm%Td%TH%TM.%TS\n' 2>/dev/null | cut -f 1-2 -d '.' | sort -n | tail -n 1)"
fi

. "${pkgfile}"

archivedir="$(pwd)/ARCHIVE"
workdir="workdir-$$${RANDOM}${RANDOM}${RANDOM}"
pkgarchive="${archivedir}/${pkg}-${version}"
mkdir "${archivedir}" >/dev/null 2>/dev/null
mkdir "${workdir}" || exit 1
cd "${workdir}" || exit 1
workdir="$(pwd)"

# Download
download

# Extract
extract

# If we just have one directory, use that directory
dir="$(echo *)"
if [ -e "${dir}" ]; then
	mv "${dir}"/* .
fi

# Verify pre-requisites are met
verifyRequiredPackages || die 'Required packages missing'
verifyPrerequisites || die 'Prerequisities failed'

# Start logging
set -x

# Determine properties
determinePrefix

# Start the build
preconfigure || die 'preconfigure failed'
configure || die 'configure failed'
postconfigure || die 'postconfigure failed'

prebuild || die 'prebuild failed'
build || die 'build failed'
postbuild || die 'postbuild failed'

preinstall || die 'preinstall failed'
install || die 'install failed'
postinstall || die 'postinstall failed'

(
	case "${targetmode}" in
		install)
			appdir="$(appfsinstalldir)/${prefixsuffix}"
			mkdir -p "${appdir}"

			cd "${destdir}/${prefix}" || exit 1

			cp -rp * "${appdir}"
			find "${appdir}" -print0 | xargs -0 touch -t "${pkgdate}"
			find "${appdir}" -print0 | xargs -0 touch -m -t "${pkgdate}"
			find "${appdir}" -print0 | xargs -0 touch -a -t "${pkgdate}"
			;;
		archive)
			archivefile="${scriptdir}/${pkg}-${version}-$(echo "${pkgdate}" | sed 's@\.@@g')-${os}-${arch}-${domain}.cpio"
			cd "${destdir}/${prefix}/../../.." || exit 1
			find "${prefixsuffix}" -print0 | xargs -0 touch -t "${pkgdate}"
			find "${prefixsuffix}" -print0 | xargs -0 touch -m -t "${pkgdate}"
			find "${prefixsuffix}" -print0 | xargs -0 touch -a -t "${pkgdate}"
			find "${prefixsuffix}" | sort | cpio --owner 0:0 -H newc -o > "${archivefile}"
			;;
	esac
) || die 'final installation failed'

cleanup

exit 0
