#!/bin/bash
# -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
source "$(dirname "$0")/ulib/ulib" || exit 1; urequire DEFAULTS semver

function display_help() {
    uecho "$scriptname: gérer des numéros de version selon les règles du versionnage sémantique v2.0.0 (http://semver.org/)

USAGE
    $scriptname [options]

OPTIONS
    -f, --file VERSIONFILE
        Gérer le numéro de version se trouvant dans le fichier spécifié. Le
        fichier est créé si nécessaire. C'est l'option par défaut si un fichier
        nommé VERSION.txt se trouve dans le répertoire courant.
    -s, --string VERSION
        Prendre pour valeur de départ le numéro de version spécifié

    --show
        Afficher le numéro de version. C'est l'action par défaut
    --check
        Vérifier que le numéro de version est conforme aux règles du versionnage
        sémantique
    --convert
    --no-convert
        Activer (resp. désactiver) la conversion automatique.  Par défaut, si la
        version est au format classique 'x.z[.p]-rDD/MM/YYYY', elle est
        convertie automatiquement au format sémantique x.z.p+rYYYYMMDD
        XXX Pour le moment, aucune conversion n'est effectuée
    --eq VERSION
    --ne VERSION
    --lt VERSION
    --le VERSION
    --gt VERSION
    --ge VERSION
    --same VERSION
    --diff VERSION
        Comparer avec la version spécifiée. Les opérateurs --eq, --ne, --lt,
        --le, --gt, et --ge ignorent l'identifiant de build (comme le demande la
        règle du versionnage sémantique). Les opérateurs --same et --diff
        comparent aussi les identifiants de build.
    -v, --set-version VERSION
        Spécifier un nouveau numéro de version qui écrase la valeur actuelle.
        Cette option ne devrait pas être utilisée en temps normal parce que cela
        va contre les règles du versionnage sémantique.
    -u, --update
        Mettre à jour le numéro de version.

    --menu
        Afficher un menu permettant de choisir le composant de la version à
        incrémenter
    -x, --major
        Augmenter le numéro de version majeure
    -z, --minor
        Augmenter le numéro de version mineure. C'est la valeur par défaut.
    -p, --patchlevel
        Augmenter le numéro de patch

    -l, --prelease ID
        Spécifier un identifiant de pré-release, à ajouter au numéro de version.
    -a, --alpha
    -b, --beta
    -r, --rc
        Spécifier une pré-release de type alpha, beta, ou rc. Si la version est
        déjà dans ce type, augmenter la dernière valeur numérique des composants
        de l'identifiant, e.g. alpha deviant alpha.1, beta-1.2 devient beta-1.3,
        rc1 devient rc2
    -R, --final, --release
        Supprimer l'identifiant de prérelease

    -m, --metadata ID
        Spécifier un identifiant de build, à ajouter au numéro de version."
}

action=auto
source=auto
file=
version=
convert=auto
operator=
oversion=
setversion=
incversion=
setprelease=
setalpha=
setbeta=
setrc=
setrelease=
setmetadata=
parse_opts "${PRETTYOPTS[@]}" \
    --help '$exit_with display_help' \
    -f:,--file: '$set@ file; source=' \
    -s:,--string: '$set@ version; source=' \
    --show action=show \
    --check action=check \
    --convert convert=1 \
    --no-convert convert= \
    --eq: '$action=compare; operator=eq; set@ oversion' \
    --ne: '$action=compare; operator=ne; set@ oversion' \
    --lt: '$action=compare; operator=lt; set@ oversion' \
    --le: '$action=compare; operator=le; set@ oversion' \
    --gt: '$action=compare; operator=gt; set@ oversion' \
    --ge: '$action=compare; operator=ge; set@ oversion' \
    -v:,--set-version: '$action=update; set@ setversion; incversion=' \
    -u,--update '$action=update; [ -z "$incversion" ] && incversion=auto' \
    --menu '$action=update; incversion=menu' \
    -x,--major '$action=update; incversion=major' \
    -z,--minor '$action=update; incversion=minor' \
    -p,--patchlevel '$action=update; incversion=patchlevel' \
    -l:,--prelease:,--prerelease: '$action=update; set@ setprelease; [ -z "$setprelease" ] && { setalpha=; setbeta=; setrc=; setrelease=1; }' \
    -a,--alpha '$action=update; setalpha=1; setbeta=; setrc=; setrelease=' \
    -b,--beta '$action=update; setalpha=; setbeta=1; setrc=; setrelease=' \
    -r,--rc '$action=update; setalpha=; setbeta=; setrc=1; setrelease=' \
    -R,--release,--final '$action=update; setalpha=; setbeta=; setrc=; setrelease=1' \
    -m:,--metadata: '$action=update; set@ setmetadata' \
    @ args -- "$@" && set -- "${args[@]}" || die "$args"

