#!/bin/sh

# apk add \
#	abuild apk-tools alpine-conf busybox fakeroot syslinux xorriso cmd:mksquashfs
#	(for efi:) mtools grub
#	(for s390x:) s390-tools
#	(for ppc64le:) grub

# FIXME: clean workdir out of unneeded sections
# FIXME: --release: cp/mv images to REPODIR/$ARCH/releases/
# FIXME: --update-latest: rewrite latest-releases.yaml with this build

set -e

# get abuild configurables
. "${ABUILD_SHAREDIR:-/usr/share/abuild}"/functions.sh

scriptdir="$(dirname "$0")"
git=$(command -v git) || git=true

set_source_date() {
	# dont error out if we're not in git
	if ! $git rev-parse --show-toplevel >/dev/null 2>&1; then
		git=true
	fi
	# set time stamp for reproducible builds
	if [ -z "$SOURCE_DATE_EPOCH" ] && [ $($git -C "$scriptdir" status -s | wc -l) -ne 0 ]; then
		SOURCE_DATE_EPOCH=$($git -C "$scriptdir" log -1 --format=%cd --date=unix)
	fi
	if [ -z "$SOURCE_DATE_EPOCH" ]; then
		SOURCE_DATE_EPOCH=$(date -u "+%s")
	fi
	export SOURCE_DATE_EPOCH
}

set_source_date

# 
all_sections=""
all_profiles=""
all_checksums="sha256 sha512"
all_dirs=""
build_date="$(date -u +%y%m%d -d "@$SOURCE_DATE_EPOCH")"
default_arch="$(apk --print-arch)"
_hostkeys=""
_simulate=""
_checksum=""

OUTDIR="$PWD"
RELEASE="${build_date}"


msg() {
	if [ -n "$quiet" ]; then return 0; fi
	local prompt="$GREEN>>>${NORMAL}"
	local name="${BLUE}mkimage${ARCH+-$ARCH}${NORMAL}"
	printf "${prompt} ${name}: %s\n" "$1" >&2
}

list_has() {
	local needle="$1"
	local i
	shift
	for i in $@; do
		[ "$needle" != "$i" ] || return 0
	done
	return 1
}

usage() {
	cat <<EOF

$0	[--tag RELEASE] [--outdir OUTDIR] [--workdir WORKDIR]
		[--arch ARCH] [--profile PROFILE] [--hostkeys] [--simulate]
		[--repository REPO [--repository REPO]]
		[--repositories-file REPO_FILE] [--yaml FILE]
$0	--help

options:
--arch			Specify which architecture images to build
			(default: $default_arch)
--hostkeys		Copy system apk signing keys to created images
--outdir		Specify directory for the created images
--profile		Specify which profiles to build
--repositories-file	List of repositories to use for the image create
--repository		Package repository to use for the image create
--simulate		Don't execute commands
--tag			Build images for tag RELEASE
--workdir		Specify temporary working directory (cache)
--yaml

known profiles: $(echo $all_profiles | sort -u)

EOF
}

# helpers
load_plugins() {
	local f
	[ -e "$1" ] || return 0
	for f in "$1"/mkimg.*.sh; do
		[ -e "$f" ] || return 0
		break
	done
	all_profiles="$all_profiles $(sed -n -e 's/^profile_\(.*\)() {$/\1/p' $1/mkimg.*.sh)"
	all_sections="$all_sections $(sed -n -e 's/^section_\(.*\)() {$/\1/p' $1/mkimg.*.sh)"
	for f in "$1"/mkimg.*.sh; do
		. $f
	done
}

checksum() {
	sha1sum | cut -f 1 -d ' '
}

build_section() {
	local section="$1"
	local args="$@"
	local _dir="${args//[^a-zA-Z0-9]/_}"
	shift
	local args="$@"

	if [ -z "$_dir" ]; then
		return 1
	fi

	if [ ! -e "$WORKDIR/${_dir}" ]; then
		DESTDIR="$WORKDIR/${_dir}.work"
		msg "--> $section $args"
		if [ -z "$_simulate" ]; then
			rm -rf "$DESTDIR"
			mkdir -p "$DESTDIR"
			build_${section} "$@"
			mv "$DESTDIR" "$WORKDIR/${_dir}"
			_dirty="yes"
		fi
	fi
	unset DESTDIR
	all_dirs="$all_dirs $_dir"
	_my_sections="$_my_sections $_dir"
}

