#!/bin/sh -efu

. girar-sh-functions

[ -n "${GIRAR_USER-}" ] ||
	fatal 'GIRAR_USER undefined'

usage()
{
	[ -z "$*" ] || echo >&2 "$PROG: $*"
cat >&2 <<EOF
Usage: $PROG <gear_repo> <tag_file> <template_branch> <module_branch> <kernel_flavour> <repo>
       $PROG <gear_repo> <tag_file> <template_branch> <module_branch> <kernel_flavour> -p <pocket>
EOF
	exit 1
}

if [ "${1-}" = '--help' ]; then
	usage
fi

[ "$#" -le 7 ] || usage 'Too many arguments.'
[ "$#" -ge 6 ] || usage 'Not enough arguments.'

gear="$1"; shift
tag_file="$1"; shift
template_branch="$1"; shift
module_branch="$1"; shift
kernel_flavour="$1"; shift

# detect our arch
SYSARCH=$(uname -m)
[ "$SYSARCH" = "x86_64" ] || SYSARCH=i586
GET_VERSION_IN_REPO="girar-get-package-version-in-repo"
REPO_SUFFIX_COMMAND="girar-get-repo-suffix"
if [ "$#" = 2 ]; then
	if [ "$1" = "-p" ]; then
		GET_VERSION_IN_REPO="girar-get-package-version-in-pocket"
		REPO_SUFFIX_COMMAND="girar-get-repo-suffix -p"
		shift
	else
		usage "Invalid option."
	fi
fi

repo="$1"; shift
suffix="$($REPO_SUFFIX_COMMAND $repo)"

packager_name="${GIRAR_PACKAGER_NAME-Girar Packager}"
packager_email="${GIRAR_PACKAGER_EMAIL-girar@etersoft.ru}"

realpath()
{
	readlink -f "$@"
}

# quiet_if_ok <command> <args>...
#
# Hides stdout and stderr output of the command if it returns 0, otherwise
# prints all stored output to stderr.
#
quiet_if_ok()
{
	local output rc=0

	output="$( "$@" 2>&1 )" || rc=$?
	[ $rc = 0 ] || printf '%s\n' "$output" 1>&2
	return $rc
}

branch_exists()
{
	git rev-parse --verify "refs/heads/$1" >/dev/null 2>&1
}

# make_git_workdir <new_workdir>
#
# Create a new git working directory which shares most of .git/ internal files
# with an existing git repository.  In particular, .git/refs/ is shared,
# therefore commits made in $new_workdir propagate to $git_dir.
#
make_git_workdir()
{
	local new_workdir="$1" && shift

	local git_dir x
	git_dir="$(git rev-parse --git-dir)" ||
		fatal "Failed to find a valid git directory"
	git_dir="$(readlink -ev "$git_dir")" ||
		fatal "Invalid git directory \"$git_dir\""
	mkdir -p "$new_workdir/.git/logs"
	if [ -d "$git_dir/logs/refs" ]; then
		ln -s "$git_dir/logs/refs" "$new_workdir/.git/logs/refs"
	else
		mkdir "$new_workdir/.git/logs/refs"
	fi
	for x in refs objects info hooks packed-refs remotes rr-cache; do
		ln -s "$git_dir/$x" "$new_workdir/.git/$x"
	done
	for x in config HEAD; do
		cp "$git_dir/$x" "$new_workdir/.git/$x"
	done
}

kernel_version_code()
{
	local release="$1" && shift

	local kver="$(printf %s "$release" | cut -d- -f1)"
	local version="$(printf %s "$kver" | cut -d. -f1)"
	local patchlevel="$(printf %s "$kver" | cut -d. -f2)"
	local sublevel="$(printf %s "$kver" | cut -d. -f3)"

	# from kernel Makefile
	expr "$version" \* 65536 + "$patchlevel" \* 256 + "$sublevel"
}