# Calculer la source
if [ "$source" == auto ]; then
    for i in VERSION.txt version.txt; do
        if [ -f "$i" ]; then
            file="$i"
            break
        fi
    done
    [ -n "$file" ] || file=VERSION.txt
elif [ -n "$file" ]; then
    [ "$action" == auto ] && action=update
fi
[ "$action" == auto ] && action=show

# Lire la version
if [ -n "$file" ]; then
    [ -f "$file" ] && version="$(<"$file")"
    [ -n "$version" ] ||  version=0.0.0
fi

# Actions

if [ "$action" == show ]; then
    if isatty; then
        estepi "La version actuelle est $version"
    else
        echo "$version"
    fi
    exit 0
fi

psemver_parse "$version"
if [ "$action" == check ]; then
    [ -n "$valid" ] || die "Numéro de version invalide: $version"

elif [ "$action" == compare ]; then
    psemver_parse "$oversion" o

    case "$operator" in
    eq|same)
        for var in valid major minor patchlevel; do
            ovar="o$var"
            [ "${!var}" == "${!ovar}" ] || exit 1
        done
        array_eq prelease oprelease || exit 1
        if [ "$operator" == same ]; then
            array_eq metadata ometadata || exit 1
        fi
        exit 0
        ;;
    ne|diff)
        for var in valid major minor patchlevel; do
            ovar="o$var"
            [ "${!var}" != "${!ovar}" ] && exit 0
        done
        ! array_eq prelease oprelease && exit 0
        if [ "$operator" == diff ]; then
            ! array_eq metadata ometadata && exit 0
        fi
        exit 1
        ;;
    lt)
        [ -z "$valid" -a -z "$ovalid" ] && exit 1
        [ "$major" -lt "$omajor" ] && exit 0
        [ "$major" -gt "$omajor" ] && exit 1
        [ "$minor" -lt "$ominor" ] && exit 0
        [ "$minor" -gt "$ominor" ] && exit 1
        [ "$patchlevel" -lt "$opatchlevel" ] && exit 0
        [ "$patchlevel" -gt "$opatchlevel" ] && exit 1
        case "$(psemver_compare_prelease "" o)" in
        lt) exit 0;;
        esac
        exit 1
        ;;
    le)
        [ -z "$valid" -a -z "$ovalid" ] && exit 1
        [ "$major" -lt "$omajor" ] && exit 0
        [ "$major" -gt "$omajor" ] && exit 1
        [ "$minor" -lt "$ominor" ] && exit 0
        [ "$minor" -gt "$ominor" ] && exit 1
        [ "$patchlevel" -lt "$opatchlevel" ] && exit 0
        [ "$patchlevel" -gt "$opatchlevel" ] && exit 1
        case "$(psemver_compare_prelease "" o)" in
        lt|eq) exit 0;;
        esac
        exit 1
        ;;
    gt)
        [ -z "$valid" -a -z "$ovalid" ] && exit 1
        [ "$major" -lt "$omajor" ] && exit 1
        [ "$major" -gt "$omajor" ] && exit 0
        [ "$minor" -lt "$ominor" ] && exit 1
        [ "$minor" -gt "$ominor" ] && exit 0
        [ "$patchlevel" -lt "$opatchlevel" ] && exit 1
        [ "$patchlevel" -gt "$opatchlevel" ] && exit 0
        case "$(psemver_compare_prelease "" o)" in
        gt) exit 0;;
        esac
        exit 1
        ;;
    ge)
        [ -z "$valid" -a -z "$ovalid" ] && exit 1
        [ "$major" -lt "$omajor" ] && exit 1
        [ "$major" -gt "$omajor" ] && exit 0
        [ "$minor" -lt "$ominor" ] && exit 1
        [ "$minor" -gt "$ominor" ] && exit 0
        [ "$patchlevel" -lt "$opatchlevel" ] && exit 1
        [ "$patchlevel" -gt "$opatchlevel" ] && exit 0
        case "$(psemver_compare_prelease "" o)" in
        gt|eq) exit 0;;
        esac
        exit 1
        ;;
    esac

