# -*- mode: sh; -*-

declare -A CMD_HANDLERS
CMD_HANDLERS=(
    ["init"]=_repo_init
    ["help"]=_repo_help
    ["abandon"]=_repo_abandon
    ["branch"]=_repo_branch
    ["branches"]=_repo_branches
    ["checkout"]=_repo_checkout
    ["cherry-pick"]=_repo_cherry_pick
    ["diff"]=_repo_diff
    ["download"]=_repo_download
    ["forall"]=_repo_forall
    ["grep"]=_repo_grep
    ["list"]=_repo_list
    ["prune"]=_repo_prune
    ["rebase"]=_repo_rebase
    ["selfupdate"]=_repo_selfupdate
    ["smartsync"]=_repo_smartsync
    ["stage"]=_repo_stage
    ["start"]=_repo_start
    ["status"]=_repo_status
    ["sync"]=_repo_sync
    ["upload"]=_repo_upload
    ["version"]=_repo_version
)

# To be populated by command handlers.
declare -a OPTIONS
declare -A ARG_OPTIONS

declare cur
declare prev

_init_cur_prev() {
    cur=$(_get_cword "=")
    prev=$(_get_cword "=" 1)

    _split_longopt
}

_find_repo() {
    local dir=$(pwd)
    local found=1

    while [ "${dir}" != / ]
    do
        if [ -e "${dir}/.repo/repo/main.py" ]
        then
            found=0
            break
        fi

        dir=$(cd "${dir}/.." && pwd)
    done

    if [ ${found} -eq 0 ]
    then
        echo "${dir}"
    fi
}

_is_repo_dir() {
    local repo_root=$(_find_repo)

    [ -n "${repo_root}" ]
}

