#!/bin/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
#
# ZSH completion file for KDB
#
# Sebastian Bachmann <hello@reox.at>, 2016

_kdb_paths () {
    # It seems this is enough to get all keys into the compsystem

    # TODO
    # Some paths only have a single possible completion.
    # like if there is no other path starting with s/foo/bar and 
    # s/foo/bar/sdf/baz/bar/xxx is the only leaf, we can complete for this directly

    typeset -a paths
    # Count the number of slashes to know where we are
    # TODO this seems to be not working in a zsh without configuratin
    # e.g. my configuration does something weird.
    # it should be ${#PREFIX//[^\/]/}
    slashes=${#PREFIX//[^\\\/]/}
    if [ $slashes -eq 0 ]; then
        # If no slashes, we perfom search on /
        paths=($(kdb ls / | cut -d "/" -f 1 | uniq))
    else
        # Check if key is a leaf
        # TODO how we can get rid of the trailing / then?
        kdb ls / | grep -q -e "^${PREFIX}$"
        if [ $? -eq 0 ]; then
            _message "$PREFIX is a leaf."
            return
        fi
        kdb ls / | grep -q -e "^$PREFIX"
        if [ $? -ne 0 ]; then
            # TODO need to check here for the key w/o /. 
            # probably move into a function
            kdb ls / | grep -q -e "^${PREFIX:0:-1}$"
            if [ $? -eq 0 ]; then
                _message "${PREFIX:0:-1} is a leaf."
                return
            fi
            _message -e "Could not find $PREFIX as a valid path"
        fi

        # We need to check if the last char is a /
        # If not, we remove the garbled part at the end and search from there.
        if [[ ${PREFIX[-1]} == "/" ]]; then
            paths=($(kdb ls $PREFIX | cut -d "/" -f -$(( slashes + 1 )) ))
        else
            pre=$(cut -d "/" -f -$slashes <<< $PREFIX)
            paths=($(kdb ls $pre | cut -d "/" -f -$(( slashes + 1 )) ))
        fi

    fi
    # TODO an approximate form would be nice, so we can correct for user input
    _values -s / 'kdb path' $paths
}

# These are the functions for each command
_kdb-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-getmeta () { 
    # TODO key-name metaname
    _kdb_paths
}

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

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

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

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

_kdb-lsmeta () { 
    _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-setmeta () { 
    # 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-vset () {
    # TODO path value regex [message]
    _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.'
        vset:'Set a value together with a validation regex.'
        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
###########################################################################
##
## 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} 2>/dev/null | sed -e '0,/^Known commands are/d' | awk '{print $1}' ) )
		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 vset"
	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

