#!/bin/bash
#
# This file is distributed under the Eclipse Public License 2.0.
# See LICENSE for details.

#set -x -v
set -e

if expr "$0" : '.*/.*' >/dev/null 2>&1 ; then
  cmdDir=`dirname $0`
else
  cmdDir='.'
fi
if test -r $cmdDir/coin-functions ; then
  . $cmdDir/coin-functions
else
  echo "Cannot find utility functions file coin-functions; exiting."
  exit 1
fi

# process parameters
releaseStyle="reset"
while [ $# -ge 1 ] ; do
  case "$1" in
    -h | --help | --usage )
      cat <<EOF
Usage: $0 { -h | --help | --usage | -t }

Options:
  -h, --help, --usage   Print this help and exit
  -t                    Make sure release tag is visible on current branch

More details about -t:
  If -t is specified, the script will commit the release version number
  change (xx.yy -> xx.yy.zz) to the current branch, tag this commit, and
  then adds another commit that reverts the tagged commit.
  If -t is not specified, the script will also commit the release version
  number change to the current branch, tag this commit, but then removes
  the tagged commit from the current branch by resetting it.
  Using -t has the advantage that the tag will be visible in the log of the
  stable branch. Not using -t has the advantage that the temporary change in
  the version number will not be part of the stable branch.

COIN-OR standard practice is to generate releases by taking a snapshot
of the stable branch of the project. This script can be used to prepare
a new release based on the currently checked out stable branch.
Thus, this script is supposed to be run from a directory that contains the
clone of a COIN-OR project and that has a branch named stable/X.Y checkout out.

This script will do the following:

  - Automatically determine the next release number (X.Y.0 if this is the
    first release for stable/X.Y, otherwise one greater than any existing
    release)
  - Update configure.ac to specify the release version number in the AC_INIT
    macro and the corresponding libtool version number in the AC_COIN_INITIALIZE
    macro.
  - Execute run_autotools to update configure, Makefile.in, etc., to reflect
    the most recent release of BuildTools and change in version number.

If there is any error during these tasks the script will stop and you should
examine the output.
If the script completes without error, examine the output, too.

This script does not make any changes to the remote repository, but prints
out the commands that should be issued to push the release tag or
to remove the release tag in the local clone.
EOF
      exit 0
    ;;
    -t | --ted )
      releaseStyle="revert"
      ;;
    *)
      echo "Unknown argument. Run with -h to see usage."
      exit 1
    ;;
  esac
  shift
done

if ! git diff --exit-code --quiet HEAD ; then
  echo "EXIT: There are uncommitted changes. I'm too afraid to proceed."
  exit 1
fi

