#!/usr/bin/env bash
#clear
#export PS4='\e[90m+${LINENO} in ${#BASH_SOURCE[@]}>${FUNCNAME[0]}:${BASH_SOURCE[@]##*/} \e[0m'
#set -x

#echo "starting: $0 <LOG_LEVEL=$1>"
#This=$OOSH_DIR/ng/2c


### new.method

2c.get.functions()  # <script> <functionNameFilter> # lista all functions in a script without description
# functions are identified by someFunctionName( ) but mainly by the brackets ()
# they are parsed till the next curley bracket
# so that this detailed description is part of the grep
{
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  cat "$from" \
  | line.find "^[^ ]*$1[^ ]*\(\) " \
  | line.filter "^{" \

}

2c.get.function.with.documentation()  # <script> <functionNameFilter> # lista all functions in a script with description
# functions are identified by someFunctionName( ) but mainly by the brackets ()
# they are parsed till the next curley bracket
# so that this detailed description is part of the grep
{
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  cat "$from" \
  | line.find "^[^ ]*$1[^ ]*\(\) " "\{" \
  | line.filter "^{" \

  # | line.replace "$name\." \
  # | grep "$1"

  # | line.filter "^#" \
  # | sort \
}


2c.format.functions() { # <script> <functionNameFilter> # only used as pipe: e.g. 2c.get.functions | 2c.format.functions $script $filter
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  cat - \
  | line.replace "$" "${NO_COLOR}" \
  | line.replace "^#" "${GREEN}#" \
  | line.replace "$name\." "${CYAN}${name}${NO_COLOR}\." \
  | line.replace "\([^ ]\)()" "\1${NO_COLOR}" \
  | line.replace "<" "${YELLOW}<" \
  | line.replace ">" ">${GREEN}" \
  | line.replace "#" "${GREEN}#" \
  | line.filter "\.completion" \
  | line.filter "^private" 
}

2c.get.formated.function.description() {
    local from="$1"
    if [ -n "$1" ]; then
      from="$1"
    shift
    else
      from="$This"
    fi

    local name=$(basename $from)

    local filter="$1"
    if [ -n "$1" ]; then
      filter="$1"
      shift
    fi    

      2c.get.function.with.documentation $from ${filter} \
    | 2c.format.functions $from ${filter} \
    | line.split "#" \
    | line.split "'" \
    | line.unquote \
    | line.filter "^[^ ]*$" \
    | line.replace "^" "${GREEN}"
}

2c.function.completion() {
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  2c.get.functions "$from" "$@" \
  | line.replace "\([^ ]\)().*$" "\1" \
  | line.replace "^$name\." \
  | line.replace " {" \
  | line.filter "\.completion" \
  | line.filter "^private" \
  | line.filter "^#" \
  | sort

}

2c.test.absolutePathName() {
  this.absolutePathName "$@"
  echo "${CYAN}${RESULT}"
}

2c.get.function.declaration() {
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

 2c.get.functions "$from" "$1" \
  | grep "$1(" \
  | line.select 1 \
  | line.replace "\([^ ]\)()" "\1" \
  | line.replace " {" \
  | line.unify '#' " no name" " none" " please add a description" \
  | line.format FORMAT_PARSE_METHOD \
  | line.split "|" \
  | line.unquote \
  | line.add "'" >$CONFIG_PATH/current.method.env

  {
    echo declare -- SCRIPT="$from"
    echo declare -- CLASS="$name"
  } >>$CONFIG_PATH/current.method.env

  if [ "$LOG_LEVEL" -gt "3" ]; then
    cat $CONFIG_PATH/current.method.env
  fi
}


2c.get.function.parameter() {
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  2c.get.function.declaration "$from" "$1" 
  shift
  source $CONFIG_PATH/current.method.env

  echo $METHOD_PARAMETER \
  | line.parse.paramList.new "$@" >$CONFIG_PATH/result.env

  # cat $CONFIG_PATH/result.env \
  # | line.count \
  # | line.format "declare -i PARAMS=%d\n" >>$CONFIG_PATH/result.env


  cat $CONFIG_PATH/result.env >>$CONFIG_PATH/current.method.env

  if [ "$LOG_LEVEL" -gt "3" ]; then
    cat $CONFIG_PATH/result.env
  fi

}


2c.debug.parse.param() {
    cat - \
  | line.unify ':' "parameterName" "addDefaultValue" \

}

2c.completion.enable()  # <cmd> # enables the 2c completion on <cmd> 
# ${RED}Does not work, since complete is an internal bash command
# it only works inteactively or when being ${YELLOW}sourced${RED} as a command
{
  local cmd="$1"
  if [ -z "$1" ]; then
    cmd=' '
  else
    shift
  fi
  complete  -o nospace -o bashdefault -o default -F "_oo_completion" "$cmd" 
  RETURN="$1"
}

# oo.completion.enable.completion() {
#   compgen -d $OOSH_DIR/$1
# }

2c.completion() {
  compgen -f $OOSH_DIR/ | line.replace.sedquoted "${OOSH_DIR}/" 
}