_gen_comps() {
    local completions="$1"
    local suffix="${2:- }"

    local -i i
    local -a tmp=( $(compgen -W "${completions}" -- ${cur}) )

    for (( i=0; i < ${#tmp[*]}; i++ ))
    do
        tmp[$i]="${tmp[$i]}${suffix}"
    done

    COMPREPLY=(
        "${COMPREPLY[@]}"
        "${tmp[@]}"
    )
}

_strip_colors () {
    # taken from http://goo.gl/7KlLZ
    sed -r "s/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g"
}

_no_completion() {
    true
}

_command_completion() {
    local cmds

    if _is_repo_dir
    then
        cmds=("abandon" "branch" "branches" "checkout" "cherry-pick" "diff"
            "download" "forall" "grep" "help" "init" "list" "prune" "rebase"
            "selfupdate" "smartsync" "stage" "start" "status" "sync"
            "upload" "version")
    else
        cmds=("help" "init")
    fi

    _gen_comps "${cmds[*]}"
}

_branch_completion() {
    local raw_branches

    # separate statement required to be able to access exit code
    raw_branches=$(repo branches 2>/dev/null)

    if [ $? -eq 0 ]
    then
        local branches=$(
            echo "${raw_branches}" |
            _strip_colors | awk 'BEGIN { FS="|" } { print $1 }' | cut -c 3-
        )

        _gen_comps "${branches}"
    fi
}

_dir_completion() {
    _filedir -d
}

_project_completion() {
    local repo_root=$(_find_repo)

    if [ -n "${repo_root}" -a -f "${repo_root}/.repo/project.list" ]
    then
        local projects=$(cat "${repo_root}/.repo/project.list")
        _gen_comps "${projects}"
    fi
}

_manifest_completion() {
    local repo_root=$(_find_repo)

    if [ -n "${repo_root}" ]
    then
        local manifests_dir="${repo_root}/.repo/manifests"
        local git_dir="${manifests_dir}/.git"
        local candidates

        manifests=$(
            git --git-dir "${git_dir}" ls-files "*.xml" 2>/dev/null)

        if [ $? -eq 0 ]
        then
            _gen_comps "${manifests}"
        fi
    fi
}

_path_cmd_completion() {
    _gen_comps "$(compgen -c ${cur})"
}

_is_option() {
    local opt="$1"

    [[ "${opt}" == -* ]]
}

_is_long_option() {
    local opt="$1"

    [[ "${opt}" == --* ]]
}

_expects_arg() {
    local opt="$1"

    if [[ ${ARG_OPTIONS[$opt]} ]]
    then
        return 0
    else
        return 1
    fi
}

_handle_options() {
    if _expects_arg "${prev}"
    then
        local handler=${ARG_OPTIONS[$prev]}
        eval ${handler} "${cur}"
    elif _is_option "${cur}"
    then
        _gen_comps "${OPTIONS[*]}"

        local arg_short
        local arg_long

        for opt in "${!ARG_OPTIONS[@]}"
        do
            if _is_long_option "${opt}"
            then
                arg_long="${arg_long} ${opt}"
            else
                arg_short="${arg_short} ${opt}"
            fi
        done

        _gen_comps "${arg_short}"
        _gen_comps "${arg_long}" "="
    else
        return 1
    fi

    return 0
}

_is_known_shortopt() {
    local needle="$1"

    for opt in ${OPTIONS[@]}
    do
        if [ "${opt}" = "${needle}" ]
        then
            return 0
        fi
    done

    return 1
}

_is_known_longopt() {
    local needle="$1"

    [[ ${ARG_OPTIONS[$1]} ]]
}

_arg_index() {
    local -i i=2               # skip repo and command
    local -i ix=0

    while [ ${i} -lt ${COMP_CWORD} ]
    do
        if _is_known_shortopt "${COMP_WORDS[i]}"
        then
            i+=1
        elif _is_known_longopt "${COMP_WORDS[i]}"
        then
            i+=2
        elif _is_option "${COMP_WORDS[i]}"
        then
            i+=1
        else
            i+=1
            ix+=1
        fi
    done

    eval $1="${ix}"
}

_when_ix() {
    local ix="$1"
    local completion="$2"

    _arg_index arg_ix

    if [ ${arg_ix} -eq ${ix} ]
    then
        ${completion}
        return 0
    else
        return 1
    fi
}

_when_first() {
    _when_ix 0 "$1"
}

_when_even() {
    local completion="$1"

    _arg_index arg_ix

    if [ $(( ${arg_ix} % 2 )) -eq 0 ]
    then
        ${completion}
        return 0
    else
        return 1
    fi
}

_cmp_opts() {
    local opt="$1"
    local word="$2"

    if _is_option "${opt}" && ! _is_long_option "${opt}"
    then
        [ "${word}" == "${opt}" ]
    else
        [[ "${word}" == "${opt}"=* || "${word}" == "${opt}" ]]
    fi
}

_before() {
    local completion="$1"
    local words

    shift

    _get_comp_words_by_ref -n = words

    for word in "${words[@]}"
    do
        for needle in "$@"
        do
            if _cmp_opts "${needle}" "${word}"
            then
                return 1
            fi
        done
    done

    ${completion}
    return 0
}

_repo_init() {
    OPTIONS=(
        "-h" "--help"
        "-q" "--quite"
        "--mirror"
        "--no-repo-verify"
    )

    ARG_OPTIONS=(
        ["-u"]=_no_completion
        ["--manifest-url"]=_no_completion
        ["-b"]=_no_completion
        ["--manifest-branch"]=_no_completion
        ["-m"]=_manifest_completion
        ["--manifest-name"]=_manifest_completion
        ["--reference"]=_dir_completion
        ["--repo-url"]=_no_completion
        ["--repo-branch"]=_no_completion
    )

    _handle_options
}

_repo_help() {
    OPTIONS=(
        "-a" "--all"
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _when_first _command_completion
}

_repo_abandon() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _when_first _branch_completion || _project_completion
}

_repo_branch() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options
}

_repo_branches() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options
}

_repo_checkout() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _when_first _branch_completion || _project_completion
}

_repo_cherry_pick() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options
}

_repo_diff() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _project_completion
}

_repo_download() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _when_even _project_completion
}