# figure out name of project from repo URL, remove possible .git suffix
topProjName=`git remote get-url origin`
topProjName=${topProjName##*/}
topProjName=${topProjName/.git/}
echo "Project        : $topProjName"

branchName=`git rev-parse --abbrev-ref HEAD`
echo "Branch         : $branchName"

if [[ "$branchName" != stable/* ]] ; then
  echo "ERROR: Expected branchname to start with 'stable/"
  exit 1
fi


# get tags from remote
git fetch -t

# get last release from current stable branch
lastRelease=`git tag --list "${branchName/stable/releases}*" | sort -V | tail -1`
echo "Last Release   : $lastRelease"

# figure out new release number
if [ -z "$lastRelease" ] ; then
  # no last release, then take stable version and add .0
  if [[ "$branchName" =~ ^stable/([0-9][0-9]*)\.([0-9][0-9]*) ]] ; then
    majVer=${BASH_REMATCH[1]}
    minVer=${BASH_REMATCH[2]}
    relVer=0
  else
    echo "ERROR: Branchname does not match stable/xx.yy"
    exit 1
  fi
elif [[ "$lastRelease" =~ ^releases/([0-9][0-9]*)\.([0-9][0-9]*)\.([0-9][0-9]*)$ ]] ; then
  majVer=${BASH_REMATCH[1]}
  minVer=${BASH_REMATCH[2]}
  relVer=${BASH_REMATCH[3]}
  (( relVer += 1 ))
else
  echo "ERROR: Last release name does not match releases/xx.yy.zz format"
  exit 1
fi
newVer=${majVer}.${minVer}.${relVer}

echo "New release    : releases/$newVer"

isNormal=yes
if test $isNormal = yes ; then
  newLTCurrent=`calcLibtoolCurrentGit $majVer $minVer`
  newLTRevision=$relVer
  newLTAge=`calcLibtoolAgeGit $majVer $minVer`
  (( newLTAge -= 1 ))
  newLTVer=${newLTCurrent}:${newLTRevision}:${newLTAge}
  echo "Libtool version: $newLTVer"
fi

# Find configure.ac files and update the version.

echo ''
echo "===> Checking for configure.ac files ..."
confac_files=`find . -name 'configure.ac' -print`

if test -n "$confac_files" ; then

# Take the attitude that [] around parameters in AC_INIT is optional,
# it's the commas that count. This does make for a surpassing ugly regular
# expression.  A comma in the version string will cause a spectacular failure.
# In AC_COIN_INITIALIZE, take the attitude that we only add a
# libtool version number as argument.

  echo ''
  echo "===> Updating version numbers in configure.ac files ..."
  for i in $confac_files; do
    sed -i -e "s|AC_INIT\([^,]*\),[^,]*,\(.*\)|AC_INIT\1,[$newVer],\2|" $i
    sed -i -e "s|AC_COIN_PROJECTDIR_INIT(\(.*\))|AC_COIN_PROJECTDIR_INIT\(\1,$newLTCurrent:$newLTRevision:$newLTAge\)|" $i
    git diff $i
  done
else
  echo "    ... none to process."
fi

# Find config_proj_default.h. If there's a definition for PROJ_VERSION, adjust it and
# add config_proj_default.h.bak to the list of files to be restored.

configFileLoc=`find . -name 'config_*_default.h' -print`
if test -n "$configFileLoc" ; then
  versionSym=${topProjName^^}_VERSION
  echo ''
  echo "===> Updating $versionSym in $configFileLoc (if present)"
  echo ''
  sed -i -e "s/# *define $versionSym .*\$/#define $versionSym \"$newVer\"/" \
    -e "s/# *define ${versionSym}_MAJOR .*\$/#define ${versionSym}_MAJOR $majVer/" \
    -e "s/# *define ${versionSym}_MINOR .*\$/#define ${versionSym}_MINOR $minVer/" \
    -e "s/# *define ${versionSym}_RELEASE .*\$/#define ${versionSym}_RELEASE $relVer/" \
    $configFileLoc
  git diff $configFileLoc
fi

if [ $topProjName != BuildTools ] ; then
  buildtoolsDir=$cmdDir

  echo ''
  echo '===> Running $buildtoolsDir/run_autotools ...'
  echo ''

  [ -e BuildTools ] && mv BuildTools BuildTools.bak

  $buildtoolsDir/run_autotools

  [ -e BuildTools.bak ] && mv BuildTools.bak BuildTools
fi

if [ -e .coin-or/generate_readme ]; then
  if [ x"${COINBREW_HOME-}" == x ]; then
    if ! command -v coinbrew; then
      export COINBREW_HOME=$(command -V coinbrew | head -n1 | cut -d ' ' -f 3)
    else  
      echo "EXIT: To generate README, you must either have coinbrew in your"
      echo "      path or manually set COINBREW_HOME to the location of the"
      echo "      coinbrew repo." 
      exit 1
    fi
  fi
  echo ''
  echo '===> Generating README ...'
  echo ''
  
  COIN_VERSION_OVERRIDE=releases/$newVer bash .coin-or/generate_readme > README.md

  git diff README.md
fi

echo ''
echo '===> Showing status, just to be sure ...'
echo ''

git status

echo ''
echo '===> Applying changes to local git clone ...'
echo ''

echo "Committing verion number update"
git commit -a -m "version number updates for release $newVer"
echo
echo "Tagging release: releases/$newVer"
git tag releases/$newVer
echo

if [ "$releaseStyle" = "reset" ] ;
then
  echo "Resetting to original commit"
  git reset --hard HEAD~1
else
  echo "Reverting version number update"
  git revert --no-edit HEAD
fi

echo ''
echo '===> Done ...'
echo ''
echo 'After reviewing the output above, you can push the new release via'
echo ''
echo "   git push -u origin releases/$newVer"
echo ''
echo 'Alternatively, you can forget the locally committed release via'
echo ''
echo "   git tag -d releases/$newVer"
if [ "$releaseStyle" = "revert" ] ;
then
  echo "   git reset --hard HEAD~2"
fi
echo
echo "Don't forget to also run update-doxygen, as appropriate!"