2c.completion.discover() # <numberOfWords> <currretWord> <script> <?method> <?rest># 
#2c.completion.discover() # <?script> <?method> <?rest> # 
{
  local argc=$#    # nuber of arguments
  #printf "\n"
  #clear -x

  local word="$1"  # completion word number (important for parameter)
  shift
  debug.log "2c $* ${RED}$argc word:${word}" 


  local cur="$1"
  shift
  if [ "$cur" = "--" ]; then
    cur=""
  fi

  local script="$1"
  if [ -n "$1" ]; then
    shift
  else
    script="$OOSH_DIR/ng/2c"
  fi

  if [ "$script" = "2c" ]; then
    debug.log "2c $curr <?script:$1> -"

    script="$1"
    shift

    if [ "$script" = "-" ]; then
      2c.completion >$CONFIG_PATH/completion.result.txt
      debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
    else
      2c.completion | grep "^${script}" >$CONFIG_PATH/completion.result.txt
      debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
    fi


    local count=$( cat $CONFIG_PATH/completion.result.txt | line.count )
    completionResult=$(cat $CONFIG_PATH/completion.result.txt)    
    
    info.log "got ${RED}${count}${NO_COLOR} suggestions"
    # if [ $word -le 1 ]; then
    #   completionResult=$(cat $CONFIG_PATH/completion.result.txt)    
    # else 
    #   return 0
    # fi
    if [ $word -le 1 ]; then
      return 0
    fi
  fi

  local method="$1"
  info.log "2c ${YELLOW}<?script:$script> <?method:$method>${NO_COLOR} $*   ${RED}$argc $cur" 
  if [ -z "$completionResult" ]; then
    completionResult="$script"
  fi


  # if [ "$script" = "$completionResult" ]; then
  #   info.log "2c ${YELLOW}<?script:$completionResult> === <?script:$script>${NO_COLOR} $*   ${RED}$argc $cur" 
  # else 
  #   info.log "2c ${YELLOW}<?script:$completionResult> =!= <?script:$script>${NO_COLOR} $*   ${RED}$argc $cur" 
  #   return 0
  # fi  



  if [ -n "$1" ]; then shift ; fi

  info.log "2c completion.discover ${YELLOW}<?script:$script> <?method:$method>${NO_COLOR} $*   ${RED}$argc $cur"

  script=$( which $script )
  class=$(basename $script)

  filter=""
  if ! [ "$method" = "-" ]; then
    filter="$class.$method"
  fi

      # local cur prev opts;
    COMPREPLY=();
    # cur="${COMP_WORDS[COMP_CWORD]}";
    # prev="${COMP_WORDS[COMP_CWORD-1]}";



    info.log "finding functions in $script containing $method" 
      
    2c.get.functions $script ${filter} \
    | 2c.format.functions $script ${filter} \
    | line.replace "\.$method" ".${RED}${method}${NO_COLOR}"
    # 2c.format.functions $script ${filter} \
    #   | line.replace "\.$method" ".${RED}${method}${NO_COLOR}"
    
    #stat -L -c "%a %G %U" $CONFIG_PATH
    2c.function.completion $script ${filter} >$CONFIG_PATH/completion.result.txt
    debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"


    local count=$( cat $CONFIG_PATH/completion.result.txt | line.count )
    completionResult=$(cat $CONFIG_PATH/completion.result.txt)
    
    info.log "got ${RED}${count}${NO_COLOR} suggestions"
    if [ $count -le 1 ]; then
          echo ""
          2c.get.formated.function.description $script ${filter}
    fi
    if [ $word -le 1 ] || [ "$method" = "-" ]; then
      create.result 0 ${count}
      return 0
    else
       info.log "$method" "$word"
    fi

    # if [ $count -le 1 ]; then
    #   return 0
    # fi


    if private.call.custom.completion "$cur" "$class" "$method"; then return 0 ; fi
    #private.call.custom.completion "$cur" "$script" "$method" 
    # MKT: Here script is overridden if it is not "local" in
    # private.call.custom.completion

    debug.log "now checking for parameter" 
    2c.get.function.parameter "$script" "$method" "$@"

    source $CONFIG_PATH/current.method.env

    info.log "...: $@"
    echo "" #just new line and color reset

    # echo "${YELLOW}$METHOD_PARAMETER${NO_COLOR}" \
    #   | line.replace "[<?]*" \
    #   | line.split ">" \
    #   | line.unquote >/dev/tty

     cat $CONFIG_PATH/current.method.env \
       | line.find "PARAM_" \
       | line.replace "declare -- PARAM_" "" \
       | line.replace "=.*$" >$CONFIG_PATH/completion.parameter.txt


    parc=$(( $word - 2 ))

    #set -x
    PARAMETER_COMPLETION=( $( cat $CONFIG_PATH/completion.parameter.txt  ) ) 
    local currentParameter="${PARAMETER_COMPLETION[parc]}"


    # MKT: This was put directly into tty. Why not by logging?
    debug.log `cat $CONFIG_PATH/current.method.env \
      | line.find "PARAM_" \
      | line.replace "declare -- PARAM_" "${YELLOW}" \
      | line.replace "${currentParameter}" "${CYAN}${currentParameter}${YELLOW}"`
    
      # if [ -z "${currentParameter}" ]; then currentParameter="${PARAMETER_COMPLETION[parc-1]}"; fi
      # console.log "current parameter ${currentParameter}"




    if private.call.custom.completion "$cur" "$script" "$method" "${currentParameter}"; then
      create.result 0 ${count}
      return 0
    else
      private.call.custom.completion "$cur" "$script" "parameter" "${currentParameter}"
    fi
    debug.log "comp result: $? ${count}"
    #set +x
}


