#!/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/c2


### new.method

c2.get.functions() # <script> <?functionNameFilter> # lists 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 "^{" \
  | sort
}

c2.get.function.with.documentation() # <script> <?functionNameFilter> # lists all functions in a script with their documentation
# 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 "^{" \
  | sort
}


c2.format.functions() # <script> <?functionNameFilter> # formats function output with colors (pipe usage: c2.get.functions | c2.format.functions)
{
  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}" \
  | sed "s/</${YELLOW}</g" \
  | sed "s/>/>$(echo -n "${GREEN}")/g" \
  | line.replace "#" "${GREEN}#" \
  | line.filter "\.completion" \
  | line.filter "^private" \
  | line.filter "\.protected\."
}

c2.get.formated.function.description() # <script> <?functionNameFilter> # gets formatted function description with colors for display
{
    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

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

c2.function.completion() # <script> <?functionNameFilter> # returns list of method names for completion (filters out .completion and private methods)
{
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

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

}

c2.test.absolutePathName() # <?path> # tests this.absolutePathName and displays result in cyan
{
  this.absolutePathName "$@"
  echo "${CYAN}${RESULT}"
}

c2.get.function.declaration() # <script> <method> # extracts function declaration and parameters, saves to current.method.env
{
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  # BUG FIX: use class-qualified name to avoid substring matches.
  # Without "$name.", method "tree" matched otmux.client.choose.tree (longer
  # function names containing the method as suffix), and `sort` made
  # client.choose.tree come first alphabetically. METHOD_PARAMETER ended up
  # empty → no parameter completion ran for tree.
 c2.get.functions "$from" "${name}.$1" \
  | grep "${name}\.$1(" \
  | line.select 1 \
  | line.replace "\([^ ]\)()" "\1" \
  | line.replace " {" \
  | sed "s/'//g" \
  | 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 "4" ]; then
    cat $CONFIG_PATH/current.method.env >>$LOG_DEVICE
  fi
}


c2.get.function.parameter() # <script> <method> <?args> # extracts parameter definitions from function signature
{
  local from="$1"
  if [ -n "$1" ]; then
    from="$1"
    shift
  else
    from="$This"
  fi

  local name=$(basename $from)

  c2.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 "4" ]; then
    cat $CONFIG_PATH/result.env >>$LOG_DEVICE
  fi

}


c2.debug.parse.param() # # internal: debug helper for parsing parameters (pipe usage)
{
    cat - \
  | line.unify ':' "parameterName" "addDefaultValue" \

}

c2.completion.enable() # <cmd> # enables c2 completion on a command (must be sourced interactively)
# ${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
# }

c2.completion() # # returns list of oosh scripts available for completion
{
  compgen -f $OOSH_DIR/ | line.replace.sedquoted "${OOSH_DIR}/"
}