build_profile() {
	local _id _dir _spec
	_my_sections=""
	_dirty="no"

	profile_$PROFILE
	list_has $ARCH $arch || return 0

	msg "Building $PROFILE"

	# Collect list of needed sections, and make sure they are built
	for SECTION in $all_sections; do
		section_$SECTION
	done

	# Defaults
	[ -n "$image_name" ] || image_name="alpine-${PROFILE}"
	[ -n "$output_filename" ] || output_filename="${image_name}-${RELEASE}-${ARCH}.${image_ext}"
	local output_file="${OUTDIR:-.}/$output_filename"

	# Construct final image
	local _imgid=$(echo -n $_my_sections | sort | checksum)
	DESTDIR=$WORKDIR/image-$_imgid-$ARCH-$PROFILE
	if [ "$_dirty" = "yes" -o ! -e "$DESTDIR" ]; then
		msg "Creating $output_filename"
		if [ -z "$_simulate" ]; then
			# Merge sections
			rm -rf "$DESTDIR"
			mkdir -p "$DESTDIR"
			for _dir in $_my_sections; do
				for _fn in $WORKDIR/$_dir/*; do
					[ ! -e "$_fn" ] || cp -Lrs $_fn $DESTDIR/
				done
			done
			echo "${image_name}-${RELEASE} ${build_date}" > "$DESTDIR"/.alpine-release
		fi
	fi

	if [ "$_dirty" = "yes" -o ! -e "$output_file" ]; then
		# Create image
		[ -n "$output_format" ] || output_format="${image_ext//[:\.]/}"
		create_image_${output_format}

		if [ "$_checksum" = "yes" ]; then
			for _c in $all_checksums; do
				echo "$(${_c}sum "$output_file" | cut -d' '  -f1)  ${output_filename}" > "${output_file}.${_c}"
			done
		fi
	fi
	if [ -n "$_yaml_out" ]; then
		$mkimage_yaml --release $RELEASE \
			--title "$title" \
			--desc "$desc" \
			"$output_file" >> "$_yaml_out"
	fi
}

# load plugins
load_plugins "$scriptdir"
if [ -n "$HOME" ]; then
	load_plugins "$HOME/.mkimage"
fi

mkimage_yaml="$(dirname $0)"/mkimage-yaml.sh

# parse parameters
while [ $# -gt 0 ]; do
	opt="$1"
	shift
	case "$opt" in
	--repositories-file) REPOS_FILE="$1"; shift ;;
	--repository)
		if [ -z "$REPOS" ]; then
			REPOS="$1"
		else
			REPOS=$(printf '%s\n%s' "$REPOS" "$1");
		fi
		shift ;;
	--extra-repository)
		warning "--extra-repository is deprecated. Use multiple --repository"
		EXTRAREPOS="$EXTRAREPOS $1"
		shift ;;
	--workdir) WORKDIR="$1"; shift ;;
	--outdir) OUTDIR="$1"; shift ;;
	--tag) RELEASE="$1"; shift ;;
	--arch) req_arch="$1"; shift ;;
	--profile) req_profiles="$1"; shift ;;
	--hostkeys) _hostkeys="--hostkeys";;
	--simulate) _simulate="yes";;
	--checksum) _checksum="yes";;
	--yaml) _yaml="yes";;
	--help) usage; exit 0;;
	--) break ;;
	-*) usage; exit 1;;
	esac
done

if [ -z "$RELEASE" ]; then
	if git describe --exact-match >/dev/null 2>&1; then
		RELEASE=$(git describe --always)
		RELEASE=${RELEASE#v}
	else
		RELEASE="${build_date}"
	fi
fi

if [ -z "$REPOS" ]; then
  echo "Must provide --repository"
  exit 2
fi

# setup defaults
if [ -z "$WORKDIR" ]; then
	WORKDIR="$(mktemp -d -t mkimage.XXXXXX)"
	trap 'rm -rf $WORKDIR' INT EXIT
fi
req_profiles=${req_profiles:-${all_profiles}}
req_arch=${req_arch:-${default_arch}}
[ "$req_arch" != "all" ] || req_arch="${all_arch}"
[ "$req_profiles" != "all" ] || req_profiles="${all_profiles}"

mkdir -p "$OUTDIR"

# get abuild pubkey used to sign the apkindex
# we need inject this to the initramfs or we will not be able to use the
# boot repository
_pub=${PACKAGER_PRIVKEY:+${PACKAGER_PRIVKEY}.pub}
_abuild_pubkey="${PACKAGER_PUBKEY:-$_pub}"

# create images
for ARCH in $req_arch; do
	APKROOT="$WORKDIR/apkroot-$ARCH"
	if [ ! -e "$APKROOT" ]; then
		# create root for caching packages
		mkdir -p "$APKROOT/etc/apk/cache" "$APKROOT"/etc/apk/keys
		if [ -d /usr/share/apk/keys/"$ARCH" ]; then
			cp /usr/share/apk/keys/"$ARCH"/* "$APKROOT"/etc/apk/keys
		fi
		if [ -n "$_hostkeys" ]; then
			cp /etc/apk/keys/* "$APKROOT"/etc/apk/keys
		fi
		if [ -n "$_abuild_pubkey" ]; then
			cp "$_abuild_pubkey" "$APKROOT"/etc/apk/keys
		fi
		apk --arch "$ARCH" --root "$APKROOT" add --initdb --no-chown

		_repositories="$APKROOT/etc/apk/repositories"
		if [ -n "$REPOS_FILE" ]; then
			cat "$REPOS_FILE" > "$_repositories"
		fi
		echo "$REPOS" >> "$_repositories"
		for repo in $EXTRAREPOS; do
			echo "$repo" >> "$_repositories"
		done
	fi
	apk update --root "$APKROOT"

	if [ "$_yaml" = "yes" ]; then
		_yaml_out=${OUTDIR:-.}/latest-releases.yaml
		echo "---" > "$_yaml_out"
	fi
	for PROFILE in $req_profiles; do
		(set -eo pipefail; build_profile)
	done
done
echo "Images generated in $OUTDIR"
