#!/bin/bash

set -e # abort on errors (otherwise we get a soup of dirs)

# TODO: add $(git describe) to $ME (by keyword subst when checking in).
ME=cabal2gear

srcbranch=upstream
autobranch="$srcbranch"_/AUTO/cabal2rpm
outbranch=master

GHC_VERSION="$(ghc -V | sed 's/^.* version //'  | tr -d '\n')"
export LANG=C
export LC_TIME=C
#==================================================================#
# Create gear-repo from cabal package                              #
#==================================================================#
# (C) Denis Smirnov <mithraen@freesource.info>                     #
# (C) 2015 Ivan Zakharyaschev <imz@altlinux.org>                   #
#     (the support for updates)                                    #
#==================================================================#

if [ ! -f "$1" ]; then
    echo "Use: $0 <package file>"
    echo
    echo 'Note: If the gear-repo already exists in the current working dir, it will be updated.'
    exit -1
fi

PACKAGE="$(realpath "$1")"

echo "$PACKAGE"
N="${PACKAGE##*/}"
N="${N%%.tar.gz}"
PACKAGE_NAME="${N%-*}"
PACKAGE_VER="${N##*-}"
# lowcased package name
PACKAGE_NAME_LC="$(echo -n "$PACKAGE_NAME" | tr '[A-Z]' '[a-z]')"

echo "$N"
echo "$PACKAGE_NAME" "$PACKAGE_NAME"

DIR_NAME="ghc$GHC_VERSION-$PACKAGE_NAME_LC"

spec-clean-sideeffects()
{
    sed -e '/^Packager: .*$/ d; /^%changelog$/,$ d'
}

recover_autobranch()
{   # Repeating more-or-less the code from cabal2rpm-0.20.08-alt10
    # (to be able to recover the previous automatic result
    # and detect the maintainer's tweaks as the diff).
    #
    # TODO: factor out creating a new autobranch from the main code
    # and recover_autobranch() for clarity and convenience (although
    # we want to use the old code here whereas the main code may
    # change drastically).

    echo
    echo $'Well, trying to recover the previous state for:' "$autobranch"
    echo $'1) by re-doing it; 2) by un-doing some common tweaks.'
    echo $'If 1) and 2) match, then we succeed.'
    echo $'If not, this might mean that cabal2rpm itself changed too much since then (and you need to intervene).'
    echo
    echo $'1) re-doing the previous state for: ' "$autobranch"
    
    pushd "$DIR_NAME"
    
    git checkout "$srcbranch" || git checkout -b "$srcbranch" origin/"$srcbranch"
    git checkout -b "$autobranch"
    
    cd "$PACKAGE_NAME"

    cabal2rpm "$(ls -1 *.cabal | head -1)" | spec-clean-sideeffects > ../"ghc$GHC_VERSION-$PACKAGE_NAME_LC".spec
    cd ..

    mkdir -p .gear
    echo "tar: $srcbranch:$PACKAGE_NAME name=@name@-@version@" > .gear/rules
    echo "diff: $srcbranch:$PACKAGE_NAME .:$PACKAGE_NAME name=@name@-@version@-@release@.patch" >> .gear/rules
    echo "copy: $PACKAGE_NAME_LC.watch" >> .gear/rules
    gear-update-tag -a

    # create watch file
    cat > "$PACKAGE_NAME_LC".watch <<EOF
version=3
opts="downloadurlmangle=s|archive/([\w\d_-]+)/([\d\.]+)/|archive/\$1/\$2/\$1-\$2.tar.gz|,\\
filenamemangle=s|(.*)/\$|$PACKAGE_NAME-\$1.tar.gz|" \\
    http://hackage.haskell.org/packages/archive/$PACKAGE_NAME \\
    ([\d\.]*\d)/
EOF

    git add .

    git commit -a -m "automatically generated by $ME from $srcbranch"

    git checkout "$outbranch"
    git merge -s ours "$autobranch" -m 'recovered the automatic result of cabal2rpm/cabal2gear'

    popd
}

set_updating_autobranch()
{
    updating_autobranch="$(cd "$DIR_NAME"; git rev-parse "$autobranch")"
}

if [[ -e "$DIR_NAME" ]]; then
    # we use this variable as a boolean flag, and also we save the old
    # master revision (to use as a parent at the end).
    if ! set_updating_autobranch; then
	{
	    echo $'Cannot proceed without the previous purely automatic result of cabal2rpm/cabal2gear.'
	    echo $'It is expected to be in Git branch: ' "$autobranch"
	    echo $'If your gear-repo comes from an older version of cabal2gear,'
	    echo $'please fake the branch like this (by removing all your tweaks'
	    echo $'in .spec including added buildreqs) and retry:'
	    echo
	    echo "git checkout -b $autobranch $srcbranch"
	    echo "git cherry-pick --no-commit $outbranch"
	    echo "EDIT .spec"
	    echo "git commit -a"
	    echo "git checkout $outbranch"
	    echo "git merge -s ours $autobranch -m 'fake automatic result of cabal2rpm/cabal2gear'"
	} >&2
	recover_autobranch >&2 && set_updating_autobranch || exit 1
    fi
