#XXX drop this name
function trace
{
    #echo "$*" >&2
    echo "$*" >>/var/tmp/triton-completion.log
}

function _dashdash_complete() {
    trace ""
    trace "-- $(date)"
    trace "\$@: '$@'"
    trace "COMP_WORDBREAKS: '$COMP_WORDBREAKS'"
    trace "COMP_CWORD: '$COMP_CWORD'"
    trace "COMP_LINE: '$COMP_LINE'"
    trace "COMP_POINT: $COMP_POINT"

    # Guard against negative COMP_CWORD. This is a Bash bug at least on
    # Mac 10.10.4's bash. See
    # <https://lists.gnu.org/archive/html/bug-bash/2009-07/msg00125.html>.
    if [[ $COMP_CWORD -lt 0 ]]; then
        trace "abort on negative COMP_CWORD"
        exit 1;
    fi

    local interspersed shortOpts longOpts takesArgOpts subCmds
    interspersed=$1
    shift   # $interspersed
    shortOpts=$1
    shift   # $shortOpts
    longOpts=$1
    shift   # $longOpts
    takesArgOpts=$1
    shift   # $takesArgOpts
    subCmds=$1
    shift   # $subCmds or '--'
    if [[ -n "$subCmds" ]]; then
        shift   # '--'
    fi
    trace "interspersed: '$interspersed'"
    trace "shortOpts: '$shortOpts'"
    trace "longOpts: '$longOpts'"
    trace "takesArgOpts: '$takesArgOpts'"
    trace "subCmds: '$subCmds'"

    # I don't know how to do array manip on argv vars,
    # so copy over to ARGV array to work on them.
    declare -a ARGV
    i=0
    len=$#
    while [[ $# -gt 0 ]]; do
        ARGV[$i]=$1
        shift;
        i=$(( $i + 1 ))
    done

    trace "ARGV: '${ARGV[@]}'"
    trace "ARGV[COMP_CWORD-1]: '${ARGV[$(( $COMP_CWORD - 1 ))]}'"
    trace "ARGV[COMP_CWORD]: '${ARGV[$COMP_CWORD]}'"
    trace "ARGV len: '$len'"

    # Get 'state' of option parsing at this COMP_POINT.
    # Copying "dashdash.js#parse()" behaviour here.
    state=
    nargs=0
    i=1
    while [[ $i -lt $len && $i -le $COMP_CWORD ]]; do
        optname=
        prefix=
        word=

        arg=${ARGV[$i]}
        trace "consider ARGV[$i]: '$arg'"

        if [[ "$arg" == "--" ]]; then
            state=longopt
            word=--
            i=$(( $i + 1 ))
            break;
        elif [[ "${arg:0:2}" == "--" ]]; then
            arg=${arg:2}
            if [[ "$arg" == *"="* ]]; then
                optname=${arg%%=*}
                val=${arg##*=}
                trace "  long opt: optname='$optname' val='$val'"
                state=arg
                word=$val
                prefix="--$optname="
            else
                optname=$arg
                val=
                trace "  long opt: optname='$optname'"
                state=longopt
                word=--$optname

                if [[ "$takesArgOpts" == *"-$optname="* && $i -lt $COMP_CWORD ]]; then
                    i=$(( $i + 1 ))
                    state=arg
                    word=${ARGV[$i]}
                    trace "  takes arg (next arg, word='$word')"
                fi
            fi
        elif [[ "${arg:0:1}" == "-" ]]; then
            trace "  short opt group"
            state=shortopt
            word=$arg

            j=1
            while [[ $j -lt ${#arg} ]]; do
                optname=${arg:$j:1}
                trace "  consider index $j: optname '$optname'"

                if [[ "$takesArgOpts" == *"-$optname="* ]]; then
                    if [[ $(( $j + 1 )) -lt ${#arg} ]]; then
                        state=arg
                        word=${arg:$(( $j + 1 ))}
                        trace "    takes arg (rest of this arg, word='$word')"
                    elif [[ $i -lt $COMP_CWORD ]]; then
                        state=arg
                        i=$(( $i + 1 ))
                        word=${ARGV[$i]}
                        trace "  takes arg (word='$word')"
                    fi
                    break
                fi

                j=$(( $j + 1 ))
            done
        elif [[ "$interspersed" == "true" ]]; then
            trace "  not an opt"
            state=arg  #XXX arg type
            word=$arg
            nargs=$(( $nargs + 1 ))
        elif [[ -n "$subCmds" ]]; then
            if [[ $i -eq $COMP_CWORD ]]; then
                state=subcmds
                word=$arg
            else
                trace "XXX defer on to _triton_${arg}_complete"
                #_triton_${arg}_complete XXX a b c XXX
                return
            fi
        else
            trace "XXX here else"
            XXX
        fi

        trace "  state=$state prefix='$prefix' word='$word'"
        i=$(( $i + 1 ))
    done

    trace "parsed: state=$state prefix='$prefix' word='$word'"
    compgen_opts=
    if [[ -n "$prefix" ]]; then
        compgen_opts="$compgen_opts -P $prefix"
    fi

    case $state in
    shortopt)
        compgen $compgen_opts -W "$shortOpts" -- "$word"
        ;;
    longopt)
        compgen $compgen_opts -W "$longOpts" -- "$word"
        ;;
    subcmds)
        compgen $compgen_opts -W "$subCmds" -- "$word"
        ;;
    arg)
        # Figure out arg type, if we can. If this was an *option* arg, then
        # 'optname' is set.
        if [[ -n "$optname" ]]; then
            argtype=$(echo "$takesArgOpts" | awk -F "-$optname=" '{print $2}' | cut -d' ' -f1)
            trace "argtype (for opt '$optname'): $argtype"
        else
            # TODO set argtype from nargs
            trace "argtype (for arg $nargs): $argtype"
        fi

        # If we don't know what completion to do, then emit nothing. We
        # expect that we are running with:
        #       complete -o default ...
        # where "default" means: "Use Readline's default filename completion if
        # the compspec generates no matches." This gives us the good filename
        # completion, completion in subshells/backticks.
        #
        # The trade-off here is that we cannot explicitly have *no* completion.
        if [[ "${word:0:1}" == '$' ]]; then
            # "-o default" does *not* give us envvar completion apparently. This
            # means that with '-A export' we are missing envvars like "PS1" et al.
            compgen $compgen_opts -P '$' -A export -- "${word:1}"
        elif [[ $argtype == "none" ]]; then
            # TODO: want this hacky way to avoid completions for explicit none?
            echo "(no completion)"
        elif [[ $argtype == "directory" ]]; then
            # XXX damnit... using 'complete -o default' means that no dir hits
            #     results in filenames matching. Arrrrgggg. Compare to 'rmdir'.
            #     Does `compopt` work on Linux? If so
            #           compopt +o filenames -o dirnames
            compgen $compgen_opts -S '/' -A directory -- "$word"
        elif [[ -z $argtype || $argtype == "file" ]]; then
            # 'complete -o default' gives the best filename completion, at least
            # on Mac. ... but we're not using 'complete -o default' here.
            compgen $compgen_opts -S '/' -A file -- "$word"
        else
            # Custom completion types.
            potentials=$($argtype "" "$word")
            compgen $compgen_opts -W "$potentials" -- "$word"
        fi
        ;;
    *)
        trace "unknown state: $state"
        ;;
    esac
}

function _triton_account_complete() {
    trace "XXX _triton_account_complete start"
    COMPREPLY=( $(_dashdash_complete \
        true \
        "-h -j" \
        "--help --json" \
        "" \
        -- "$*") )
}

function _triton_complete() {
    COMPREPLY=( $(_dashdash_complete \
        false \
        "-h -v" \
        "--version --help --verbose" \
        "" \
        "help account info keys create-instance instances instance instance-audit start-instance stop-instance reboot-instance ssh images image packages package" \
        -- "${COMP_WORDS[@]}") )
}

complete -F _triton_complete triton