#!/usr/bin/env bash

# This script will install the necessary configuration for zsh and bash completion
#
# Sebastian Bachmann <hello@reox.at>, 2016

ask() {
	# http://djm.me/ask
	local prompt default REPLY

	while true; do

		if [ "${2:-}" = "Y" ]; then
			prompt="Y/n"
			default=Y
		elif [ "${2:-}" = "N" ]; then
			prompt="y/N"
			default=N
		else
			prompt="y/n"
			default=
		fi

		# Ask the question (not using "read -p" as it uses stderr not stdout)
		echo -n "$1 [$prompt] "

		# Read the answer (use /dev/tty in case stdin is redirected from somewhere else)
		read REPLY < /dev/tty

		# Default?
		if [ -z "$REPLY" ]; then
			REPLY=$default
		fi

		# Check if the reply is valid
		case "$REPLY" in
		Y* | y*) return 0 ;;
		N* | n*) return 1 ;;
		esac

	done
}

if ask "Install for zsh?" Y; then

	if [[ -f ~/.zshrc ]]; then
		if ask "Create a backup of .zshrc?" Y; then
			cp -i ~/.zshrc ~/.zshrc.bak_kdb
		fi
	fi

	cat << HERE >> ~/.zshrc
### generated by kdb install-sh-completion
fpath=(~/.zsh-comp $fpath)
autoload -U compinit && compinit
zmodload -i zsh/complist
### end of generated by kdb install-sh-completion
HERE

	if [ ! -d ~/.zsh-comp ]; then
		mkdir ~/.zsh-comp
	fi

	cat << 'HERE' > ~/.zsh-comp/_kdb
#compdef kdb
# This file needs to be renamed to _kdb to work and must be in the $fpath
# It must start with #compdef kdb
#
# ZSH completion file for KDB
#
# Sebastian Bachmann <hello@reox.at>, 2017