_repo_forall() {
    OPTIONS=(
        "-h" "--help"
        "-p"
        "-v" "--verbose"
    )

    ARG_OPTIONS=(
        ["-c"]=_path_cmd_completion
        ["--command"]=_path_cmd_completion
    )

    _handle_options || _before _project_completion -c --command || _filedir
}

_repo_grep() {
    OPTIONS=(
        "-h" "--help"
        "--cached"
        "-r" "--revision"
        "-i" "--ignore-case"
        "-a" "--text"
        "-I"
        "-w" "--word-regexp"
        "-v" "--invert-match"
        "-G" "--basic-regexp"
        "-E" "--extended-regexp"
        "-F" "--fixed-strings"
        "--all-match"
        "--and" "--or" "--not"
        "-(" "-)"
        "-n"
        "-l" "--name-only" "--files-with-matches"
        "-L" "--files-without-match"
    )

    ARG_OPTIONS=(
        ["-e"]=_no_completion
        ["-C"]=_no_completion
        ["-B"]=_no_completion
        ["-A"]=_no_completion
    )

    _handle_options || _project_completion
}

_repo_list() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _project_completion
}

_repo_prune() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options || _project_completion
}

_repo_rebase() {
    OPTIONS=(
        "-h" "--help"
        "-i" "--interactive"
        "-f" "--force-rebase"
        "--no-ff"
        "-q" "--quiet"
        "--autosquash"
    )

    ARG_OPTIONS=(
        ["--whitespace"]=_no_completion
    )

    _handle_options || _project_completion
}

_repo_selfupdate() {
    OPTIONS=(
        "-h" "--help"
        "--no-repo-verify"
    )

    ARG_OPTIONS=()

    _handle_options
}

_repo_smartsync() {
    OPTIONS=(
        "-h" "--help"
        "-f" "--force-broken"
        "-l" "--local-only"
        "-n" "--network-only"
        "-d" "--detach"
        "-q" "--quiet"
        "--no-repo-verify"
    )

    ARG_OPTIONS=(
        ["-j"]=_no_completion
        ["--jobs"]=_no_completion

    )

    _handle_options || _project_completion
}

_repo_stage() {
    OPTIONS=(
        "-h" "--help"
        "-i" "--interactive"
    )

    ARG_OPTIONS=()

    _handle_options || _project_completion
}

_repo_start() {
    OPTIONS=(
        "-h" "--help"
        "--all"
    )

    ARG_OPTIONS=()

    _handle_options || _when_first _branch_completion || _project_completion
}

_repo_status() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=(
        ["-j"]=_no_completion
        ["--jobs"]=_no_completion
    )

    _handle_options || _project_completion
}

_repo_sync() {
    OPTIONS=(
        "-h" "--help"
        "-f" "--force-broken"
             "--force-sync"
        "-l" "--local-only"
        "-n" "--network-only"
        "-d" "--detach"
        "-q" "--quiet"
        "-s" "--smart-sync"
        "--no-repo-verify"
    )

    ARG_OPTIONS=(
        ["-j"]=_no_completion
        ["--jobs"]=_no_completion
    )

    _handle_options || _project_completion
}

_repo_upload() {
    OPTIONS=(
        "-h" "--help"
        "-t"
        "--no-verify"
        "--verify"
    )

    ARG_OPTIONS=(
        ["--re"]=_no_completion
        ["--reviewers"]=_no_completion
        ["--cc"]=_no_completion
        ["--br"]=_branch_completion
    )

    _handle_options || _project_completion
}

_repo_version() {
    OPTIONS=(
        "-h" "--help"
    )

    ARG_OPTIONS=()

    _handle_options
}

_repo() {
    COMPREPLY=()

    _init_cur_prev

    if [ ${COMP_CWORD} -eq 1 ]
    then
        _command_completion
    else
        local cmd=${COMP_WORDS[1]}
        local handler=${CMD_HANDLERS["${cmd}"]}
        if [ -n ${handler} ]
        then
            eval ${handler}
        fi
    fi

    return 0
}

complete -o nospace -F _repo repo
