# ghc-pkg command line completion for bash
#
# Copyright 2006-2007 Lennart Kolmodin <kolmodin@dtek.chalmers.se>

_ghc-pkg-get-ghc-pkg()
{
    echo ghc-pkg
}

_ghc-pkg-pkg-fields()
{
    # usage: _ghc-pkg-pkg-fields pkg-id
    #
    # list all fields of the pkg-id

    # same fields for all packages but different in different versions of
    # ghc-pkg? this can probably be done better/faster

    if [[ -z "$1" ]]; then
        echo "usage: _ghc-pkg-pkg-fields pkg-id"
        return 1
    fi

    local fields

    fields="$( $(_ghc-pkg-get-ghc-pkg) describe $1 )"

    #if [[ fields != *"cannot find package"* ]]; then
    echo "$fields" | grep ".*:.*" | sed "s/^\(.*\):.*\$/\1/"
    #fi
}

_ghc-pkg-pkg-ids()
{
    # usage: _ghc-pkg-pkg-ids
    #
    # simply lists all package ids known by ghc-pkg.
    $(_ghc-pkg-get-ghc-pkg) list --simple-output
}

_ghc-pkg-pkgs()
{
    # usage: _ghc-pkg-pkgs [include-pkgs] [include-ids]
    #
    # with optional parameter include-pkgs it will list all packages known
    # to ghc-pkg.
    # with optional parameter include-ids it will list all package-ids known
    # to ghc-pkg.
    local pkgs
    local result
    pkgs=( $( _ghc-pkg-pkg-ids ) )
    result=( )

    local withPkgs="no" withIds="no"
    while [[ -n "$1" ]]; do
        case "$1" in
            include-pkgs)
                withPkgs="yes" ;;
            include-ids)
                withIds="yes" ;;
            *)
                echo "unknown parameter '$1' to _ghc-pkg-pkgs"
                return 1 ;;
        esac
        shift
    done

    # user must supply either include-pkgs, include-ids or both
    if [[ $withPkgs != "yes" && $withIds != "yes" ]]; then
        echo "usage: _ghc-pkg-pkgs [include-pkgs] [include-ids]"
        return 1
    fi

    # find all packages if the user requested them
    if [[ $withPkgs == "yes" ]]; then
        # O(n^2) algorithm to exclude duplicates
        for p in ${pkgs[*]}; do
            p="${p//-[0-9.]*/}"
            for existing in ${result[*]}; do
                if [[ "$existing" == "$p" ]]; then
                    continue 2
                fi
            done
            result=( "${result[@]}" "${p}" )
        done
    fi

    # include all pkg-ids if requested
    if [[ $withIds == "yes" ]]; then
        result=( "${result[@]}" "${pkgs[@]}" )
    fi

    # we are finished, echo the result
    echo "${result[*]}"

    # happy ending
    return 0
}

_ghc-pkg()
{
    local cur
    cur=${COMP_WORDS[COMP_CWORD]}

    COMPREPLY=()

    local actions flags
    actions='register update unregister expose hide list latest describe field'
    dbflags="--user \
        --global \
        -f --package-conf= \
        --global-conf="
    registerflags="--force \
        -g --auto-ghci-libs \
        -D --define-name="
    listflags="--simple-output"
    flags="$dbflags \
        $registerflags \
        $listflags \
        -? --help \
        -V --version"

    # if it's the users first word; complete it and return
    if (($COMP_CWORD == 1)); then
        COMPREPLY=( $( compgen -W "$actions $flags" -- $cur ) )
        return 0
    fi

    # now we know we have at least one word written

    local action="unknown" \
          prev numwords \
          cword act
    prev=${COMP_WORDS[COMP_CWORD-1]}
    numwords=${#COMP_WORDS[@]}

    # find the action with O(n*m) algorithm
    # where n = ${#COMP_WORDS[*]}
    #       m = number of actions
    for cword in ${COMP_WORDS[*]}; do
        for act in $actions; do
            if [[ "$cword" == "$act" ]]; then
                action=$cword
            fi
        done
    done

    case $action in
        register|update)
            # we want to complete both flags and paths, how?
            # we do it by checking if the user has started to write a flag
            # or a path, and then decide what to complete.
            # that is, to complete a flag, the user must start to write a '-'
            if [[ "$cur" == -* ]]; then
                # (we assume) it's the start of a flag
                # set COMPREPLY to flags relevant to these actions
                COMPREPLY=( $( compgen -W "$dbflags $registerflags" -- $cur ) )
            fi
            ;;
        unregister|expose|hide|list|describe)
            # all these actions can be completed with exactly one argument,
            # a pkg-id.
            COMPREPLY=( $( compgen -W "$dbflags" -- $cur ) )

            # add special flags for some actions
            if [[ "$action" == "list" ]]; then
                COMPREPLY+=( $( compgen -W "$listflags" -- $cur ) )
            fi

            COMPREPLY+=( $( compgen -W "$( _ghc-pkg-pkgs include-ids )" -- $cur ) )
            ;;
        latest)
            # complete only packages, not package ids
            COMPREPLY=( $( compgen -W "$( _ghc-pkg-pkgs include-pkgs )" -- $cur ) )
            ;;
        field)
            # we should always complete on the flags...
            COMPREPLY=( $( compgen -W "$dbflags" -- $cur ) )

            # then, we should either complete the package name or the field
            # lets find out which one

            # find the number of words in COMP_WORDS before COMP_CWORD that
            # isn't flags. it should be 2 or 3 for us to complete it,
            #   exactly 2 if we should complete the package name
            #   exactly 3 if we should complete the field name
            #   otherwise, don't do any additional completion except the
            #   flags

            # count the number of non flags up till the current word
            local numnonflags=0 lastword i
            for (( i=0 ; $i < $COMP_CWORD ; i++ )); do
                if [[ ${COMP_WORDS[$i]} != -* ]]; then
                    lastword=${COMP_WORDS[$i]}
                    numnonflags=$(( ++numnonflags ))
                fi
            done

            case $numnonflags in
                2)
                    # complete on pkg-ids
                    COMPREPLY+=( $( compgen -W "$( _ghc-pkg-pkgs include-ids )" -- $cur ) ) ;;
                3)
                    # complete on fields
                    COMPREPLY+=( $( compgen -W "$( _ghc-pkg-pkg-fields $lastword )" -- $cur ) ) ;;
            esac
            ;;
        *)
            # unknown action, not yet given by the user
            # return all possible completions
            COMPREPLY=( $( compgen -W "$actions $flags" -- $cur ) )
            ;;
    esac
}

complete -F _ghc-pkg -o default ghc-pkg

# vim: set ft=sh tw=80 sw=4 et :