#!/bin/bash
# MKit - simple install helper
# See LICENSE file for copyright and license details.

mkit_import ini
mkit_import facts

__bump_version() {
    #
    # Bump version on stdin by level $1 (x, y or z)
    #
    local rlevel=$1     # release level
    local old           # old version
    local oldx          # ... X
    local oldz          # ... Z
    local oldy          # ... Y
    local tmpy          # Y parsing cache
    local new           # bumped version
    read -r old
    oldx=${old%.*.*}
    oldz=${old#*.*.}
    tmpy=${old%.*}
    oldy=${tmpy#*.}
    case $rlevel in
        x) new="$((oldx+MKIT_BUMPSIZE)).0.0"            ;;
        y) new="$oldx.$((oldy+MKIT_BUMPSIZE)).0"        ;;
        z) new="$oldx.$oldy.$((oldz+MKIT_BUMPSIZE))"    ;;
        *) die "invalid release level: $1"  ;;
    esac
    echo "$new"
}

__relck() {
    #
    # Die if blocking condition $1 is detected
    #
    local condition=$1      # condition name
    local oracle            # expected value
    case "$condition" in
        git_present)
            git rev-parse HEAD >&/dev/null\
             || die "cannot do this outside git repo"
            ;;
        at_relsrc)
            oracle=$(ini 1value project:relsrc)
            git_fact current_branch \
              | grep -qFx "$oracle" \
             || die "you are not on release source branch: $oracle"
            ;;
        not_dirty)
            git diff --shortstat 2>/dev/null \
              | grep -q . \
             && die "tree is dirty!"
            ;;
        tags_ok)
            git_fact latest_tag \
              | grep -q . \
             || die "cannot find latest tag"
            ;;
        vbump_hot)
            git diff-tree --no-commit-id --name-only -r HEAD \
              | grep -qFx mkit.ini \
             || die "last change must be version bump in mkit.ini"
            ;;
        no_wip)
            git_fact reldiff \
              | grep '^....... WIP ' \
             && die "WIP commit since $(git_fact latest_tag)"
            ;;
        ini_version)
            oracle=$(git_fact latest_version | __bump_version "$rlevel")
            ini 1value project:version  \
              | grep -qFx "$oracle" \
             || die "new version not in mkit.ini: $oracle"
            ;;
        *)
            die "unknown release check: $condition"
            ;;
    esac
}

__release() {
    #
    # Prepare release
    #
    # Span release routines: make a signed tag, check branch
    # and update release branch
    #
    # FIXME: Using project:prerl as build.sh:semver() does may not be
    #        compatible with this release strategy
    #
    local rlevel=$1     # release level (x, y or z)
    local newtag        # new tag
    local relsrc        # release source branch (if any)
    local reldst        # release destination branch (if any)

    __relck git_present
    __relck at_relsrc
    __relck not_dirty
    __relck tags_ok
    __relck vbump_hot
    __relck no_wip
    __relck ini_version

    newtag=$(git_fact latest_version | __bump_version "$rlevel" | git_ver2tag )
    set -e
    debug_var newtag
    $MKIT_DRY && return
    git tag -m "$(__release_msg)" "$newtag"

    relsrc=$(ini 1value project:relsrc)
    reldst=$(ini 1value project:reldst)
    debug_var relsrc reldst
    if test -n "$reldst" && test "$reldst" != "$relsrc"; then
        git branch -f "$reldst" "$newtag"
    fi
}

__release_msg() {
    #
    # Generate message for annotated tag
    #
    # The last commit before issuing a release must be "Bump version" commit
    # suggested by _vbump_gitmsg() and  manually edited by user.  Since the
    # commit contains changelog, this function just uses the message body.
    #
    # The sort message (first line) is replaced with a nicer one (with project
    # name, codename and version).
    #
    echo "$(ini 1value project:name) $newtag - $(ini 1value project:codename)"
    echo
    git show -s --format=%B \
      | tail -n +3
}

__vbump() {
    #
    # Do version bump at level $1
    #
    # Perform checks, compute new version, update mkit.ini and initiate
    # 'Bump version' commit with changelog template.
    #
    local rlevel=$1     # bump level (x, y or z)
    local nextver       # version after the bump
    local cache         # cache for the message
    __relck git_present
    __relck at_relsrc
    __relck not_dirty
    nextver=$(ini 1value project:version | __bump_version "$rlevel")
    debug_var nextver
    $MKIT_DRY && return
    update_version "$nextver" mkit.ini \
      || die "failed to update version in mkit.ini"
    git add mkit.ini \
      || die "failed to add mkit.ini to the index"
    cache=$(mktemp -t "mkit._vbump_gitmsg.XXXXXXXX")
    _vbump_gitmsg > "$cache"
    git commit -e -F "$cache"   # note: reading from stdin will break vim
    rm "$cache"
}

_vbump_gitmsg() {
    #
    # Compose git message template for 'Bump version' commit
    #
    echo "Bump version"
    echo ""
    echo "Overview of changes:"
    echo ""
    git_fact reldiff \
      | sed '
            s/^[a-f0-9]\{7\} /\n *  &/; t PATHS
            s/^/        /
            :PATHS
        '
}

release() {
    #
    # Perform release on Z level
    #
    __release z
}

release_x() {
    #
    # Perform release on X level
    #
    __release x
}

release_y() {
    #
    # Perform release on Y level
    #
    __release y
}

release_z() {
    #
    # Perform release on Z level
    #
    __release z
}

vbump() {
    #
    # Perform version bump on Z level
    #
    __vbump z
}

vbump_x() {
    #
    # Perform version bump on X level
    #
    __vbump x
}

vbump_y() {
    #
    # Perform version bump on Y level
    #
    __vbump y
}

vbump_z() {
    #
    # Perform version bump on Z level
    #
    __vbump z
}