#!/bin/bash

set -e # exit on any error immediately!

readonly GOAL="$1"

readonly headref="$(git symbolic-ref HEAD)"
readonly BRANCH="${headref##refs/heads/}"

# There are two supposed modes of your workflow:
# 
# * your tweaks in "$BRANCH"_/OUT/"$GOAL" are automatically merged with the new generated
#   output and--after a successful automatic check with `make check-"$GOAL"`--automatically
#   committed. This is good if your goal is to prepare a nice output, but the tools are
#   not perfect; manual intervention is needed only when new tweaks are needed.
# 
# * each new thing in the output must be manually accepted by you. To use this workflow, 
#   simply make `make check-"$GOAL"` fail. Then you are left in "$BRANCH"_/OUT/"$GOAL"
#   with an uncommitted merge. There are two essential submodes of this workflow then:
#   - "rejecting": you use `git reset --patch` to reject bad results, and commit then;
#   - "accepting": `git reset`  to reset the index, and then you use `git add --patch`
#     to accept good results, and then commit.
#   Of course, you can even put these commands in your rules for `make check-"$GOAL"`
#   to start this process automatically whenever new output is produced.

# First, we're going to check that the AUTO branch is "valid", i.e., 
# that we haven't lost any extra history in our BRANCH as compared to AUTO.
function can_be_valid_auto() {
    if is_ancestor "$1"; then
	return 0
    fi
    local -a their_parents=($(git rev-parse "$1"^@))
    if (( ${#their_parents[@]} == 1 )); then
	if is_ancestor "${their_parents[0]}"; then
	    return 0 # ok
	else
	    return 1 # bad
	fi
    elif (( ${#their_parents[@]} == 2 )); then
	if is_ancestor "${their_parents[0]}" && can_be_valid_auto "${their_parents[1]}" || 
	    { is_ancestor "${their_parents[1]}" && can_be_valid_auto "${their_parents[0]}"; }; 
	then
	    return 0 # ok
	else
	    return 1 # bad
	fi
    else
	return 1
    fi
}

function is_ancestor() {
    if [[ "$(git rev-list "$BRANCH".."$1")" ]]; then
	return 1 # bad
    else
	return 0 # ok
    fi
}

# Borrowed from gear --commit:
nothing_to_commit() {
    [ "$(git write-tree)" = "$(git rev-parse 'HEAD^{tree}')" ] \
	&& echo $"Nothing to commit." >&2
}

make_clean() {
    make clean-"$GOAL"
}

make_list_wanted() {
    make wanted-"$GOAL"
}

set -x

if ! { can_be_valid_auto "$BRANCH"_/AUTO/"$GOAL" \
    && git checkout "$BRANCH"_/AUTO/"$GOAL"; };
then
    printf $"Refusing to proceed; %s has extra history you'll loose:" "$BRANCH"_/AUTO/"$GOAL" >&2
    echo >&2
    git checkout "$BRANCH"
    exit 1
elif make_clean \
    && git merge --no-commit -s recursive -X theirs "$BRANCH" \
    && make "$GOAL" \
    && make_list_wanted | xargs git add \
    && git add -u \
    && { nothing_to_commit || git commit -m "(AUTO) $GOAL"$'\n\nDone with the help of gitmk-make.'; }
then
    if git checkout "$BRANCH"_/OUT/"$GOAL" \
	&& git merge --no-commit "$BRANCH"_/AUTO/"$GOAL" \
	&& make check-"$GOAL";
    then
	if { nothing_to_commit || git commit -m "Updated (merged $BRANCH""_/AUTO/$GOAL) and checked."; } \
	    && git checkout "$BRANCH";
	then
	    set +x
	    printf $"You can enjoy now any left-over output from make check-%s" "$GOAL"
	    echo
	else
	    readonly status=$?
	    set +x
	    printf $"Oops, something went wrong at the final stage."
	    echo
	    printf $"We expected to commit now, and checkout your sources branch: %s" "$BRANCH"
	    echo
	    exit $status
	fi
    else
	readonly status=$?
	set +x
	printf $"Now, check the output manually (and resolve conflicts, as needed), then commit."
	echo
	printf $"(You could also write a rule for make check-%s for this to be done automatically.)" "$GOAL"
	echo
	exit $status
    fi
else
    readonly status=$?
    printf $"make %s and commit failed; now, fix the sources there:" "$GOAL"
    echo
    git checkout "$BRANCH"
    exit $status
fi
