# 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

    readarray command_list < <(pkg -l)

    local -A alias_map
    local _alias _command
    while read _alias _command _; do
        alias_map["$_alias"]="${_command//\'}"
    done < <(pkg alias -q)
    command_list+=("${!alias_map[@]}")

    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 [[ -n "$word" && -n "${alias_map["$word"]}" ]]; then
            word="${alias_map["$word"]}"
        fi
        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
                    compopt -o default
                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: This implementation covers only a subset of
                # pkg-create(8)'s functionality.
                _pkg_complete_installed_package
                ;;

            delete)
                # 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.
		if [[ $cur == -* ]]; then
			COMPREPLY=($(compgen -W '
			-a --all
			-C --case-sensitive
			-D --no-scripts
			-f --force
			-g --glob
			-i --case-insensitive
			-n --dry-run
			-q --quiet
			-R --recursive
			-x --regex
			-y --yes
			' -- "$cur"))
		else
			_pkg_complete_installed_package
		fi
                ;;

            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