_kdb_paths () {
    # Function to complete a KDB path by using `kdb complete`

    typeset -a paths
    # The current user input is saved in $PREFIX
    # we can use kdb complete -M 1 $PREFIX to look up new paths
    #
    # as the _values function already completes with a seperator,
    # we need to remove trailing slashes from the list.
    paths=($(kdb complete -M 1 $PREFIX | sed 's#/$##'))

    # Check if kdb complete got any result at all
    if [ $#paths -eq 0 ]; then
        _message "No key to complete"
        return
    fi

    # Check if the supplied path completes to a leaf:
    kdb complete $PREFIX | grep -q -e '/$'
    if [ $? -eq 1 ] && [ $#paths -eq 1 ]; then
        # Leaf
        _message "$PREFIX is a leaf."
        # If we have a leaf, we do not add the / seperator but a whitespace
        _values 'kdb path' $paths
        return
    else
        # Otherwise we add a / seperator
        _values -s '/' 'kdb path' $paths
    fi
}

# These are the functions for each command
_kdb-plugin-check () {
    # TODO [plugin]
}

_kdb-convert () {
    # TODO [import format] [export format] [import file] [export file]
}

_kdb-cp () {
    # TODO src dst
    _kdb_paths
}

_kdb-export () {
    # TODO src [format]
    _kdb_paths
}

_kdb-file () {
    # TODO path
    _kdb_path
}

_kdb-fstab () {
    # TODO key-path fs_spec fs_file fs_vfstype fs_mntops
    _kdb_path
}

_kdb-get () {
    # TODO disallow multiple kdb paths
    _kdb_paths
}

_kdb-meta-get () {
    # TODO key-name metaname
    _kdb_paths
}

_kdb-import () {
    # TODO dst [format]
    _kdb_paths
}

_kdb-plugin-info () {
    # TODO plugin [clause name]
}

_kdb-plugin-list () {
    _message -e "No Arguments for list"
    return
}

_kdb-ls () {
    # TODO disallow multiple kdb paths
    _kdb_paths
}

_kdb-meta-ls () {
    _kdb_paths
}

_kdb-merge () {
    # TODO ourpath theirpath basepath resultpath
}

_kdb-mount () {
    # TODO [path mountpoint] [plugin config] ...
}

_kdb-mv () {
    # TODO src dst
    _kdb_paths
}

_kdb-remount () {
    # TODO new-path new-mountpoint existing-mountpoint
    _kdb_paths
}

_kdb-rm () {
    # TODO single path
    _kdb_paths
}

_kdb-set () {
    # TODO after a path comes something else
    _kdb_paths
}

_kdb-meta-set () {
    # TODO key-name metaname metavalue
    _kdb_paths
}

_kdb-sget () {
    # TODO single path and default value
    _kdb_paths
}

_kdb-shell () {
    _message -e "No Arguments for shell"
    return
}

_kdb-test () {
    # TODO path [testname ...]
    _kdb_paths
}

_kdb-umount () {
    # TODO name
    _kdb_paths
}

_kdb-help () {
    # TODO can we get the command list all over again?
}

_kdb-list-tools () {
    _message -e "No Arguments for list-tools"
    return
}


# This function handles the commands of kdb and passes them
# to the function which actually shows the options.

_kdb_commands () {
    # TODO Copy&Paste from git completion
    # I have some clue what is going on here, but need to rewrite that someday

    local -a cmdtypes
    cmdtypes=(main_commands)

    local -a $cmdtypes

    main_commands=(
        check:'Do some basic checks on a plugin.'
        convert:'Convert configuration.'
        cp:'Copy keys within the key database.'
        export:'Export configuration from the key database.'
        file:'Prints the file where a key is located.'
        fstab:'Create a new fstab entry.'
        get:'Get the value of an individual key.'
        getmeta:'Get a metavalue.'
        import:'Import configuration to the key database.'
        info:'Print information about a plugin.'
        list:'List available plugins.'
        ls:'List the names of keys below a given name.'
        lsmeta:'Get all meta information of an individual key.'
        merge:'Three-way merge of KeySets.'
        mount:'Mount a new backend.'
        mv:'Move configuration within the key database.'
        remount:'Remount an existing backend with a different filename.'
        rm:'Remove key(s) from key database.'
        set:'Set the value of an individual key.'
        setmeta:'Set a metavalue.'
        sget:'Get the value of an individual key within a shell script.'
        shell:'Start a kdb shell.'
        test:'Run key database test suite.'
        umount:'Unmount backend from key database.'
        help:'View the man page of a tool'
        list-tools:'List all external tools')

  zstyle -a :completion:$curcontext: user-commands user_commands

  local -a aliases
  aliases=()
  local cmdtype len dup sep
  local -a allcmds allmatching alts disp expl

  zstyle -s ":completion:${curcontext}:" list-separator sep || sep=--
  for cmdtype in $cmdtypes aliases; do
    if [[ $cmdtype = aliases ]]; then
      for dup in ${${aliases%:*}:*allcmds}; do
	aliases=( ${aliases:#$dup:*} )
      done
    fi
    local -a ${cmdtype}_m
    set -A ${cmdtype}_m ${(P)cmdtype%%:*}
    allcmds+=( ${(P)${:-${cmdtype}_m}} )
  done
  zstyle -T ":completion:${curcontext}:" verbose && disp=(-ld '${cmdtype}_d')
  _description '' expl '' # get applicable matchers
  compadd "$expl[@]" -O allmatching -a allcmds
  len=${#${(O)allmatching//?/.}[1]} # length of longest match
  for cmdtype in aliases $cmdtypes; do
    local -a ${cmdtype}_d
    (( $#disp )) && set -A ${cmdtype}_d \
        ${${(r.COLUMNS-4.)${(P)cmdtype}/(#s)(#m)[^:]##:/${(r.len.)MATCH[1,-2]} $sep }%% #}
    alts+=( "${cmdtype//_/-}:${${cmdtype//_/ }%%(e|)s}:compadd ${(e)disp} -a ${cmdtype}_m" )
  done

  _alternative $alts
}


# Main Function
_kdb () {
    local curcontext=$curcontext state line

    integer ret=1

    _arguments -C \
                   '-H[show the man page]'\
                   '--help[show the man page]'\
                   '-V[Print version info]'\
                   '--version[Print version info]'\
                   '-v[Explain what is happening]'\
                   '--verbose[Explain what is happening]'\
                   '-C[Print never/auto(default)/always colored output]:when'\
                   '--color=-[Print never/auto(default)/always colored output]: :'\
                   '-p[Use a different kdb profile]:profile'\
                   '--profile=-[Use a different kdb profile]: :'\
                   '(-): :->command' \
                   '(-)*:: :->option-or-argument' && return

    case $state in
        (command)
            _kdb_commands
        ;;
        (option-or-argument)
            curcontext=${curcontext%:*:*}:kdb-$words[1]:

            if ! _call_function ret _kdb-$words[1]; then
                if zstyle -T :completion:$curcontext: use-fallback; then
                    _default && ret=0
                else
                    _message "unknown sub-command: $words[1]"
                fi
            fi
        ;;

    esac

    return
}

_kdb

HERE

	echo "installed zsh completion configuration"
fi

if ask "Install for bash?" Y; then
	if [[ -f ~/.bashrc ]]; then
		if ask "Create a backup of .bashrc?" Y; then
			cp -i ~/.bashrc ~/.bashrc.bak_kdb
		fi
	fi

	cat << 'HERE' >> ~/.bashrc
### generated by kdb install-sh-completion
#!/usr/bin/env bash

###########################################################################
##
## This script sets up basic bash completion for the kdb command
##
##
## Put it under /etc/bash_completion.d or load it just for the current
## session with . <scriptname>
##
###########################################################################

_kdb() {
	COMPREPLY=()

	# assign the currently active word
	local cur="${COMP_WORDS[COMP_CWORD]}"
	local prev="${COMP_WORDS[COMP_CWORD - 1]}"

	# initialize
	local kdbpath=$(which kdb)

	# only kdb was entered yet, print a list of available commands
	if [[ $COMP_CWORD -eq 1 ]]; then
		local IFS=$'\n'
		local commands=($(${kdbpath} list-commands))
		COMPREPLY=($(compgen -W '${commands[@]}' -- "${cur}"))
		return
	fi

	# commands which expects a path as next parameter
	local pathcommands="export file get getmeta cp ls lsmeta mv rm set setmeta sget"
	if [[ $pathcommands =~ $prev ]]; then
		local IFS=$'\n'
		local paths=($( (${kdbpath} ls system && ${kdbpath} ls user) 2> /dev/null))
		COMPREPLY=($(compgen -W '${paths[@]}' -- "${cur}"))

		# Now escape special characters (spaces, single and double quotes),
		# so that the argument is really regarded a single argument by bash.
		# See http://stackoverflow.com/questions/1146098/properly-handling-spaces-and-quotes-in-bash-completion
		local i=0
		for entry in ${COMPREPLY[*]}; do
			if [[ ${cur:0:1} == "'" ]]; then
				# started with single quote, escaping only other single quotes
				# [']bla'bla"bla\bla bla --> [']bla'\''bla"bla\bla bla
				local escaped_single_quote="'\''"
				COMPREPLY[$i]="${entry//\'/${escaped_single_quote}}"
			elif [[ ${cur:0:1} == '"' ]]; then
				# started with double quote, escaping all double quotes and all backslashes
				# ["]bla'bla"bla\bla bla --> ["]bla'bla\"bla\\bla bla
				entry="${entry//\\/\\\\}"
				COMPREPLY[$i]="${entry//\"/\\\"}"
			else
				# no quotes in front, escaping _everything_
				# [ ]bla'bla"bla\bla bla --> [ ]bla\'bla\"bla\\bla\ bla
				entry="${entry//\\/\\\\}"
				entry="${entry//\'/\'}"
				entry="${entry//\"/\\\"}"
				COMPREPLY[$i]="${entry// /\\ }"
			fi
			((i++))
		done

		return
	fi
}

# complete the command with _kdb and fall back to filename completion
complete -o default -F _kdb kdb

### end of generated by kdb install-sh-completion
HERE
	echo "installed bash completion configuration"
fi
