#compdef gpaste-client
#
# SPDX-FileCopyrightText: 2010-2026 Marc-Antoine Perennou <Marc-Antoine@Perennou.com>
# SPDX-License-Identifier: BSD-2-Clause

# Is a daemon already there to answer us? Completion must never be what
# activates one: D-Bus would start it behind the user's back and block the shell
# until it finished loading the history.
(( $+functions[_gpaste-client_daemon_running] )) ||
_gpaste-client_daemon_running() {
    local answer

    answer=$(gdbus call --session --dest org.freedesktop.DBus \
                                  --object-path /org/freedesktop/DBus \
                                  --method org.freedesktop.DBus.NameHasOwner org.gnome.GPaste 2>/dev/null)

    [[ ${answer} == *true* ]]
}

# One file per history, named <history>.<extension>. Listing them off disk keeps
# history completion working (and instant) with no daemon running at all.
(( $+functions[_gpaste-client_histories] )) ||
_gpaste-client_histories() {
    local dir=${XDG_DATA_HOME:-${HOME}/.local/share}
    local -a names

    # The daemon stores under PACKAGE_NAME ("GPaste") when that directory
    # already exists, and under PACKAGE ("gpaste") otherwise.
    if [[ -d ${dir}/GPaste ]]; then
        dir=${dir}/GPaste
    else
        dir=${dir}/gpaste
    fi

    names=(${dir}/*.(xml|xmls|db|dbs)(N:t:r))
    names=(${(u)names})

    _describe -t histories 'history' names
}

# "<uuid>: <item>" lines, which is exactly the value:description form _describe
# wants, so the item's text shows up next to its uuid. gpaste-client reads stdin
# whenever it is not a tty (that is how "foo | gpaste-client" works), so feed it
# /dev/null: completion is not always run with a terminal on stdin, and it would
# otherwise block waiting for EOF.
(( $+functions[_gpaste-client_uuids] )) ||
_gpaste-client_uuids() {
    local -a items

    _gpaste-client_daemon_running || return 1

    items=(${(f)"$(gpaste-client history --oneline </dev/null 2>/dev/null)"})

    _describe -t items 'item' items
}

# With --use-index the item arguments are indexes into the history, not uuids.
(( $+functions[_gpaste-client_indexes] )) ||
_gpaste-client_indexes() {
    local -a items

    _gpaste-client_daemon_running || return 1

    items=(${(f)"$(gpaste-client history --oneline --use-index </dev/null 2>/dev/null)"})

    _describe -t indexes 'index' items
}

(( $+functions[_gpaste-client_subcommands] )) ||
_gpaste-client_subcommands() {
    local -a subcommands

    subcommands=(
        about:"Display the about dialog"
        {add,a}:"Set text to clipboard"
        {add-password,ap}:"Add a name / password pair to the clipboard"
        {backup-history,bh}:"Back up the current history"
        change-passphrase:"Change the passphrase of the encrypted history"
        {daemon-reexec,dr}:"Re-execute the daemon"
        {daemon-version,dv}:"Display the daemon version"
        {delete,del,d,remove,rm}:"Delete an element of the history"
        {delete-history,dh}:"Delete a history"
        {delete-password,dp}:"Delete a password"
        {empty,e}:"Empty the history"
        {favourite,fav}:"Pin an item so the history never drops it automatically"
        {file,f}:"Put the content of a file into the clipboard"
        {get,g}:"Display an element of the history"
        {get-history,gh}:"Get the name of the current history"
        help:"Display the help"
        {history,h}:"Display the history"
        {history-size,hs}:"Display the size of the current history"
        {list-histories,lh}:"List available histories"
        {make-password,mp}:"Make an item a password, or update one that already is"
        {merge,m}:"Merge various elements from the history"
        migrate:"Migrate the history to a different storage backend"
        replace:"Replace the contents of an item"
        search:"Search the history"
        {select,set,s}:"Select an element of the history"
        {settings,preferences,p}:"Launch the configuration tool"
        show-history:"Make the GNOME Shell extension display the history"
        {start,daemon,d}:"Start tracking clipboard changes"
        {stop,quit,q}:"Stop tracking clipboard changes"
        {unfavourite,unfav}:"Unpin an item, letting the history drop it again"
        {switch-history,sh}:"Switch to another history"
        ui:"Launch the graphical tool"
        {upload,u}:"Upload an item to a pastebin service"
        {version,v}:"Display the version"
    )

    _describe -t commands 'gpaste-client subcommand' subcommands
}

(( $+functions[_gpaste-client_arguments] )) ||
_gpaste-client_arguments() {
    # Which of the two an item argument is spelled as depends on --use-index.
    local item=_gpaste-client_uuids

    (( ${+opt_args[-i]} || ${+opt_args[--use-index]} )) && item=_gpaste-client_indexes

    case ${words[1]} in
        (empty|e|delete-history|dh|switch-history|sh)
            _arguments -s : \
                '1:history:_gpaste-client_histories'
            ;;
        (backup-history|bh)
            # <history> <backup name>, the history being optional.
            _arguments -s : \
                '1:history:_gpaste-client_histories' \
                '2:backup name:'
            ;;
        (file|f)
            _arguments -s : \
                '*:file:_files'
            ;;
        (get|g|select|set|s|delete|del|d|remove|rm|upload|u|favourite|fav|unfavourite|unfav)
            _arguments -s : \
                "1:item:${item}"
            ;;
        (replace)
            _arguments -s : \
                "1:item:${item}" \
                '2:contents:'
            ;;
        (make-password|mp)
            _arguments -s : \
                "1:item:${item}" \
                '2:name:'
            ;;
        (merge|m)
            _arguments -s : \
                "*:item:${item}"
            ;;
        (add|a)
            _arguments -s : \
                '1:text:'
            ;;
        (add-password|ap)
            _arguments -s : \
                '1:name:' \
                '2:password:'
            ;;
        (search)
            _arguments -s : \
                '1:pattern:'
            ;;
    esac
}

_gpaste-client() {
    local context state state_descr line curcontext="${curcontext}"
    typeset -A opt_args

    # getopt_long permutes, so every option is accepted anywhere on the line;
    # they are declared once here rather than per subcommand.
    _arguments -s -C : \
        '(-h --help)'{-h,--help}'[Display the help.]' \
        '(-v --version)'{-v,--version}'[Display the version.]' \
        '(-f --favourites)'{-f,--favourites}'[Only display the pinned items.]' \
        '(-i --use-index)'{-i,--use-index}'[Use the index of the item instead of its UUID.]' \
        '(-o --oneline)'{-o,--oneline}'[Display each item on one line.]' \
        '(-r --raw)'{-r,--raw}'[Display the raw item, without its UUID.]' \
        '(-e --reverse)'{-e,--reverse}'[Display the items in reverse order.]' \
        '(-z --zero)'{-z,--zero}'[Use a NUL character instead of a newline between each item.]' \
        '(-d --decoration)'{-d,--decoration}'[Add the decoration at the beginning and end of each item before merging.]:decoration:' \
        '(-s --separator)'{-s,--separator}'[Add a separator between each item when merging.]:separator:' \
        '(-t --timeout)'{-t,--timeout}'[How long a password stays on the clipboard, in seconds; 0 for as long as anything else.]:seconds:' \
        '1: :_gpaste-client_subcommands' \
        '*:: :->arguments' && return

    case ${state} in
        (arguments)
            curcontext="${curcontext%:*:*}:gpaste-client-${words[1]}"
            _gpaste-client_arguments
            ;;
    esac
}

_gpaste-client "${@}"
