#! /usr/bin/env bash

# Manage build2 toolchain version.
#
# Usage: version [<module>...]
#
usage="usage: $0 [<module>...]"

# Notes:
#
# * min/max are [min max)
#   if min == max, then '== min' is assumed
#   if max is unspecified, then '>= min' is assumed
#   currently, max can only be 1.2.3- (#if-conditions will need adjustment)
#

build2_min=0.3.0 # No max, always >=.

libbutl=0.4.0-a1
libbutl_min=$libbutl
libbutl_max=$libbutl
#libbutl_max=0.5.0-              # Comment if pre-release.
libbutl_build2=$build2_min
libbutl_hxx=butl/version

build2=0.4.0-a1
build2_libbutl_min=$libbutl_min
build2_libbutl_max=$libbutl_max
build2_build2=$build2_min
build2_hxx=build2/version

libbpkg=0.4.0-a1
libbpkg_min=$libbpkg
libbpkg_max=$libbpkg
#libbpkg_max=0.5.0-             # Comment if pre-release.
libbpkg_libbutl_min=$libbutl_min
libbpkg_libbutl_max=$libbutl_max
libbpkg_build2=$build2_min
libbpkg_hxx=bpkg/version

bpkg=0.4.0-a1
bpkg_libbutl_min=$libbutl_min
bpkg_libbutl_max=$libbutl_max
bpkg_libbpkg_min=$libbpkg_min
bpkg_libbpkg_max=$libbpkg_max
bpkg_build2=$build2_min
bpkg_hxx=bpkg/bpkg-version

build2_toolchain=0.4.0-a1
build2_toolchain_build2=$build2_min

brep=0.4.0-a1
brep_libbutl_min=$libbutl_min
brep_libbutl_max=$libbutl_max
brep_libbpkg_min=$libbpkg_min
brep_libbpkg_max=$libbpkg_max
brep_build2=$build2_min
brep_hxx=brep/version

#
#
owd=`pwd`
trap "{ cd $owd; exit 1; }" ERR
set -o errtrace # Trap in functions.

function info () { echo "$*" 1>&2; }
function error () { info "$*"; exit 1; }

modules=

while [ $# -gt 0 ]; do
  case $1 in
    *)
      modules="$modules ${1%/}"
      shift
      ;;
  esac
done

if [ -z "$modules" ]; then
  modules="libbutl build2 libbpkg bpkg build2-toolchain brep"
fi

# Convert string version representation (1.2.3-a1) to its numeric equivalent
# in the form AABBCCDD where:
#
# AA - major version number
# BB - minor version number
# CC - bugfix version number
# DD - alpha / beta (DD + 50) version number
#
# When DD is not 00, 1 is subtracted from AABBCC. For example:
#
# Version     AABBCCDD
# 2.0.0       02000000
# 2.1.0       02010000
# 2.1.1       02010100
# 2.2.0-a1    02019901
# 3.0.0-b2    02999952
#
# For a version in the 1.2.3- form, it returns (AABBCC-1)00, which is the
# highest possible version that is less than 1.2.3-.
#
function str_num () # <ver-str>
{
  # Here we assume the version is valid.
  #
  local a=`echo $1 | sed -e 's/\([0-9]*\).*/\1/' -`
  local b=`echo $1 | sed -e 's/[^.]*\.\([0-9]*\).*/\1/' -`
  local c=`echo $1 | sed -e 's/[^.]*\.[^.]*\.\([0-9]*\).*/\1/' -`
  local d=`echo $1 | sed -n -e 's/[^.]*\.[^.]*\.[^-]*\(-[ab]*[0-9]*\)/\1/p' -`

  local v=$(($a * 1000000 + $b * 10000 + $c * 100))

  if [ "$d" ]; then
    ((v -= 100))
    if [ $d != "-" ]; then
      if [ ${d:1:1} = 'b' ]; then
	((v += 50))
      fi
      d=`echo $d | sed -e 's/-[ab]\([0-9]*\)/\1/' -`
      ((v += $d))
    fi
  fi

  #info $1 -> $a $b $c $d -> $v
  echo $v
}

function str_num_test () { if [ `str_num $1` != $2 ]; then error $1 != $2; fi }

str_num_test 2.0.0    2000000
str_num_test 2.1.0    2010000
str_num_test 2.1.1    2010100
str_num_test 2.2.0-a1 2019901
str_num_test 3.0.0-b2 2999952

str_num_test 2.0.0-   1999900
str_num_test 2.2.0-   2019900

# In-place sed.
#
function ised () # <regex> <file>
{
  local r=$1
  local f=$2
  local o=$f.orig

  mv $f $o
  cp -p $o $f # Keep owner, permissions.

  if ! sed -e "$r" $o >$f; then
    mv $o $f
    return 1
  fi

  if cmp -s $o $f; then
    mv $o $f
  else
    rm $o
  fi
}

# Modules which we may depend on. These are translated to 'depends:' in the
# manifest and preprocessor checks in the version header. Note that we can
# add external modules like libodb, etc., when the time comes.
#
depends="libbutl libbpkg"

for m in $modules; do
  v=`echo $m | sed -e 's/-/_/g'`
  V=`echo $v | sed -e 's/\(.*\)/\U\1/'`

  s=${!v}
  n=`str_num $s`

  b=${v}_build2; b=${!b}

  # version
  #
  ised "s/^[0-9].*/$s/" $m/version

  # manifest
  #
  if [ -f $m/manifest ]; then
    ised "s/^\(version:\) .*/\1 $s/" $m/manifest
    ised "s/^\(requires: build2\) .*/\1 >= $b/" $m/manifest
  fi

  # bootstrap.build
  #
  ised "s/^\(version =\) .*/\1 $s/" $m/build/bootstrap.build
  ised "s/^\(using build@\).*/\1$b/" $m/build/bootstrap.build

  # version header
  #
  h=${v}_hxx; h=${!h}
  if [ "$h" ]; then
    ised "s/^\(#define ${V}_VERSION\) .*/\1 $n/" $m/$h
    ised "s/^\(#define ${V}_VERSION_STR\) .*/\1 \"$s\"/" $m/$h
  fi

  # Dependencies (manifest and version header).
  #
  for d in $depends; do
    if [ $d = $m ]; then continue; fi

    dv=`echo $d | sed -e 's/-/_/g'`
    DV=`echo $dv | sed -e 's/\(.*\)/\U\1/'`

    dmin=${v}_${dv}_min; dmin=${!dmin}
    dmax=${v}_${dv}_max; dmax=${!dmax}

    if [ -z "$dmin" ]; then continue; fi # No dependency.

    dminn=`str_num $dmin`

    # Figure out dependency contraints for the manifest and the header.
    #
    if [ -z "$dmax" ]; then
      dcm=">= $dmin"
      dch="${DV}_VERSION < $dminn"
    elif [ "$dmin" = "$dmax" ]; then
      dcm="== $dmin"
      dch="${DV}_VERSION != $dminn"
    else
      dmaxn=`str_num $dmax`
      dcm="[$dmin $dmax)"
      dch="${DV}_VERSION < $dminn || ${DV}_VERSION > $dmaxn"
    fi

    if [ -f $m/manifest ]; then
      ised "s/^\(depends: $d\) .*/\1 $dcm/" $m/manifest
    fi

    if [ "$h" ]; then
      ised "s/^#if ${DV}_VERSION .*/#if $dch/" $m/$h
    fi
  done
done