##@cooked comments # -*- coding: utf-8 mode: sh -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8
## Fonctions pour la gestion des projets
##@cooked nocomments
##@include vcs
##@include semver
uprovide ptools
urequire vcs semver

function is_any_branch() {
    local branch="$1"; shift
    if [ -z "$branch" ]; then
        branch="$(git_get_branch)" || return 2
    fi
    local annex; git_have_annex && annex=1
    local spec r
    for spec in "$@"; do
        case "$spec" in
        m|master)
            [ -n "$r" ] || r=1
            [ "$branch" == "master" ] && return 0
            ;;
        d|develop)
            [ -n "$r" ] || r=1
            [ "$branch" == "develop" ] && return 0
            ;;
        r|release)
            [ -n "$r" ] || r=1
            [[ "$branch" == release-* ]] && return 0
            ;;
        h|hotfix)
            [ -n "$r" ] || r=1
            [[ "$branch" == hotfix-* ]] && return 0
            ;;
        f|feature|t|topic)
            [ -n "$r" ] || r=1
            [ "$branch" == "master" ] && continue
            [ "$branch" == "develop" ] && continue
            [[ "$branch" == release-* ]] && continue
            [[ "$branch" == hotfix-* ]] && continue
            [ -n "$annex" -a "$branch" == "git-annex" ] && continue
            [[ "$branch" == */* ]] && continue
            return 0
            ;;
        -m|-master)
            [ -n "$r" ] || r=0
            [ "$branch" == "master" ] && return 1;;
        -d|-develop)
            [ -n "$r" ] || r=0
            [ "$branch" == "develop" ] && return 1;;
        -r|-release)
            [ -n "$r" ] || r=0
            [[ "$branch" == release-* ]] && return 1;;
        -h|-hotfix)
            [ -n "$r" ] || r=0
            [[ "$branch" == hotfix-* ]] && return 1;;
        -f|-feature|-t|-topic)
            [ -n "$r" ] || r=0
            [ "$branch" == "master" ] && continue
            [ "$branch" == "develop" ] && continue
            [[ "$branch" == release-* ]] && continue
            [[ "$branch" == hotfix-* ]] && continue
            [ -n "$annex" -a "$branch" == "git-annex" ] && continue
            [[ "$branch" == */* ]] && continue
            return 1
            ;;
        esac
    done
    return ${r:-1}
}
function is_master_branch() { is_any_branch "$1" master; }
function is_develop_branch() { is_any_branch "$1" develop; }
function is_release_branch() { is_any_branch "$1" release; }
function is_hotfix_branch() { is_any_branch "$1" hotfix; }
function is_feature_branch() { is_any_branch "$1" feature; }

function list_release_branches() {
    local origin="$1"
    git_list_${origin:+p}branches "$origin" | grep '^release-'
}
function list_hotfix_branches() {
    local origin="$1"
    git_list_${origin:+p}branches "$origin" | grep '^hotfix-'
}
function list_feature_branches() {
    local origin="$1"
    git_list_${origin:+p}branches "$origin" |
    grep -vF master |
    grep -vF develop |
    grep -v '^release-' |
    grep -v '^hotfix-' |
    grep -v '/' |
    if git_have_annex; then
        grep -vF git-annex
    else
        cat
    fi
}

################################################################################
# Outils de haut niveau

function __pom_get_version() {
    # obtenir la version dans le pom $1(=pom.xml)
    local pom="${1:-pom.xml}"
    awk <"$pom" '/^[ \t]*<version>/ {
  sub(/^.*<version>/, "")
  sub(/<\/version>.*$/, "")
  print
  exit
}'
}
function __pom_set_version() {
    # modifier la version du le fichier $1(=pom.xml) à la valeur
    # $2(=1.0.0-SNAPSHOT)
    local pom="${1:-pom.xml}"
    local version="${2:-1.0.0-SNAPSHOT}"
    local tmpfile; ac_set_tmpfile tmpfile
    awk <"$pom" >"$tmpfile" '
BEGIN {
  version = '"$(qawk "$version")"'
  found = 0
}
!found && $0 ~ /^[ \t]*<version>/ {
  prefix = "<version>"
  if (match($0, /^.*<version>/)) {
    prefix = substr($0, RSTART, RLENGTH)
  }
  suffix = "</version>"
  if (match($0, /<\/version>.*$/)) {
    suffix = substr($0, RSTART, RLENGTH)
  }
  print prefix version suffix
  found = 1
  next
}
{ print }'
    cat "$tmpfile" >"$pom"
    ac_clean "$tmpfile"
}