# subst_module_spec <spec>
#
# Substitute module spec template keywords (@kversion@, @krelease@, ...) with
# values specified in global variables $KERNEL_VERSION, $KERNEL_RELEASE, ...
#
subst_module_spec()
{
	local spec="$1" && shift
	local kernel_code=$(kernel_version_code $kernel_version)
	local kernel_buildrelease="$(echo $kernel_release | sed -e 's/^alt//' -e 's/^eter//')"

	subst "s,@kversion@,$kernel_version," "$spec"
	subst "s,@krelease@,$kernel_release," "$spec"
	subst "s,@kflavour@,$kernel_flavour," "$spec"

	local arch
	[ "$kernel_flavour" = "std-pae" ] &&
		arch="%ix86" ||
		arch="%ix86 x86_64"
	subst "s,@kernelarch@,$arch," "$spec"

	if fgrep -qs "@kreleasebuild@" "$spec"; then
		subst "s,@kreleasebuild@,$kernel_code.$kernel_buildrelease," \
			"$spec"
	else
		subst "s,^release:.*$,&.$kernel_code.$kernel_buildrelease,1I" \
			"$spec"
		add_changelog --nocheck -s girar-stamp-spec -a "$packager_name <$packager_email>" \
			-e "- Build for kernel-image-$kernel_flavour-$kernel_version-$kernel_release." \
			"$spec"
	fi
}

# update_module_branch <branch> <template>
#
# Create a new commit in <branch> based on the previous commit in that branch
# and the template in <template>.  The new commit may be a merge commit if
# <template> has been updated after the previous update of <branch>, or a
# simple commit if <template> was not updated.
#
# If <branch> did not exist yet, it will be created based on <template>.
#
update_module_branch()
{
	local branch="$module_branch"
	local template="$template_branch"

	local old_desc new_desc old_head= new_head

	make_git_workdir "$workdir/module_tree"
	cd "$workdir/module_tree"
	unset GIT_DIR

	git config user.name "$packager_name"
	git config user.email "$packager_email"
	git config core.bare false

	if branch_exists "$branch"; then
		git checkout -f -q "$branch^{commit}}"
		old_head="$(git rev-parse --verify HEAD)"
	else
		git checkout -f -q "$template^{commit}"
	fi

	old_desc="$(gear --describe)"
	quiet_if_ok git merge --no-summary --no-commit -s ours "$template"
	git read-tree --reset -u "$template"

	( set +f; subst_module_spec *.spec ) || return
	if [ -n "$(git diff --raw HEAD)" ]; then
		GIT_EDITOR=: gear-commit -a -q
		new_desc="$(gear --describe)"
		[ "$old_desc" != "$new_desc" ] ||
			fatal "Package \"$old_desc\" already committed"
	else
		new_desc="$old_desc"
	fi
	GNUPGHOME=/usr/lib/girar-gpgkeys gear-create-tag -v \
		-n "@name@-@version@-@release@" -u "$packager_email" >/dev/null 2>&1 ||
			fatal "failed to create tag named \"$(echo $new_desc | tr ' ' '-')\", it maybe already existed"
	gear --describe | tr ' ' '-' >"$tag_file"

	new_head="$(git rev-parse --verify HEAD)"
	[ -n "$old_head" ] ||
		git branch "$branch" "$template"
	git update-ref -m "commit package: $new_desc" \
		"refs/heads/$branch" "$new_head" $old_head
}


workdir=
atexit()
{
	local rc=$?
	trap - EXIT
	[ -z "$workdir" ] || rm -rf -- "$workdir"
	exit $rc
}

trap '' HUP INT QUIT PIPE TERM
trap atexit EXIT

workdir="$(mktemp -dt "$PROG.XXXXXXXXXX")"
tag_file=$(realpath "$tag_file")

kernel_relver=$($GET_VERSION_IN_REPO kernel-image-$kernel_flavour $repo)
[ -n "$kernel_relver" ] ||
	fatal "Package \"kernel-image-$kernel_flavour\" not found in $repo"
kernel_version=${kernel_relver%%-*}
kernel_release=${kernel_relver##*-}

update_module_branch
