#! /bin/bash

function Recho() { echo -e "\e[0;31m""$@""\e[0m"; }
function Gecho() { echo -e "\e[0;32m""$@""\e[0m"; }
function Becho() { echo -e "\e[0;34m""$@""\e[0m"; }

function errexit() {
  exec >&2
  Recho "Error: $@"
  exit 1
}

function usage() {
  exec >&2
  cat <<EOF
  Usage:
	$(basename $0) [OPTIONS]

  Options:
	-h,-?,--help	This page.
	-n		Dryrun.

Prepare a new changes file entry preloaded with all commits since the last
changes tag and load it into \$EDITOR (vi). If the version file was changed,
optionally submitt and tag the new changes. Otherwise simply leave the changes
in place.

Don't forgett to push created tags as well: git push --tags

EOF
  exit 1
}

DRYRUN=0

case "$1" in
  -[n]*)
    DRYRUN=1
    ;;
  -[hH?]*)
    usage
    ;;
  --help)
    usage
    ;;
esac


export LC_ALL=""
export LANG="en"

function Edit()
{
  local FILE="$1"
  vi "$FILE"
  sed -i 's/ \+$//' "$FILE"
}

EMAIL="$(git config --get user.email)"

CHANGESFILE=$(ls package/*.changes)
test -f "$CHANGESFILE" || errexit "No changes file '$CHANGESFILE'"

VERSIONFILE="VERSION.cmake"
test -f "$VERSIONFILE" || errexit "No version file '$VERSIONFILE'"

function getversion() {
  cat "$VERSIONFILE" \
  | awk '
  function getnum() {
    gsub("^[^\"]*\"","")
    gsub("\".*$","")
  }
  /^ *SET *\( *LIBZYPP_MAJOR *"[0-9]+" *\)/       {getnum();major=$0}
  /^ *SET *\( *LIBZYPP_MINOR *"[0-9]+" *\)/       {getnum();minor=$0}
  /^ *SET *\( *LIBZYPP_PATCH *"[0-9]+" *\)/       {getnum();patch=$0}
  /^ *SET *\( *LIBZYPP_COMPATMINOR *"[0-9]+" *\)/ {getnum();compatminor=$0}
  /^# LAST RELEASED:/ {
      gsub("^.*RELEASED: *","");
      gsub(" +$","");
      lastcompat=$0
      gsub(".*\\(","",lastcompat)
      gsub("\\).*","",lastcompat)
      gsub(" +\\(.*","");
      lastrelease=$0
  }
  END {
    thisrelease = major"."minor"."patch" ("compatminor")"
    printf "LAST_RELEASE='%s'\n", lastrelease
    printf "LAST_COMPAT='%s'\n", lastcompat
    printf "THIS_RELEASE='%s'\n", major"."minor"."patch
    printf "THIS_COMPAT='%s'\n", compatminor
    printf "THIS_MINOR='%s'\n", minor
    printf "THIS_PATCH='%s'\n", patch
  }
  '
}

function setversion() {
  local KEY="$1"
  local VAL="$2"
  sed -i "s/^ *SET *( *${KEY} .*/SET(${KEY} \"${VAL}\")/" "$VERSIONFILE"
}

function sameVersion() {
  test "$LAST_RELEASE" == "$THIS_RELEASE" -a "$LAST_COMPAT" == "$THIS_COMPAT"
}

function getchanges() {
  git log --no-merges --pretty=format:'@@%B' "$LAST_RELEASE"..HEAD	\
  | awk '/^@@/{p=1}/^@@Translated using Weblate/{p=0}(p){print}'	\
  | sed '/./{H;$!d};x;/./{s/ *\n */ /g;s/^ *//;s/ *$//;/[^]})!?:.]$/s/$/./;p};d'	\
  | fold -s -w 66 | sed '/^@@/{s/^../- /;p;d};s/^/  /'
}

function newchangesentry() {
  echo "-------------------------------------------------------------------"
  echo "$(date) - $EMAIL"
  echo ""
  echo "$(getchanges)"
  sameVersion || {
    echo "- version $THIS_RELEASE ($THIS_COMPAT)"
  }
  echo ""
}

function is_fast_forward() {
  git fetch
  test "$(git rev-list HEAD..origin/$(git name-rev --name-only HEAD) --count)" == "0"
}

is_fast_forward || {
  Recho "!!!"
  Recho "!!! Branch is not fast-forward. Pull changes first."
  Recho "!!!"
  exit 7
}

git status --porcelain | grep '^[^ ?]' | grep -v "$VERSIONFILE\|$CHANGESFILE" && {
  Becho "!!! Files other than version and changes are added to the index."
  Becho "!!! Doing dryrun..."
  DRYRUN=1
}

# A tag for $LAST_RELEASE must exist!
eval $(getversion)
git rev-parse -q --verify "$LAST_RELEASE" >/dev/null || {
  Recho "!!!"
  Recho "!!! There is no LAST_RELEASE tag '$LAST_RELEASE'. Check manually. "
  Recho "!!! (git tag -m 'tagging $LAST_RELEASE' '$LAST_RELEASE' ?commit?)"
  Recho "!!!"
  exit 8
}

if [ "$DRYRUN" == "1" ]; then
  newchangesentry
  sameVersion && {
    Becho "!!! Version is unchanged at $LAST_RELEASE ($LAST_COMPAT)."
  }
  exit 0
fi

# check version file
#
while true; do
  # $LAST_RELEASE
  # $LAST_COMPAT
  # $THIS_RELEASE
  # $THIS_COMPAT
  sameVersion && {
    newchangesentry
    Becho "!!! Version is unchanged at $LAST_RELEASE ($LAST_COMPAT)."
    read -n 1 -p "$(Gecho "(a)bort, (c)ontinue, (P) patch, (M) minor, (I) incompat minor, (e)dit version [e]: ")" RES
    echo
    case "${RES:-e}" in
      [eE]*)
	Edit $VERSIONFILE
	eval $(getversion)
	continue
	;;
      [cC])
	Becho "!!! Leave $VERSIONFILE untouched"
	break
	;;
      [P])
	setversion LIBZYPP_PATCH $(($THIS_PATCH + 1))
	eval $(getversion)
	continue
	;;
      [M])
	setversion LIBZYPP_MINOR $(($THIS_MINOR + 1))
	setversion LIBZYPP_PATCH 0
	eval $(getversion)
	continue
	;;
      [I])
	setversion LIBZYPP_COMPATMINOR $(($THIS_MINOR + 1))
	setversion LIBZYPP_MINOR $(($THIS_MINOR + 1))
	setversion LIBZYPP_PATCH 0
	eval $(getversion)
	continue
	;;
      *)
	errexit "aborted"
	;;
    esac
  }
  break
