# Bob build tool
# Copyright (C) 2016  TechniSat Digital GmbH
#
# SPDX-License-Identifier: GPL-3.0-or-later

# Generate a completion reply for a list of given words. By default a space is
# appended, though this might be changed.
#
# Arguments:
# 1: List of possible completion words
# 2: A prefix to be added to each possible completion word (optional)
# 3: A suffix to be appended to each possible completion word (optional, default: ' ').
__bob_complete_words()
{
   local c i=0 IFS=$' \t\n'
   for c in $1 ; do
      c="${2-}$c${3- }"
      if [[ $c == "$cur"* ]]; then
         COMPREPLY[i++]="$c"
      fi
   done
}

# Complete a directory while obeying "-C" parameters.
__bob_complete_dir()
{
   local IFS=$'\n'
   COMPREPLY=( $(for i in "${chroot[@]}" ; do eval cd "$i" || exit ; done
                 compgen -d -P "$2" -S / -- "$1" ) )
}

__bob_commands="build dev clean graph help init jenkins ls project status \
                query-scm query-recipe query-path query-meta show"

# Complete a Bob path
#
# We effectively call "bob ls" to get the list of possible completions. What
# makes this a bit complicated is that we have to use the same settings as the
# original command, namely the sandbox, config files and defines.
#
# To make things even more complicated we have to handle "-C" where we change
# the root directory. Apparently bash passes us the quoted or escaped strings
# so we have to use "eval" for tilde expansion and un-escaping. This feels
# really bad, though.
#
# BUG: "compgen" does not produce correctly escaped strings. This breaks file
# names with spaces. :'(
__bob_complete_path()
{
   local h i prefix result
   declare -a cmd_settings=( )
   declare -a global_settings=( )
   local IFS=$'\n'

   # Take over sandbox (--(no-)sandbox), config files (-c <file>, -c<file>) and
   # defines (-D FOO=bar, -DFOO=bar). They
   # influence parsing and must be passed to "bob ls".
   for i in "${words[@]}"; do
      case "$i" in
         --sandbox)
            sandbox="--sandbox"
            ;;
         --no-sandbox)
            sandbox="--no-sandbox"
            ;;
		 -c?* | -D?*)
		    cmd_settings+=( "$i" )
			;;
		 *)
		    case "$h" in
		       -c | -D)
			      cmd_settings+=( "$h" "$i" )
				  ;;
			esac
      esac
	  h="$i"
   done

   # Auto complete config files. They can be in directories and their '.yaml'
   # suffix is implicitly added by Bob. The file name might directly start
   # after '-c' making it a bit more complicated.
   if [[ $prev = "-c" || $cur = -c?* ]] ; then
	  if [[ $cur = -c?* ]] ; then
	     prefix="-c"
		 cur="${cur:2}"
	  else
		 prefix=
	  fi
      __bob_complete_dir "$cur" "$prefix"
	  for i in $(for i in "${chroot[@]}" ; do eval cd "$i" || exit ; done
                 compgen -f -P "$prefix" -S " " -X "!*.yaml" -- "$cur" )
      do
         COMPREPLY+=( "${i%.yaml }" )
	  done
   else
      case "$cur" in
         -*)
            __bob_complete_words "-h --help $1"
            ;;
         *)
            if [[ $cur == */* ]] ; then
               prefix="${cur%/*}/"
            else
               prefix=""
            fi
            for i in "${chroot[@]}" ; do global_settings+=( "-C" "$i" ) ; done
            result="$(eval $bob --color=never "${global_settings[@]}" ls $sandbox "${cmd_settings[@]}" $prefix 2>/dev/null)"
            __bob_complete_words "$result" "$prefix" "/"
            ;;
      esac
   fi
}

__bob_clean()
{
   __bob_complete_words "--attic -c -D --develop --dry-run -h --help --release -s --src -v --verbose"
}

__bob_cook()
{
   if [[ "$prev" = "--destination" ]] ; then
      __bob_complete_dir "$cur"
   elif [[ "$prev" = "--download" ]] ; then
         __bob_complete_words "yes no deps forced forced-deps forced-fallback"
   elif [[ "$prev" = "--download-layer" ]] ; then
         __bob_complete_words "yes= no= forced="
   elif [[ "$prev" = "--always-checkout" ]] ; then
      COMPREPLY=( )
   else
      __bob_complete_path "--destination -j --jobs -k --keep-going -f --force -n --no-deps -p --with-provided --without-provided -A --no-audit --audit -b --build-only -B --checkout-only --normal --clean --incremental --always-checkout --resume -q --quiet -v --verbose --no-logfiles -D -c -e -E -M --upload --link-deps --no-link-deps --download --download-layer --shared --no-shared --install --no-install --sandbox --no-sandbox --clean-checkout"
   fi
}

__bob_build()
{
   sandbox="--sandbox"
   __bob_cook "$@"
}

__bob_dev()
{
   sandbox="--no-sandbox"
   __bob_cook "$@"
}

__bob_graph()
{
   if [[ "$prev" = "-t" || "$prev" = "--type" ]] ; then
         __bob_complete_words "d3 dot"
   else
      __bob_complete_path "-c -D -e --exclude -f --filename -H --highlight -n --max-depth -t --type -o --sandbox --no-sandbox"
   fi
}

__bob_help()
{
    __bob_complete_words "$__bob_commands"
}

__bob_ls()
{
   __bob_complete_path "-a --all -c -D -d --direct --no-sandbox -o --origin -p --prefixed -r --recursive --sandbox -u --unsorted"
}

__bob_init()
{
   case "$cur" in
      -*)
         __bob_complete_words "-h --help"
         ;;
      *)
         __bob_complete_dir "$cur"
         ;;
   esac
}

__bob_jenkins_add()
{
   sandbox="--sandbox"

   case "$cur" in
      -*)
         __bob_complete_words "--clean --credentials --download --help --keep --longdescription --no-sandbox --nodes --prefix --root --shortdescription --upload --windows -D -h -n -o -p -r -w"
         ;;
      *)
         case "$prev" in
            -r|--root)
               __bob_complete_path
               ;;
            -o)
               __bob_complete_words "artifacts.copy jobs.isolate jobs.policy scm.git.shallow scm.git.timeout scm.ignore-hooks scm.poll shared.dir" "" "="
               ;;
         esac
   esac
}

__bob_jenkins_export()
{
   local c jenkins dir

   while [[ $parse_pos -lt $COMP_CWORD ]] ; do
      c="${COMP_WORDS[parse_pos]}"
      : $((parse_pos++))
      case "$c" in
         -*) ;;
         *)
            if [[ -z $jenkins ]] ; then
               jenkins="$c"
            elif [[ -z $dir ]] ; then
               dir="$c"
            fi
            ;;
      esac
   done

   case "$cur" in
      -*)
         __bob_complete_words "-h --help"
         ;;
      *)
         if [[ -z $jenkins ]] ; then
            __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
         elif [[ -z $dir ]] ; then
            __bob_complete_dir "$cur"
         fi
         ;;
   esac
}

__bob_jenkins_ls()
{
   __bob_complete_words "-h --help -v --verbose"
}

__bob_jenkins_prune()
{
   case "$cur" in
      -*)
         __bob_complete_words "-h --help --intermediate --no-ssl-verify --obsolete -q --quiet -v --verbose"
         ;;
      *)
         __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
         ;;
   esac
}

__bob_jenkins_push()
{
   case "$cur" in
      -*)
         __bob_complete_words "-f --force -h --help --no-ssl-verify --no-trigger -q --quiet -v --verbose"
         ;;
      *)
         __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
         ;;
   esac
}

__bob_jenkins_rm()
{
   case "$cur" in
      -*)
         __bob_complete_words "-h --help -f --force"
         ;;
      *)
         __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
         ;;
   esac
}

__bob_jenkins_set_url()
{
   case "$cur" in
      -*)
         __bob_complete_words "-h --help"
         ;;
      *)
         __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
         ;;
   esac
}

__bob_jenkins_set_options()
{
   sandbox="--no-sandbox"

   case "$cur" in
      -*)
         __bob_complete_words "-h --help --reset -n --nodes -o -p --prefix --add-root --del-root -D -U --credentials --authtoken --shortdescription --longdescription --keep --no-keep --download --no-download --upload --no-upload --sandbox --no-sandbox --clean --incremental"
         ;;
      *)
         case "$prev" in
            --add-root)
               __bob_complete_path
               ;;
            -n|--nodes|-p|--prefix|-D|-U|--credentials|--authtoken)
               COMPREPLY=( )
               ;;
            -o)
               __bob_complete_words "artifacts.copy jobs.isolate scm.git.shallow scm.git.timeout scm.ignore-hooks scm.poll shared.dir" "" "="
               ;;
            *)
               __bob_complete_words "$($bob --color=never jenkins ls 2>/dev/null)"
               ;;
         esac
   esac
}

__bob_jenkins()
{
   __bob_subcommands "add export ls prune push rm set-url set-options" "jenkins"
}

__bob_project()
{
   local i c command completion_func

   while [[ $parse_pos -lt $COMP_CWORD ]] ; do
      c="${COMP_WORDS[parse_pos]}"
      : $((parse_pos++))
      case "$c" in
         -D | -c | -e | --download | -j)
            : $((parse_pos++))
            ;;
         -*) ;;
         *) command="$c" ; break ;;
      esac
   done

   if [[ "$prev" = "--download" ]] ; then
         __bob_complete_words "yes no deps"
   elif [[ -z "$command" ]] ; then
      case "$cur" in
         -*)
            __bob_complete_words "-b -c -D --download -E -e -j --list -n --no-sandbox --resume --sandbox"
            ;;
         *)
            __bob_complete_words "$($bob --color=never project --list 2>/dev/null)"
            ;;
      esac
   else
       __bob_complete_path "--help"
   fi
}

__bob_query_scm()
{
    __bob_complete_path "-c -D -f --default -r --recursive --sandbox --no-sandbox"
}

__bob_query_path()
{
  __bob_complete_path "-f -D -c --sandbox --no-sandbox --develop --release"
}

__bob_query_meta()
{
  __bob_complete_path " -c -D -r --recursive --sandbox --no-sandbox"
}

__bob_query_recipe()
{
    __bob_complete_path "-c -D --sandbox --no-sandbox"
}

__bob_status()
{
  __bob_complete_path "--attic --develop --recursive --no-sandbox --release --sandbox --show-clean --show-overrides --verbose -D -c -r -v"
}

__bob_show()
{
    if [[ "$prev" = "--format" ]] ; then
        __bob_complete_words "yaml json flat diff"
    elif [[ "$prev" = "-f" ]] ; then
        __bob_complete_words "buildNetAccess buildTools buildToolsWeak
            buildVars buildVarsWeak checkoutAssert checkoutDeterministic
            checkoutSCM checkoutTools checkoutToolsWeak checkoutVars
            checkoutVarsWeak depends fingerprintIf jobServer meta
            metaEnvironment packageNetAccess packageTools packageToolsWeak
            packageVars packageVarsWeak relocatable root sandbox scriptLanguage
            shared"
    elif [[ "$prev" = "--indent" ]] ; then
        COMPREPLY=( )
    else
        __bob_complete_path "-D -c --sandbox --no-sandbox --show-empty
            --show-common --indent --no-indent --format -f"
    fi
}

__bob_subcommands()
{
   local i c command completion_func
   declare -a chroot

   while [[ $parse_pos -lt $COMP_CWORD ]] ; do
      c="${COMP_WORDS[parse_pos]}"
      : $((parse_pos++))
      case "$c" in
         -C)
            if [[ $parse_pos -lt $COMP_CWORD ]] ; then
               chroot+=( "${COMP_WORDS[parse_pos]}" )
               : $((parse_pos++))
            fi
            ;;
         -C?*)
            chroot+=( "${c:2}" )
            ;;
         -*) ;;
         *) command="$c" ; break ;;
      esac
   done

   if [[ -z "$command" ]] ; then
      case "$cur" in
         -C?*)
            __bob_complete_dir "${cur:2}" "-C"
            ;;
         -*)
            __bob_complete_words "-h --help -i --version -C"
            ;;
         *)
            if [[ $prev == "-C" ]] ; then
               __bob_complete_dir "$cur"
            else
               __bob_complete_words "$1"
            fi
            ;;
      esac
   else
      local completion_func="__bob_${2-}${2+_}${command//-/_}"
      declare -f $completion_func >/dev/null && $completion_func
   fi
}

if [[ -n ${ZSH_VERSION} ]]; then
# Top level completion function for zsh.
__bob()
{
    local parse_pos=1 bob="$1" cur="$2" prev="$3"
    local sandbox=""
    local words=( "${COMP_WORDS[@]}" )

   __bob_subcommands "$__bob_commands"
}
else
# Top level completion function for bash.
__bob()
{
    local parse_pos=1 bob="$1" cur prev words
    local sandbox=""

   _get_comp_words_by_ref -n ':=' cur prev words

   __bob_subcommands "$__bob_commands"

   __ltrim_colon_completions "$cur"
}
fi

# noquote is quite new...
complete -o noquote -o nospace -F __bob bob 2>/dev/null || \
   complete -o nospace -F __bob bob