function pver() {
    local DEFAULT_FILE=VERSION.txt
    local DEFAULT_POM=pom.xml

    local -a args
    local action=auto
    local source=auto
    local file=
    local git=
    local version=
    local allow_empty=
    local convert=auto
    local operator=
    local oversion=
    local setversion=
    local incversion=
    local setprelease=
    local setalpha=
    local setbeta=
    local setrc=
    local setrelease=
    local setmetadata= addmetadata=
    local vcsmetadata=
    parse_opts "${PRETTYOPTS[@]}" \
        --help '$exit_with pver_display_help' \
        -f:,--file: '$set@ file; source=file' \
        -e:,--maven:,--pom: '$set@ file; source=pom' \
        -F:,--file-string: '$set@ file; source=file-string' \
        -g:,--git-string: '$set@ git; source=git-string' \
        -s:,--string: '$set@ version; source=string' \
        --show action=show \
        --allow-empty allow_empty=1 \
        --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=' \
        --prel '$action=update; setversion=prel; 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:,--set-metadata: '$action=update; set@ setmetadata' \
        --add-metadata: '$action=update; set@ addmetadata' \
        -M,--vcs-metadata '$action=update; vcsmetadata=1' \
        @ args -- "$@" && set -- "${args[@]}" || { eerror "$args"; return 1; }

    # Calculer la source
    if [ "$source" == auto ]; then
        source=file
        for i in "$DEFAULT_FILE" version.txt "$DEFAULT_POM"; do
            if [ -f "$i" ]; then
                case "$i" in
                "$DEFAULT_POM")
                    source=pom
                    file="$i"
                    break
                    ;;
                *)
                    source=file
                    file="$i"
                    break
                    ;;
                esac
            fi
        done
    elif [ "$source" == file ]; then
        [ "$action" == auto ] && action=update
    fi
    [ "$source" == file -a -z "$file" ] && file="$DEFAULT_FILE"
    [ "$source" == pom -a -z "$file" ] && file="$DEFAULT_POM"
    [ "$action" == auto ] && action=show

    # Lire la version
    if [ "$source" == file ]; then
        [ -f "$file" ] && version="$(<"$file")"
    elif [ "$source" == pom ]; then
        [ -f "$file" ] || {
            eerror "$file: fichier introuvable"
            return 1
        }
        version="$(__pom_get_version "$file")"
    elif [ "$source" == file-string ]; then
        if [ -z "$file" ]; then
            file="$DEFAULT_FILE"
        elif [ -d "$file" ]; then
            file="$file/$DEFAULT_FILE"
        fi
        [ -f "$file" ] && version="$(<"$file")"
        file=
    elif [ "$source" == git-string ]; then
        splitfsep2 "$git" : branch name
        [ -n "$branch" ] || branch=master
        [ -n "$name" ] || name="$DEFAULT_FILE"
        if git rev-parse --verify --quiet "$branch:$name" >/dev/null; then
            version="$(git cat-file blob "$branch:$name" 2>/dev/null)"
        fi
    fi
    [ -n "$version" -o -n "$allow_empty" ] || version=0.0.0

    # Conversion éventuelle du numéro de version
    psemver_parse "$version"
    [ -n "$valid" ] && convert=
    if [ "$convert" == auto ]; then
        [ -z "$valid" ] && convert=1
    fi
    if [ -n "$convert" -a -n "$version" ]; then
        mversion="$(awkrun version="$version" '