else
    updating_autobranch=
    mkdir -p "$DIR_NAME"
fi
readonly updating_autobranch

# A similar update logic ontop of 2 (or 3) Git branches ("upstream" --
# sources, "master" -- automatic result of cabal2rpm) has been
# implemented by me and used in
# [git-make](http://packages.altlinux.org/en/Sisyphus/srpms/git-make).
#
# (There, I use 3 Git branches: the source, the automatic result, and
# the result tweaked and approved by a human. This kind of the third
# branch could also be useful for maintainence of packages.)
#
# Some of the principal differences in the usage of git-make and
# cabal2gear are:
#
# * the source in git-make is supposed to be edited in the Git branch
# directly (not taken from a tarball);
#
# * git-make has some kind of "init" command (and more commands)
# whereas cabal2gear has no options; cabal2gear guesses the wanted
# action ("init" or "update") automatically.
#
# -- imz

pushd "$DIR_NAME"
if [[ "$updating_autobranch" ]]; then
    git checkout "$srcbranch" || git checkout -b "$srcbranch" origin/"$srcbranch"
    gear-update "$PACKAGE" "$PACKAGE_NAME"
else
    git init
    git symbolic-ref HEAD refs/heads/"$srcbranch"
    gear-update "$PACKAGE" -c "$PACKAGE_NAME"
fi
git commit -a -m "$PACKAGE_VER"
if [[ "$updating_autobranch" ]]; then
    git branch -D "$autobranch"
fi
git checkout -b "$autobranch"
if [[ "$updating_autobranch" ]]; then
    git merge -s ours "$updating_autobranch" --no-commit
fi

cd "$PACKAGE_NAME"

readonly oldspectemplate="ghc[1-9][0-9]*\.[0-9]\+\.[0-9]\+-$PACKAGE_NAME_LC".spec
readonly spec="ghc-$PACKAGE_NAME_LC".spec
readonly dirty_spec="$(mktemp --tmpdir "$spec"XXXXXX)"
cabal2rpm "$(ls -1 *.cabal | head -1)" > "$dirty_spec"
git ls-tree -r HEAD --name-only | grep "$oldspectemplate$" && git mv "$oldspec" "$spec" || :
spec-clean-sideeffects < "$dirty_spec" > ../"$spec"
cd ..

mkdir -p .gear
echo "tar: $srcbranch:$PACKAGE_NAME name=@name@-@version@" > .gear/rules
echo "diff: $srcbranch:$PACKAGE_NAME .:$PACKAGE_NAME name=@name@-@version@-@release@.patch" >> .gear/rules
echo "copy: $PACKAGE_NAME_LC.watch" >> .gear/rules
gear-update-tag -a

# create watch file
cat > "$PACKAGE_NAME_LC".watch <<EOF
version=3
opts="downloadurlmangle=s|archive/([\w\d_-]+)/([\d\.]+)/|archive/\$1/\$2/\$1-\$2.tar.gz|,\\
filenamemangle=s|(.*)/\$|$PACKAGE_NAME-\$1.tar.gz|" \\
    http://hackage.haskell.org/packages/archive/$PACKAGE_NAME \\
    ([\d\.]*\d)/
EOF

git add .

git commit -a -m "automatically generated by $ME from $srcbranch"

# The branch to be tweaked by humans:
if [[ "$updating_autobranch" ]]; then
    git checkout "$outbranch"
    git merge "$autobranch" -m 'updates by cabal2gear'
    # we do not use the changelog and new packager from the fresh cabal2rpm
    rm "$dirty_spec"
    add_changelog --entry="- updated with the help of $ME." "$spec"
else
    git checkout -b "$outbranch"
    mv "$dirty_spec" "$spec"

    git config 'remote.origin.url' "git.alt:packages/ghc$GHC_VERSION-$PACKAGE_NAME_LC"
    git config 'remote.origin.push' "refs/heads/*:refs/heads/*"
    git config 'remote.origin.fetch' "refs/heads/*:refs/remotes/origin/*"
fi

git add "$spec"
gear-commit -a --no-edit --spec="$spec"
echo $'Now you can tweak the result and git commit [--amend] your new tweaks.'
