#!/bin/bash

set -e

version="$1"

type="$2"

### CONFIGURATION OPTIONS

# Profiles to package in addition to the profile releng for the make target "dist-branches"
branches_dist_branches=(baseline gnome toolkit)

# Profiles to package in addition to the profile releng for the make target "dist"
branches_dist=(baseline)

### END OF CONFIGURATION OPTIONS

error() {
     echo "${@}. Stop." >&2
     exit 1
}

dist_branches() {
    branches="${branches_dist_branches}"
    # Refs to package (the branches)
    refs="${branches[@]}"
    # Ref of the releng profile to package
    releng="master"
    # Release branch. This branch only has commits to prepare the package.
    release_branch="release-branches"
    # Release ref. It is a branch for dist_branches. The package is created from this ref.
    release="${release_branch}"
    # Refs that should exist for the make target dist-branches. This branches will be packaged.
    allrefs=( ${releng} ${branches[@]} )
}

dist() {
    branches="${branches_dist}"
    # Refs to package (branch names appending the date, like "baseline/2013.08.12")
    refs="${branches_dist[@]/%//${version}}"
    # Ref of the releng profile to package
    releng="master/${version}"
    # Release branch. This branch only has commits to prepare the package.
    release_branch="release"
    # Release ref. Tag of commit in release_branch. The package is created from this ref.
    release="release/${version}"
    # These refs should exist to package for the make target dist.
    allrefs=( ${releng} ${refs[@]} )
}

expand() {
    ref="$1"
    dir="$2"

    GIT_INDEX_FILE="${index}" git read-tree --empty
    GIT_INDEX_FILE="${index}".release git read-tree --empty

    # Find the current head of ${ref}
    head="$(git show-ref --hash "refs/${ref_type}/${ref}")"
    # Find the current head of the release branch.
    head_release="$(git show-ref --hash refs/heads/"${release_branch}")"
    if [[ $dir == releng ]] ; then
	# Read the tree of "$ref" with profile path rename into an index file.
	git ls-tree -z -r "${head}" | sed -z "s|configs/profile|configs/${dir}|" | GIT_INDEX_FILE="${index}" git update-index -z --index-info
	# Remove the master branch from the release index, that is, leave everything from configs/ excluding configs/releng
	git ls-tree -z -r "${head_release}" | grep -zZP 'configs/(?!releng)' | GIT_INDEX_FILE="${index}".release git update-index -z --index-info
	# This combines the two indexes (obtained in the last two commands).
	GIT_INDEX_FILE="${index}".release git ls-files -z -s | GIT_INDEX_FILE="${index}" git update-index -z --add --index-info
    else
	# Read only paths configs/profile of "$ref" with profile path rename into an index file.
	git ls-tree -z -r "${head}" -- configs/profile | sed -z "s|configs/profile|configs/${dir}|" | GIT_INDEX_FILE="${index}" git update-index -z --index-info
	# Remove configs/${dir} from the release index.
	git ls-tree -z -r "${head_release}" | grep -zZv configs/"${dir}" | GIT_INDEX_FILE="${index}".release git update-index -z --index-info
	# This combines the two indexes (obtained in the last two commands).
	GIT_INDEX_FILE="${index}".release git ls-files -z -s | GIT_INDEX_FILE="${index}" git update-index -z --add --index-info
    fi
    # Write the index file into the repo as a tree.
    tree="$(GIT_INDEX_FILE="${index}" git write-tree)"
    # Write a commit to the repo.
    commit="$(echo "Update profile ${dir}" | git commit-tree "$tree" -p "${head_release}")"
    # Update the head of the repository to be the new commit.
    git update-ref refs/heads/"${release_branch}" "$commit" "${head_release}"
}

expand_empty() {
    ref="${releng}"
    dir="releng"

    GIT_INDEX_FILE="${index}" git read-tree --empty

    # Find the current head of ${ref}
    head="$(git show-ref --hash "refs/${ref_type}/${ref}")"
    # Read the tree of "$ref" with profile path rename into an index file.
    git ls-tree -z -r "${head}" | sed -z "s|configs/profile|configs/${dir}|" | GIT_INDEX_FILE="${index}" git update-index -z --index-info
    # Write the index file into the repo as a tree.
    tree="$(GIT_INDEX_FILE="${index}" git write-tree)"
    # Write a commit to the repo.
    commit="$(echo "Update profile ${dir}" | git commit-tree "$tree")"
    # Update the head of the repository to be the new commit.
    git update-ref refs/heads/"${release_branch}" "$commit" 0000000000000000000000000000000000000000
}

ref_test() {
    es=0
    git show-ref --quiet "$1" || es=$?

    if [[ $es == 1 ]] ; then
	error "$2"
    elif [[ $es > 1 ]] ; then
	error "$3"
    fi
}

ref_test_negative() {
    es=0
    git show-ref --quiet "$1" || es=$?

    if [[ $es == 0 ]] ; then
	error "$2"
    elif [[ $es > 1 ]] ; then
	error "$3"
    fi
}

release_prepare() {
    ref_test_negative "refs/tags/${release}" "Release tag ${release} can not be created because it already exists" "Error testing the release tag"
}

show_ref() {
    for ref in ${allrefs[@]} ; do
	ref_test "refs/${ref_type}/${ref}" "Git reference refs/${ref_type}/${ref} should exist" "Error testing git reference refs/${ref_type}/${ref}"
    done
}

archive_ref() {
    git archive --format=tar.gz --prefix=parabolaiso-"${version}"/ "refs/${ref_type}/${release}" > parabolaiso-"${version}".tar.gz
}

archive() {
    git tag -s -m "tag ${release}" "${release}" "${release_branch}"
    archive_ref
    gpg --detach-sign --use-agent parabolaiso-"${version}".tar.gz
}

dist_expand() {
    index="$(mktemp)"

    es=0
    git show-ref --quiet "refs/heads/${release_branch}" || es=$?

    if [[ $es == 0 ]] ; then
	expand "${releng}" releng
    elif [[ $es == 1 ]] ; then
	expand_empty
	echo "Release branch created."
    elif [[$es > 1 ]] ; then
	error "Error testing the release branch"
    fi

    for i in ${!branches[@]} ; do
	expand ${refs[$i]} ${branches[$i]}
    done
}

if [[ ${type} == dist ]] ; then
    ref_type="tags"
    dist
    release_prepare
    show_ref
    dist_expand
    archive
elif [[ ${type} == dist-branches ]] ; then
    ref_type="heads"
    dist_branches
    show_ref
    dist_expand
    archive_ref
else
     error "Second argument is not correct"
fi