function nbdot(s) {
  gsub(/[^.]/, "", s)
  return length(s)
}
BEGIN {
  ### utools, legacy
  if (version ~ /[0-9]+(\.[0-9]+)*(-r[0-9][0-9]\/[0-9][0-9]\/[0-9][0-9][0-9][0-9])?$/) {
    metadata = ""
    # traiter release date
    pos = length(version) - 12
    if (pos > 0) {
      rdate = substr(version, pos + 3)
      metadata = substr(rdate, 7, 4) substr(rdate, 4, 2) substr(rdate, 1, 2)
      version = substr(version, 1, pos)
    }
    # traiter metadata
    match(version, /[0-9]+(\.[0-9]+(\.[0-9]+)?)?/)
    pos = RLENGTH
    if (pos < length(version)) {
      if (metadata != "") metadata = metadata "."
      metadata = metadata substr(version, pos + 2)
    }
    version = substr(version, 1, pos)
    # ajouter les éléments manquants
    while (nbdot(version) < 2) {
      version = version ".0"
    }
    # afficher la version migrée au format semver
    if (metadata != "") print version "+" metadata
    else print version

  ### maven, pom.xml
  } else if (version ~ /[0-9]+(\.[0-9]+)*(-[sS][nN][aA][pP][sS][hH][oO][tT])?$/) {

    prerelease = ""
    # traiter prerelease
    pos = length(version) - 9
    if (pos > 0) {
      prerelease = substr(version, pos + 2)
      version = substr(version, 1, pos)
    }
    # traiter prerelease
    match(version, /[0-9]+(\.[0-9]+(\.[0-9]+)?)?/)
    pos = RLENGTH
    if (pos < length(version)) {
      if (prerelease != "") prerelease = prerelease "."
      prerelease = prerelease substr(version, pos + 2)
    }
    version = substr(version, 1, pos)
    # ajouter les éléments manquants
    while (nbdot(version) < 2) {
      version = version ".0"
    }
    # afficher la version migrée au format semver
    if (prerelease != "") print version "-" prerelease
    else print version
  }
}')"
        if [ -n "$mversion" ]; then
            version="$mversion"
            psemver_parse "$version"
        fi
    fi

    # Actions
    if [ "$action" == show ]; then
        if [ -n "$version" ]; then
            if isatty; then
                estepi "La version actuelle est $version"
            else
                echo "$version"
            fi
            return 0
        else
            if isatty; then
                estepi "Il n'y a pas de version définie"
            fi
            return 1
        fi
    fi

    if [ "$action" == check ]; then
        [ -n "$valid" ] || { eerror "Numéro de version invalide: $version"; return 1; }

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

    elif  [ "$action" == update ]; then
        [ -z "$version" -a -n "$allow_empty" ] && return 1
        [ -n "$valid" ] || { eerror "Numéro de version invalide: $version"; return 1; }

        if [ -n "$file" ]; then
            if [ -f "$file" ]; then
                if isatty; then
                    estepi "La version actuelle est $version"
                fi
            elif [ "$source" == pom ]; then
                eerror "$file: fichier introuvable"
                return 1
            else
                if isatty; then
                    ask_yesno "Le fichier $(ppath "$file") n'existe pas. Faut-il le créer?" O || return 1
                fi
            fi
        fi

        # forcer le numéro de version
        if [ -n "$setversion" ]; then
            if [ "$setversion" == prel ]; then
                local branch; branch="$(git_get_branch)" || return 2
                if [[ "$branch" == release-* ]]; then
                    setversion="${branch#release-}"
                else
                    eerror "$branch: n'est pas une release branch"
                    return 1
                fi
            fi
            psemver_setversion "$setversion" "" || { eerror "Numéro de version invalide: $setversion"; return 1; }
        fi

        # Calculer metadata
        if [ -n "$vcsmetadata" ]; then
            setmetadata="$(git rev-parse --short HEAD)" || return 1
        fi

        # incrémenter les numéros de version
        if [ "$incversion" == auto ]; then
            if [ -n "$setrelease" -o -n "$setprelease" -o -n "$setmetadata" -o -n "$addmetadata" ]; then
                incversion=
            else
                incversion=menu
            fi
        fi
        if [ "$incversion" == menu ]; then
            psemver_copy x; {
                psemver_incmajor x
                psemver_setprelease "$setprelease" x
                if [ -n "$addmetadata" ]; then
                    [ -n "$setmetadata" ] && psemver_setmetadata "$setmetadata" x
                    psemver_addmetadata "$addmetadata" x
                else
                    psemver_setmetadata "$setmetadata" x
                fi
                psemver_setvar versionx x
            }
            psemver_copy z; {
                psemver_incminor z
                psemver_setprelease "$setprelease" z
                if [ -n "$addmetadata" ]; then
                    [ -n "$setmetadata" ] && psemver_setmetadata "$setmetadata" z
                    psemver_addmetadata "$addmetadata" z
                else
                    psemver_setmetadata "$setmetadata" z
                fi
                psemver_setvar versionz z
            }
            psemver_copy p; {
                psemver_incpatchlevel p
                psemver_setprelease "$setprelease" p
                if [ -n "$addmetadata" ]; then
                    [ -n "$setmetadata" ] && psemver_setmetadata "$setmetadata" p
                    psemver_addmetadata "$addmetadata" p
                else
                    psemver_setmetadata "$setmetadata" p
                fi
                psemver_setvar versionp p
            }
            psemver_copy k; {
                psemver_setprelease "$setprelease" k
                if [ -n "$addmetadata" ]; then
                    [ -n "$setmetadata" ] && psemver_setmetadata "$setmetadata" k
                    psemver_addmetadata "$addmetadata" k
                else
                    psemver_setmetadata "$setmetadata" k
                fi
                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
        if [ -n "$incversion" ]; then
            case "$incversion" in
            major) psemver_incmajor;;
            minor) psemver_incminor;;
            patchlevel) psemver_incpatchlevel;;
            esac
            # Quand on incrémente, réinitialiser la valeur de prérelease et metadata
            psemver_setprelease
            [ -z "$addmetadata" ] && psemver_setmetadata
        fi

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

        # spécifier metadata
        if [ -n "$setmetadata" ]; then
            psemver_setmetadata "$setmetadata" || { eerror "Identifiant de build invalide: $setmetadata"; return 1; }
        fi
        if [ -n "$addmetadata" ]; then
            psemver_addmetadata "$addmetadata" || { eerror "Identifiant de build invalide: $addmetadata"; return 1; }
        fi

        # afficher le résultat final
        psemver_setvar version
        if [ -n "$file" ]; then
            case "$source" in
            file) echo "$version" >"$file";;
            pom) __pom_set_version "$file" "$version";;
            esac
        fi
        if isatty; then
            estepn "La nouvelle version est $version"
        else
            echo "$version"
        fi
    fi

    return 0
}