# vim: filetype=sh
#
# SPDX-License-Identifier: BSD-2-Clause
#
# Copyright (c) 2018 Mateusz Piotrowski <0mp@FreeBSD.org>
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     1. Redistributions of source code must retain the above copyright notice,
#        this list of conditions and the following disclaimer.
#     2. Redistributions in binary form must reproduce the above copyright
#        notice, this list of conditions and the following disclaimer in the
#        documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
# vim: filetype=sh
#
# ---
# Version: 0.13.1
# ---
# TODO: Respect "--".

# TODO: Make this function offer "py36-asciinema" (or "textproc/py-asciinema")
# as one of the possible completions of "ascii".
_pkg_complete_any_package()
{

    # Complete empty "$cur" with categories instead of all the packages.
    if [[ -z $cur ]]; then
        COMPREPLY=($(compgen -S '/' -W "$(pkg query %C | sort --unique)" -- "$cur"))
        return 0
    fi

    # Complete by name.
    #
    # Skip package name matching if we know the category as it means that we
    # should look at the origin instead.
    if ! [[ $cur =~ / ]]; then
        # Firstly, try to complete by name. For example this completes "py36-"
        # to "py36-pgsanity". We don't use "--regex" here because if the we
        # look for "py36-pgsanity" the completion mechnism is going to complete
        # "sanity" by replacing it with "py" and trying to complete from there,
        # which is not very helpful due to an enormous number of package names
        # starting with "py".
        COMPREPLY=($(pkg rquery --case-insensitive --regex --no-repo-update %n "$cur"))
        # Add categories.
        COMPREPLY+=($(compgen -S '/' -W "$(pkg query %C | sort --unique)" -- "$cur"))
    fi

    # Complete by origin.
    #
    # If completing by name yield no results or a category is known then try to
    # complete by origin.  This autocompletion can complete "pgsa" to
    # "databases/pgsanity".
    if [[ ${#COMPREPLY[@]} -eq 0 ]]; then
        COMPREPLY=($(pkg rquery --case-insensitive --no-repo-update --regex %o "$cur"))
    elif [[ ${#COMPREPLY[@]} -eq 1 ]] && [[ $COMPREPLY =~ / ]]; then
        cur="${COMPREPLY[0]}"
        # Add "^" to "$cur" so that the regex matches only origins starting
        # with the specified category.
        COMPREPLY=($(pkg rquery --case-insensitive --no-repo-update --regex %o "^$cur"))
    fi
}

_pkg_complete_installed_package()
{

    COMPREPLY=($(pkg query --case-insensitive --regex %n "${cur:-.}"))
}

_pkg()
{
    local cur prev words cword
    _init_completion || return

    local command_list=(help add annotate alias audit autoremove backup
        bootstrap check clean convert create delete fetch info install lock
        plugins query register repo rquery search set shell shlib stats
        unlock update updating upgrade version which)

    local raw_output_formats=(json json-compact yaml)

    local option_with_arg_list=(-C --config -c --chroot -r --rootdir -o --options -j --jail)

    # Is the command already selected?
    local pipe_separated_command_list="$(tr ' ' '|' <<< "${command_list[@]}")"
    local pipe_separated_option_with_arg_list="$(tr ' ' '|' <<< "${option_with_arg_list[@]}")"
    local selected_command=
    local i
    for (( i=0 ; i < "${#words[@]}" ; i++ )); do
        word="${words[$i]}"
        if [[ $word =~ $pipe_separated_command_list ]]; then
            if [[ $i -gt 0 ]] && [[ ${words[$(( i - 1 ))]} =~ $pipe_separated_option_with_arg_list ]]; then
                continue
            else
                selected_command="$word"
                break
            fi
        fi
    done

    if [[ -z $selected_command ]]; then
        case "$prev" in
            -c|--chroot) ;&
            -C|--config) ;&
            -o|--options) ;&
            -r|--rootdir)
                compopt -o default
                ;;
            -j|--jail)
                # XXX0MP: I am not sure if it gets the information we want.
                COMPREPLY=($(compgen -W "$(jls | awk 'NR>1{print $1, $3}')" -- "$cur"))
                ;;
            *)
                if [[ $cur == -* ]]; then
                    COMPREPLY=($(compgen -W '
                        -4
                        -6
                        -c --chroot
                        -C --config
                        -d --debug
                        -j --jail
                        -l --list
                        -N
                        -o --options
                        -r --rootdir
                        -v --version
                        ' -- "$cur"))
                else
                    COMPREPLY=($(compgen -W "${command_list[*]}" -- "$cur"))
                fi
                ;;
        esac
    else
        case "$selected_command" in
            add)
                if [[ $cur =~ ^- ]]; then
                    COMPREPLY=($(compgen -W '
                        -A --automatic
                        -f --force
                        -I --no-scripts
                        -M --accept-missing
                        -q --quiet
                        ' -- "$cur"))
                else
                    COMPREPLY=($(compgen -f -- "$cur"))
                    # "-" is for stdin.
                    COMPREPLY+=($(compgen -W '-' -- "$cur"))
                    # XXX: Do not print space after "protocol://".
                    # XXX: Do we even want to autocomplete protocols?
                    COMPREPLY+=($(compgen -S '://' -W 'ftp http https' -- "$cur"))
                fi
                ;;

            annotate)
                # Leave only pkg-annotate(8) options.
                for (( i=0 ; i < "${#words[@]}" ; i++ )); do
                    case ${words[$i]} in
                        *) words[$i]="" ;;&
                        annotate) break ;;
                    esac
                done

                case ${words[*]} in
                    *\ -A\ *|*\ --add\ *) ;&
                    *\ -D\ *|*\ --delete\ *) ;&
                    *\ -M\ *|*\ --modify\ *) ;&
                    *\ -S\ *|*\ --show\ *)
                        case ${words[*]} in
                            *\ -a\ *|*\ --all\ *)
                                COMPREPLY+=($(compgen -W "$(pkg query %At | sort --unique)" -- "$cur"))
                                ;;
                            *)
                                case $prev in
                                    -*)
                                        COMPREPLY+=($(compgen -W '-a --all' -- "$cur"))
                                        COMPREPLY+=($(compgen -W '
                                            -C --case-sensitive
                                            -g --glob
                                            -i --case-insensitive
                                            -q --quiet
                                            -x --regex
                                            -y --yes
                                            ' -- "$cur"))
                                        COMPREPLY+=($(compgen -W "$(pkg query %n)" -- "$cur"))
                                        ;;
                                    *)
                                        COMPREPLY+=($(compgen -W "$(pkg query %At | sort --unique)" -- "$cur"))
                                        ;;
                                esac
                                ;;
                        esac
                        ;;
                    *)
                        # Postpone presenting all the flags to the user.
                        if [[ $cur == -* ]]; then
                            COMPREPLY+=($(compgen -W '
                                -q --quiet
                                -y --yes
                                ' -- "$cur"))
                        fi
                        COMPREPLY+=($(compgen -W '
                            -a --all
                            -A --add
                            -D --delete
                            -M --modify
                            -S --show
                            ' -- "$cur"))
                        ;;
                esac
                ;;

            alias)
                if [[ $cur =~ ^- ]]; then
                    COMPREPLY=($(compgen -W '-l --list -q --quiet' -- "$cur"))
                else
                    COMPREPLY=($(compgen -W "$(pkg alias --quiet | cut -d ' ' -f 1)" -- "$cur"))
                fi
                ;;

            audit)
                case "$prev" in
                    -f|--file)
                        compopt -o default
                        ;;
                    *)
                        if [[ $cur =~ ^- ]]; then
                            COMPREPLY=($(compgen -W '
                                -f --file
                                -F --fetch
                                -q --quiet
                                -r --recursive
                                ' -- "$cur"))
                        else
                            _pkg_complete_any_package
                        fi
                esac
                ;;

            autoremove)
                COMPREPLY=($(compgen -W '
                    -n --dry-run
                    -q --quiet
                    -y --yes
                    ' -- "$cur"))
                ;;

            backup)
                case "$prev" in
                    -d|--dump) ;&
                    -r|--restore)
                        compopt -o default
                        ;;
                    *)
                        COMPREPLY=($(compgen -W '
                            -d --dump
                            -r --restore
                            -q --quiet
                            ' -- "$cur"))
                        ;;
                esac
                ;;

            bootstrap) ;;

            check)
                # Leave only pkg-check(8) options.
                for (( i=0 ; i < "${#words[@]}" ; i++ )); do
                    case ${words[$i]} in
                        *) words[$i]="" ;;&
                        check) break ;;
                    esac
                done

                case ${words[*]} in
                    *\ -B\ *|*\ --shlibs\ *) ;&
                    *\ -d\ *|*\ --dependencies\ *) ;&
                    *\ -s\ *|*\ --checksums\ *) ;&
                    *\ -r\ *|*\ --recompute\ *)
                        case ${words[*]} in
                            *\ -a\ *|*\ --all\ *) ;;
                            *)
                                COMPREPLY+=($(compgen -W '-a --all' -- "$cur"))
                                COMPREPLY+=($(compgen -W '
                                        -C --case-sensitive
                                        -g --glob
                                        -i --case-insensitive
                                        -x --regex
                                        ' -- "$cur"))
                                ;;
                        esac
                        COMPREPLY+=($(compgen -W '
                            -n --dry-run
                            -q --quiet
                            -v --verbose
                            -y --yes
                            ' -- "$cur"))
                        ;;
                    *)
                        COMPREPLY+=($(compgen -W '
                            -B --shlibs
                            -d --dependencies
                            -s --checksums
                            -r --recompute
                            ' -- "$cur"))
                        ;;
                esac
                ;;

            clean)
                COMPREPLY=($(compgen -W '
                    -a --all
                    -n --dry-run
                    -q --quiet
                    -y --yes
                    ' -- "$cur"))
                ;;

            convert)
                case $prev in
                    -d|--pkg-dbdir)
                        compopt -o default
                        ;;
                    *)
                        COMPREPLY=($(compgen -W '
                            -d --pkg-dbdir
                            -n --dry-run
                            ' -- "$cur"))
                        ;;
                esac
                ;;

            create)
                # TODO
                ;;

            delete)
                # TODO
                ;;

            fetch)
                # TODO
                ;;

            help) ;;

            info)
                # TODO: Some options are mutually exclusive. It might be nice
                # to make this completion logic aware of this fact. At the
                # moment it just completes all the options.
                case "$prev" in
                    -F|--file)
                        compopt -o default
                        ;;
                    --raw-format)
                        COMPREPLY=($(compgen -W "${raw_output_formats[*]}" -- "$cur"))
                        ;;
                    *)
                        if [[ $cur == -* ]]; then
                            COMPREPLY=($(compgen -W '
                                -a --all
                                -A --annotations
                                -b --provided-shlibs
                                -B --required-shlibs
                                -C --case-sensitive
                                -d --dependencies
                                -D --pkg-message
                                -e --exists
                                -E --show-name-only
                                -f --full
                                -F --file
                                -g --glob
                                -i --case-insensitive
                                -I --comment
                                -k --locked
                                -l --list-files
                                -o --origin
                                -O --by-origin
                                -p --prefix
                                -q --quiet
                                -r --required-by
                                --raw-format
                                -R --raw
                                -s --size
                                -x --regex
                                ' -- "$cur"))
                        else
                            _pkg_complete_installed_package
                        fi
                        ;;
                esac
                ;;

            install)
                case "$prev" in
                    -r|--repository)
                        compopt -o default
                        ;;
                    *)
                        if [[ $cur == -* ]]; then
                            COMPREPLY=($(compgen -W '
                                -A --automatic
                                -C --case-sensitive
                                -f --force
                                -F --fetch-only
                                -g --globe
                                -i --case-insensitive
                                -I --no-install-scripts
                                -M --ignore-missing
                                -n --dry-run
                                -q --quiet
                                -r --repository
                                -R --recursive
                                -U --no-repo-update
                                -x --regex
                                -y --yes
                                ' -- "$cur"))
                        else
                            _pkg_complete_any_package
                        fi
                        ;;
                esac
                ;;

            lock)
                # TODO
                ;;

            plugins)
                # TODO
                ;;

            query)
                # TODO
                ;;

            register)
                # TODO
                ;;

            repo)
                # TODO
                ;;

            rquery)
                # TODO
                ;;

            search)
                # TODO
                case "$prev" in
                    -L|--label) ;&
                    -S|--search)
                        # See "Search and Label Options" in pkg-search(8).
                        COMPREPLY=($(compgen -W 'comment description name origin pkg-name' -- "$cur"))
                        ;;
                    -r|--repository)
                        # TODO: Could it be completed better?
                        compopt -o default
                        ;;
                    --raw-format)
                        COMPREPLY=($(compgen -W "${raw_output_formats[*]}" -- "$cur"))
                        ;;
                    -Q|--query-modifier)
                        # See "Output Modifier Options" in pkg-search(8).
                        COMPREPLY=($(compgen -W 'annotations arch categories
                            comment description full licenses maintainer name
                            options pkg-size prefix repository required-by
                            shared-libs-required shared-libs-provided size url
                            version www' -- "$cur"))
                        ;;
                    *)
                        COMPREPLY=($(compgen -W '
                            -c --comment
                            -C --case-sensitive
                            -d --depends-on
                            -D --description
                            -e --exact
                            -f --full
                            -g --glob
                            -i --case-insensitive
                            -L --label
                            -o --origins
                            -p --prefix
                            -q --quiet
                            -Q --query-modifier
                            -r --repository
                            --raw-format
                            -R --raw
                            -s --size
                            -S --search
                            -U --no-repo-update
                            -x --regex
                            ' -- "$cur"))
                        ;;
                esac
                ;;

            set)
                # TODO
                ;;

            shell)
                # TODO
                ;;

            shlib)
                # TODO
                ;;

            stats)
                # TODO
                ;;

            unlock)
                # TODO
                ;;

            update)
                # TODO
                ;;

            updating)
                case "$prev" in
                    -d|--data)
                        case ${#cur} in
                            [0-1])
                                cur=20
                                ;&
                            [2-3])
                                # Year.
                                COMPREPLY=($(compgen -W "$(seq 2008 1 $(date +%Y))" -- "$cur"))
                                ;;
                            [4-5])
                                # Month.
                                COMPREPLY=($(compgen -W "$(echo ${cur:0:4}{01..12})" -- "$cur"))
                                ;;
                            [6-7]|8)
                                # Day.
                                case "${cur:4:2}" in
                                    02)
                                        COMPREPLY=($(compgen -W "$(echo ${cur:0:6}{01..28})" -- "$cur"))
                                        # Detect a leap year.
                                        if [[ ${cur:2:2} =~ [13579][26]|[02468][048] ]]; then
                                            COMPREPLY+=($(compgen -W "${cur:0:6}29" -- "$cur"))
                                        fi
                                        ;;
                                    0[13578]|1[02])
                                        COMPREPLY=($(compgen -W "$(echo ${cur:0:6}{01..31})" -- "$cur"))
                                        ;;
                                    *)
                                        COMPREPLY=($(compgen -W "$(echo ${cur:0:6}{01..30})" -- "$cur"))
                                        ;;
                                esac
                                ;;
                        esac
                        ;;
                    -f|--file)
                        compopt -o default
                        ;;
                    *)
                        if [[ $cur == -* ]]; then
                            COMPREPLY=($(compgen -W '
                                -d --date
                                -f --file
                                -i --case-insensitive
                                ' -- "$cur"))
                        else
                            _pkg_complete_any_package
                        fi
                        ;;
                esac
                ;;

            upgrade)
                case "$prev" in
                    -r|--repository)
                        # TODO: Could it be completed better?
                        compopt -o default
                        ;;
                    *)
                        if [[ $cur == -* ]]; then
                            COMPREPLY=($(compgen -W '
                                -C --case-sensitive
                                -f --force
                                -F --fetch-only
                                -g --glob
                                -i --case-insensitive
                                -n --dry-run
                                -q --quiet
                                -r --repository
                                -U --no-repo-update
                                -y --yes
                                ' -- "$cur"))
                        else
                            _pkg_complete_installed_package
                        fi
                        ;;
                esac
                ;;

            version)
                # TODO
                ;;

            which)
                if [[ $cur == -* ]]; then
                    COMPREPLY=($(compgen -W '
                        -g --glob
                        -m --show-match
                        -o --origin
                        -p --path-search
                        -q --quiet
                        ' -- "$cur"))
                else
                    compopt -o default
                fi
                ;;
        esac
    fi
} &&
complete -F _pkg pkg