private.call.custom.completion() { # <cur> <script> <method> <parameter> # expects $method.completion to echo results
    debug.log "private.call.custom.completion $@"
    local cur="$1"
    local class="$2"
    local method="$3"
    if [ -n "$4" ]; then

      local parameter="$4"
      #local isOptional=$( echo $parameter | line.replace "OPTIONAL_" "" | line.replace "=.*$" )
      local isOptional=$( echo $parameter | line.replace "\?" "OPTIONAL_" | line.replace "=.*$" )
      parameter=$( echo $parameter | line.replace "OPTIONAL_"  )
      #parameter=".$parameter"
      parameterENV="PARAM_$4"
      #parameterENV="PARAM_$isOptional"
    fi
    info.log "private.call.custom.completion $parameter $parameterENV --- $@"

    # Very import to be local, else it overwrites from caller
    local script="$(which $class)"
    class=$(basename $script)
    if ! this.functionExists $class.start; then
      info.log "${GREEN}sourcing ${script}${NORMAL}"
      source $script completion.discover "$@"
    fi
    
    if this.functionExists $class.$method.completion$parameter; then
      $class.$method.completion$parameter "$cur" "$class" "$method" | grep "^$cur" >$CONFIG_PATH/completion.result.txt
      debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
      RETURN_VALUE=$?
      return $RETURN_VALUE
    fi

    if this.functionExists $class.parameter.completion$parameter; then
      $class.parameter.completion$parameter "$cur" "$class" "$method" | grep "^$cur" >$CONFIG_PATH/completion.result.txt
      debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
      RETURN_VALUE=$?
      return $RETURN_VALUE
    else
      debug.log "$class.$method.completion$parameter ${RED}does not exist${NO_COLOR}"

      source $CONFIG_PATH/current.method.env
      if [ -n "$4" ] && [ "$4" != "none" ]; then
        debug.log "$parameterENV=${!parameterENV}"
        echo "${!parameterENV}" >$CONFIG_PATH/completion.result.txt
        debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
        return 0
      fi
    fi
    
    #important.log "overwriting completion.result.txt"
    echo ";" >$CONFIG_PATH/completion.result.txt
    debug.log "2c:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
    return 1
}

2c.files.completion() { 
  important.log "2c.files.completion $*"
  if [ -z "$2" ]; then
    cd "$2"
  fi
  compgen -f  "$2$1" 
}

2c.env() { 
  echo "exported ENV variables: $*"
  echo "$1=${!1}"
}
2c.env.completion() { 
  compgen -e  "$1" 
}

2c.user() { 
  echo "$*"
}
2c.user.completion() { 
  compgen -u  "$1" 
}

2c.groups() { 
  echo "$*"
}
2c.groups.completion() { 
  compgen -g  "$1" 
}

2c.buildinCommands() { 
  echo "$*"
}
2c.buildinCommands.completion() { 
  compgen -c  "$1" 
}

2c.jobs() { 
  echo "$*"
}
2c.jobs.completion() { 
  compgen -j  "$1" 
}

2c.variables() { 
  echo "all shell variables: $*"
}
2c.variables.completion() { 
  compgen -v  "$1" 
}

2c.alias() { 
  echo "aliases: $*"
}
2c.alias.completion() { 
  compgen -a  "$1" 
}

2c.folders() { 
  echo "folders: $*"
}
2c.folders.completion() { 
  compgen -d  "$1" 
}
2c.file.completion() { 
  compgen -f "$1" 
}



2c.usage()
{
  local this=${0##*/}
  echo "You started" 
  echo "$0

  Usage:
  $this: is the next generation completion with regards to function parameter and types
  
  $this <command> [PRESS TAB]

  completes the command and finally executes it
  "
  #this.help
  echo "
      ----      --------------------------
  Examples
    $this v
    $This init

    $this 2c      [PRESS TAB]
    $this devTool [PRESS TAB]
  "
}
2c.init() {
  console.log "2c init"
}

2c.install.linux() {
  $SUDO apt install bash-completion
}

2c.status() # # does not work
# ${RED}Does not work, since complete is an internal bash command
# it ouly works inteactively or when being ${YELLOW}sourced${RED} as a command
{
  console.log "2c status
  please type 
  
  complete
  
  into the console"
  complete # does not work...has to be in top level interactive shell
  cat /etc/profile.d/bash_completion.sh
}

2c.start()
{
  #echo "sourcing init"
  source $(dirname $0)/../this
  source ~/config/user.env
  source $(dirname $0)/../line
  #2c.init


  this.start "$@"
}

2c.start "$@"