c2.completion.discover() # <numberOfWords> <currentWord> <script> <?method> <?rest> # main completion discovery engine
# Discovers available completions based on script, method, and parameter position
# Returns completion suggestions in $CONFIG_PATH/completion.result.txt
{
  local argc=$#    # nuber of arguments
  #printf "\n"
  #clear -x

  local word="$1"  # completion word number (important for parameter)
  shift
  debug.log "c2 $* ${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/c2"
  fi

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

    script="$1"
    shift

    if [ "$script" = "-" ]; then
      c2.completion >$CONFIG_PATH/completion.result.txt
      debug.log "c2:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
    else
      c2.completion | grep "^${script}" >$CONFIG_PATH/completion.result.txt
      debug.log "c2:$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 "c2 ${YELLOW}<?script:$script> <?method:$method>${NO_COLOR} $*   ${RED}$argc $cur"
  if [ -z "$completionResult" ]; then
    completionResult="$script"
  fi


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



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

  info.log "c2 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"

    c2.get.functions $script ${filter} \
    | c2.format.functions $script ${filter} \
    | line.replace "\.$method" ".${RED}${method}${NO_COLOR}"
    # c2.format.functions $script ${filter} \
    #   | line.replace "\.$method" ".${RED}${method}${NO_COLOR}"

    #stat -L -c "%a %G %U" $CONFIG_PATH
    c2.function.completion $script ${filter} >$CONFIG_PATH/completion.result.txt
    debug.log "c2:$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
          console.log ""
          c2.get.formated.function.description $script ${filter}
    fi
    # If method has its own completion function, skip sub-command listing
    # and go directly to parameter completion (e.g. ossh.get has ossh.get.completion.object)
    if [ $count -gt 0 ] && [ $word -gt 1 ]; then
      local firstParam
      firstParam=$(cat $CONFIG_PATH/completion.parameter.txt 2>/dev/null | line.select 1 2>/dev/null)
      if this.functionExists "$class.$method.completion.$firstParam" 2>/dev/null || \
         this.functionExists "$class.$method.completion" 2>/dev/null; then
        info.log "method $method has own completion — skipping sub-commands"
        count=0
        completionResult=""
      fi
    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"
    c2.get.function.parameter "$script" "$method" "$@"

    source $CONFIG_PATH/current.method.env

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

    # THIS SHOULD NOT GO TO /dev/tty. Use logging instead!
    # 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]}"


    # Show parameter names: current in cyan, others in yellow
    if [ -n "$METHOD_PARAMETER" ]; then
      local coloredParams="${YELLOW}${METHOD_PARAMETER}${NO_COLOR}"
      if [ -n "$currentParameter" ]; then
        coloredParams=$(echo "$coloredParams" | sed "s|${currentParameter}|${CYAN}${currentParameter}${YELLOW}|")
      fi
      console.log " $coloredParams"
    fi




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


private.call.custom.completion() # <cur> <class> <method> <?parameter> # calls custom completion function if it exists
# Tries: $class.$method.completion.$parameter, then $class.parameter.completion.$parameter
# Falls back to default value from function signature if no completion function exists
{
    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 cur=$cur class=$class method=$method parameter=$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 "c2:$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 "c2:$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 "c2:$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 "c2:$LINENO completion.result.txt: -> `cat $CONFIG_PATH/completion.result.txt` <-"
    return 1
}

c2.files.completion() # <?prefix> <?dir> # returns file completion for prefix in directory
{
  important.log "c2.files.completion $*"
  if [ -z "$2" ]; then
    cd "$2"
  fi
  compgen -f  "$2$1"
}

c2.env() # <varName> # displays exported environment variable and its value
{
  echo "exported ENV variables: $*"
  echo "$1=${!1}"
}

c2.env.completion() # <prefix> # returns environment variable names matching prefix
{
  compgen -e  "$1"
}

c2.user() # <username> # displays username
{
  echo "$*"
}

c2.user.completion() # <prefix> # returns usernames matching prefix
{
  compgen -u  "$1"
}

c2.groups() # <groupname> # displays group name
{
  echo "$*"
}

c2.groups.completion() # <prefix> # returns group names matching prefix
{
  compgen -g  "$1"
}

c2.buildin.commands() # <command> # displays builtin command
{
  echo "$*"
}

c2.buildin.commands.completion() # <prefix> # returns builtin commands matching prefix
{
  compgen -c  "$1"
}

c2.jobs() # <job> # displays job
{
  echo "$*"
}

c2.jobs.completion() # <prefix> # returns jobs matching prefix
{
  compgen -j  "$1"
}

c2.variables() # <varName> # displays all shell variables
{
  echo "all shell variables: $*"
}

c2.variables.completion() # <prefix> # returns shell variable names matching prefix
{
  compgen -v  "$1"
}

c2.alias() # <aliasName> # displays alias
{
  echo "aliases: $*"
}

c2.alias.completion() # <prefix> # returns alias names matching prefix
{
  compgen -a  "$1"
}

c2.folders() # <path> # displays folder path
{
  echo "folders: $*"
}

c2.folders.completion() # <prefix> # returns folder paths matching prefix
{
  compgen -d  "$1"
}

c2.file.completion() # <prefix> # returns file paths matching prefix
{
  compgen -f "$1"
}



c2.usage() # # displays usage information for c2 completion system
{
  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 c2      [PRESS TAB]
    $this devTool [PRESS TAB]
  "
}

c2.init() # # initializes c2 completion system
{
  console.log "c2 init"
}

c2.install.linux() # # installs bash-completion package on Linux
{
  $SUDO apt install bash-completion
}

c2.status() # # displays completion status (interactive shell only)
# ${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 "c2 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
}

c2.start() # <?args> # initializes c2 script and dispatches to requested method
{
  #echo "sourcing init"
  # Resolve symlink to get actual script location
  local script_path="${BASH_SOURCE[0]}"
  if [ -L "$script_path" ]; then
    script_path=$(readlink "$script_path")
  fi
  local script_dir=$(cd "$(dirname "$script_path")" && pwd)

  source "$script_dir/../this"
  source ~/config/user.env
  source "$script_dir/../line"
  #c2.init


  this.start "$@"
}

c2.start "$@"