elif  [ "$action" == update ]; then
    [ -n "$valid" ] || die "Numéro de version invalide: $version"

    if [ -n "$file" ]; then
        if [ -f "$file" ]; then
            if isatty; then
                estepi "La version actuelle est $version"
            fi
        else
            if isatty; then
                ask_yesno "Le fichier $(ppath "$file") n'existe pas. Faut-il le créer?" O || die
            fi
        fi
    fi

    # forcer le numéro de version
    if [ -n "$setversion" ]; then
        psemver_setversion "$setversion" "" || die "Numéro de version invalide: $setversion"
    fi

    # incrémenter les numéros de version
    if [ "$incversion" == auto ]; then
        if [ -n "$setrelease" -o -n "$setprelease" -o -n "$setmetadata" ]; then
            incversion=
        else
            incversion=menu
        fi
    fi
    if [ "$incversion" == menu ]; then
        psemver_copy x; psemver_incmajor x; psemver_setprelease "$setprelease" x; psemver_setvar versionx x
        psemver_copy z; psemver_incminor z; psemver_setprelease "$setprelease" z; psemver_setvar versionz z
        psemver_copy p; psemver_incpatchlevel p; psemver_setprelease "$setprelease" p; psemver_setvar versionp p
        psemver_copy k; psemver_setprelease "$setprelease" k; psemver_setvar versionk k
        nextvs=(
            "$versionx : maj incompatibles de l'API (-x)"
            "$versionz : maj compatibles de l'API (-z)"
            "$versionp : correction de bugs (-p)"
            "$versionk : ne pas incrémenter la version"
        )
        nextv="${nextvs[1]}"
        simple_menu nextv nextvs \
            -t "Incrémenter le numéro de version" \
            -m "Veuillez choisir la prochaine version"
        case "${nextv%)}" in
        *-x) incversion=major;;
        *-z) incversion=minor;;
        *-p) incversion=patchlevel;;
        *) incversion=;;
        esac
    fi
    case "$incversion" in
    major) psemver_incmajor;;
    minor) psemver_incminor;;
    patchlevel) psemver_incpatchlevel;;
    esac

    # spécifier prerelease
    if [ -n "$setrelease" ]; then
        psemver_setprelease ""
    elif [ -n "$setprelease" ]; then
        psemver_setprelease "$setprelease" || die "Identifiant de pre-release invalide: $setprelease"
    fi
    if [ -n "$setalpha" ]; then
        :
    elif [ -n "$setbeta" ]; then
        :
    elif [ -n "$setrc" ]; then
        :
    fi

    # spécifier metadata
    if [ -n "$setmetadata" ]; then
        psemver_setmetadata "$setmetadata" || die "Identifiant de build invalide: $setmetadata"
    fi
    
    # afficher le résultat final
    psemver_setvar version
    if [ -n "$file" ]; then
        echo "$version" >"$file"
    fi
    if isatty; then
        estepn "La nouvelle version est $version"
    else
        echo "$version"
    fi
fi

exit 0