done

# prepare changes file
#
TMPFILE=$(mktemp)
trap " [ -f \"$TMPFILE\" ] && /bin/rm -f -- \"$TMPFILE\" " 0 1 2 3 13 15
{ newchangesentry; cat $CHANGESFILE; } >$TMPFILE

RES=e
while [ "$RES" == "e" ]; do
  Edit $TMPFILE
  echo
  awk '{print}/^----------/{n=n+1; if ( n == 2 ) exit 0; }' $TMPFILE
  read -n 1 -p "$(Gecho "(a)bort, (c)ontinue, (s)ubmitt, (e)dit [e]: ")" RES
  echo
  case "${RES:-e}" in
    [eE]*)
      RES=e
      ;;
    [cCsS])
      Becho "!!! Store new $CHANGESFILE"
      mv $TMPFILE $CHANGESFILE
      chmod 644 $CHANGESFILE

      test "$RES" == "s" && {
	if [ "$LAST_RELEASE" == "$THIS_RELEASE" ]; then
	  git add "$CHANGESFILE" && git commit -m "changes"
	else
	  Becho "!!! Remember new version $THIS_RELEASE in $VERSIONFILE"
	  sed -i "s/^# LAST RELEASED:.*$/# LAST RELEASED: $THIS_RELEASE ($THIS_COMPAT)/" $VERSIONFILE
	  if git add "$CHANGESFILE" "$VERSIONFILE" \
		&& git commit -m "changes $THIS_RELEASE ($THIS_COMPAT)" \
		  && git tag -m "tagging $THIS_RELEASE" "$THIS_RELEASE" HEAD; then
	    Becho "!!!"
	    Becho "!!! Do not forget to push the commit and the tag: $(Gecho git push --tags origin HEAD)"
	    Becho "!!!"
	  else
	    Recho "!!!"
	    Recho "!!! Commit failed. Check manually. (git reset HEAD~)"
	    Recho "!!!"
	    exit 9
	  fi
	fi
      }
      ;;
    *)
      Becho "!!! Leave $CHANGESFILE untouched"
      ;;
  esac
done
