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


config.v() { # # prints out the tates version ${RED}Hard coded...make it support oo release${NORMAL}
  echo "$0 version 2021-10-29 13:15"
}


config.completion.discover() {
  #set -x
  #info.log "completion.discover $*"
  local command=$1
  shift
  local ac=$(which $command)
  case "${command}" in
    "init"|"oosh"|"this")
      command="this"
      ac=$OOSH_DIR/this
    ;;
    "oo")
      if [ -n "$1" ]; then
        #set -x
        command=$1
        shift
        ac=$(which $command) 
        if [ -n "$ac" ]; then
          complete -F _oo_commands $ac
          important.log "added completion to $ac"
          config.completion.discover $ac
          return 0
        else
          cd $OOSH_DIR
          compgen -f $command
          return 0
        fi
      fi
    ;;    
    "once.sh")
      command="once"
    ;;
    "config")
      debug.log "in config"
      ac=$OOSH_DIR/config
    ;;
    *)
      debug.log "$command not found in completion"
    ;;
  esac
  
  local method=""
  local previous=""
  local last=""
  while [ -n "$1" ]; do
   previous=$method
   if [ -z "$method" ]; then
    method=$1
   else
    method=$method.$1
   fi
   last=$1
   shift
  done

  # if [ -n "$command" ]; then
  #   if [ -x "$(command -v $command)" ]; then
  #     info.log "sourcing $command"
  #     #source "$command"
  #   else
  #     return 1
  #   fi
  # else
  #   return 1
  # fi


  if (config.check.completion $command $previous); then
      $ac $previous.completion $last $command $previous
  fi

  if (config.check.completion $command $last); then
    if [ "$ac" = "$OOSH_DIR/config" ]; then
      if (this.functionExists $command.$last.completion); then
        $command.$last.completion '' $command $previous
        return 0
      fi
      if (this.functionExists $command.$previous.completion); then
        $command.$previous.completion $last $command $previous
        return 0
      fi
    else

      $ac $last.completion '' $command $previous
      return 0
    fi
  fi
  # if (this.functionExists $command.$previous.completion); then
  #   $command.$previous.completion $last $command $previous
  #   return 0
  # fi

  if [[ $method == *[\\/\(\)]* ]]; then
    create.result 1 "$method contains special charaters"
    #echo $RESULT
    return "$(result)" 
  fi

  #echo method:$method
  local find="^\(function \)*\($command\.\)\($method[^(#\"]*\)\(.*\)"

  grep "$find" $ac | sed 's/'"$find"'/\3/' | sed 's/\(.*\)\(\.completion.*\)/\1/'
  #echo "$@"
}

config.check.completion() {
  local command=$1
  local ac=$(which $command)
  local method="$2.completion"
  local find="^\(function \)*\($command\.\)\($method[^(#\"]*\)\(.*\)"
  if [[ $method == *[\\/\(\)]* ]]; then
    create.result 1 "$method contains special charaters"
    #echo $RESULT
    return "$(result)" 
  fi
  RESULT=$(grep "$find" $ac | sed 's/'"$find"'/\3/')
  if [ "$RESULT" = "$method" ]; then
    create.result 0 "$RESULT"
  else
    create.result 1 "$method not found" 
  fi 
  debug.log "$RESULT"
  return "$(result)"
}

config.local()  # # deprecated
{
  check dir ~/oosh exists call mv ~/oosh/ ~/oosh.gh
  ln -s /var/dev/Workspaces/2cuGitHub/once.sh oosh
  check dir ~/init.bak exists call rm -r ~/init.bak
  check dir ~/init.bak not exists call mv ~/init ~/init.bak
  #check dir ~/init not exists cp -r ~/oosh/init ~/init
  check dir ~/init not exists ln -s ~/oosh/init ~/init

}

config.ci() {  # # deprecated
  config.completion.install
  #$OOSH_DIR/oosh 
}

config.completion.install() # # deprecated
{
  DIR=${BASH_COMPLETION_USER_DIR:-${XDG_DATA_HOME:-$HOME/.local/share}/bash-completion}/completions
  config.path.create $DIR
  echo -e "
_oo_commands()
{
    #set -x
    #echo processing once completion
    local cur prev opts
    local IFS=$'\\\\n'

    COMPREPLY=( )
    cur=\"\${COMP_WORDS[COMP_CWORD]}\"
    prev=\"\${COMP_WORDS[COMP_CWORD-1]}\"
    
    #echo \"curr: \${cur}  COMP_CWORD: \$COMP_CWORD  WORDs: =\${COMP_WORDS[*]}= \"
    opts=\$(config completion.discover \${COMP_WORDS[*]})
 
    if [ \$COMP_CWORD -gt 1 ] && [ \"$opts\" = \"\${COMP_WORDS[-1]}\"  ]; then
      COMPREPLY=( \$(compgen -o default \$cur) )
      return 0
    fi
    
    COMPREPLY=( \${opts} )
    if [ \${#COMPREPLY[@]} -eq 0 ]; then
      COMPREPLY=( \$(compgen -o default -o plusdirs \$cur) )
    fi

    return 0
}
" >$DIR/_oosh_commands
  if [ ! -f ~/.bashrc.bak.without.completion ]; then
    cp ~/.bashrc ~/.bashrc.bak.without.completion 
  fi
  cp $OOSH_DIR/templates/user/bashrc_template ~/.bashrc
} 


config.edit.completion() {
  config.list.completion "$@"
}

config.delete.completion() {
  config.list.completion "$@"
}



config.list.completion.name() {
  #echo "$@"
  export FILTER=$1
  for file in ${CONFIG_PATH}/*.env 
    do
      echo "$(basename $file)" | line replace ".env"
    done
}

config.string.quote() { # <string> # quote the string to be used as a command line argument
  RESULT=$(printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/")
  echo $RESULT
}

config.init() # # inititalizes the $CONFIG environment if $CONFIG is empty
{
  export CONFIG_PATH=~/config
  export CONFIG_FILE=user.env

  if [ ! -d $CONFIG_PATH ]; then
    mkdir $CONFIG_PATH
  #else
  #  source $CONFIG
  fi
  touch $CONFIG_PATH/error.txt
  
  export CONFIG=$CONFIG_PATH/$CONFIG_FILE
  debug.log "$CONFIG"
}

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

  current file: $CONFIG
   CONFIG_PATH=$CONFIG_PATH
   CONFIG_FILE=$CONFIG_FILE


  Usage:
  $this: command   description and Parameter
      v         print version information
      init      initializes a new user config
      save      <?name> <?ENV_NAME> saves all vaiables containing ENV_NAME to name.env
      list      <?name> lists all vaiables in name.env
      add       <?name> lists all vaiables containing NAME to $CONFIG
      delete    <?name> deletes name.env
  
  Examples
    update current shell
      . \$CONFIG
      . $CONFIG

    $this v
    $this save once
    $this list oosh
  "
  
  #loop.list PATH print '---------------------: '
}

config.save() # <?name> <?ENV_PREFIX> # creates an "<name>.env" config file in $CONFIG_PATH containing all env variables that match <ENV_PREFIX>
# without parameters it inititlises the $CONFIG_PATH/user.env
{
  #set -x
  console.log "config.save (CONFIG=$CONFIG)"

  local file=$1
  shift
  local name=$1
  if [ -n "$file" ] &&  [ -z "$name" ]; then
    #set -x
    warn.log "BASH 5 support only" # on bash 3 this would help: $(tr '[:lower:]' '[:upper:]' <<< "$file")
    name=${file^^}
  else
    shift
  fi


  if [ -n "$file" ]; then
    config.file $file.env
  else
    name="CONFIG"
  fi
  
  stop.log "
  config.save [file:$file] [name:$name] to $CONFIG"
    #declare -px | grep "^\(declare .* \)*\(.*$name\)\(.*\)=" | sed 's/^\(declare .* \)*\(.*'$name'\)\(.*\)=\(\"\)*\([^\"]*\)\(\"\)*/export \2\3=\"\5\"/'
    #declare -p | grep "^\(declare -.* \)*\(.*$name\)\(.*\)=" | sed 's/^\(declare -.* \)*\(.*'$name'\)\(.*\)=\(.*\)/export \2\3=\4/'
  RESULT=""
  # declare -p 
  # important.log "now filter"
  # declare -p | grep "^\(declare -[^aA]* \)*\([^ ]*$name\)\(.*\)=" 
  # important.log "now replace"
  # declare -p | grep "^\(declare -[^aA]* \)*\([^ ]*$name\)\(.*\)=" | sed 's/\([^-]*\)\(-x *\)\(.*'$name'\)\(.*\)=\(.*\)/export \1\3\4=\5/'
  {    
    #declare -p | grep "^\(declare -[^a]* \)*\([^ ]*$name\)\(.*\)=" | sed 's/^\(declare -.* \)*\((.*'$name'\)\(.*\)=\(.*\)/export \2\3=\4/'
    #declare -p | grep "^\(declare -[^aA]* \)*\([^ ]*$name\)\(.*\)=" | sed 's/^\(declare -.* \)\(.*'$name'\)\(.*\)=\(.*\)/export \2\3=\4/'
    
    # on mac without -x
    declare -p | grep "^\(declare -[^aA]* \)*\([^ ]*$name\)\(.*\)=" | sed 's/\([^-]*\)\(-x *\)\(.*'$name'\)\(.*\)=\(.*\)/export \1\3\4=\5/'
  } >$CONFIG

  if [ -z "$file" ]; then
    #problem.log "regenerate config"

    important.log "generating $CONFIG"
    echo "export PATH=\"$PATH\"" >>$CONFIG
    #echo "export CONFIG" >>$CONFIG
    echo "export BASH_FILE=\"$BASH_FILE\"" >>$CONFIG


    ### HACK should be in log.init only. but is needed for ssh remote logging
      log.device $LOG_DEVICE
    ###


    config.save oosh OOSH
    config.save log LOG
    config.file user.env
    config.add oosh OOSH
    config.add log LOG
    # if [ -x "$(command -v once)" ]; then
    #   once v
    # fi
  fi
  config.info.log
}

config.bash.minimal.version() { # <bash.minimal.version:5> # sets the BASH_MINIMUM_MAJOR_VERSION
  echo "export BASH_MINIMUM_MAJOR_VERSION=$1" >>$CONFIG
}

config.list() # <name:$CONFIG> # lists the content of the config with <name>
{
  config.check.file $1
  cat $CONFIG
}

config.delete() # <name:$CONFIG> # deletes the config with <name>
{
  config.check.file $1
  rm $CONFIG_PATH/$CONFIG_FILE
}

config.update() { # <?name:$CONFIG> <?ENV_PREFIX> # saves and adds the config 
  config.save $1 $2
  config.add $1 $2
  config.info.log
}

config.info.log() # # logs the config when on LOG_LEVEL info or higher
{
  if [ "$LOG_LEVEL" -gt "3" ]; then
    config.list $1
  fi
}

config.edit() # <name:$CONFIG> # edits the config with <name> in vim
{
  #check.debug.level 6
  oo cmd vim
  if config.check.file $1; then
    vim $CONFIG
  else
    vim $CONFIG
    exit $ERROR_CODE_RECONFIG
  fi
}

config.clean() #  # reinitalises a clean config and removes any custom addons
{
  config.check.file $1
  sort -u $CONFIG >$CONFIG_PATH/$CONFIG_FILE.clean
  rm $CONFIG_PATH/$CONFIG_FILE
  mv $CONFIG_PATH/$CONFIG_FILE.clean $CONFIG_PATH/$CONFIG_FILE
  if [ "$LOG_LEVEL" -gt "3" ]; then
    config.list $1
  fi
}

config.add() # <name:$CONFIG> # adds the config <name> into the user.env permanently
{
  local file=$1
  shift
  local name=$1

  if [ -n "$file" ] &&  [ -z "$name" ]; then
    #set -x
    warn.log "BASH 5 support only" # on bash 3 this would help: $(tr '[:lower:]' '[:upper:]' <<< "$file")
    name=${file^^}
  else
    shift
  fi

  if [ -z "$file" ]; then
    name="CONFIG"
  fi
  
  console.log "
  config.add [file:$file] [name:$name] to $CONFIG"
  {
    echo source \$CONFIG_PATH/$file.env
  } >>$CONFIG
  config.clean
}

config.check.file() # <name:$CONFIG> # sets the default config file to <name> if it exists
{
  if [ -n "$1" ]; then
    config.file $1.env
    shift
    RETURN=$1
    RETURN_VALUE=0
  else 
    RETURN_VALUE=1
  fi
  info.log "config file: $CONFIG"
  create.result $RETURN_VALUE $CONFIG
  return "$(result)"

}

config.file() # <configfile> # sets the default config file to <configfile> if it exists
# if <?option> is "reset", it sets the $CONFIG_FILE backi to user.env
{
  if [ -z "$1" ]; then
    echo "$CONFIG_FILE"
  else
    case "$1" in
      reset)
        config.init
        echo "CONFIG_PATH=$CONFIG_PATH"
        echo "CONFIG_FILE=$CONFIG_FILE"
        echo
        echo "CONFIG=$CONFIG"
        ;;
      *)
        export FILE=$1
        CONFIG_FILE=$FILE

        export CONFIG=$CONFIG_PATH/$CONFIG_FILE
    esac
  fi
}

config.location() # <configfile> # sets the default config location to <configfile> if it exists
# if <?option> is "reset", it sets the $CONFIG_FILE backi to user.env
{
  if [ -z "$1" ]; then
    echo "$CONFIG_FILE"
  else
    case "$1" in
      reset)
        config.init
        echo "CONFIG_PATH=$CONFIG_PATH"
        echo "CONFIG_FILE=$CONFIG_FILE"
        echo
        echo "CONFIG=$CONFIG"
        ;;
      *)
        local oldConfig="$CONFIG"

        export FILE=$(basename $1)
        CONFIG_PATH=$(dirname $1)
        CONFIG_FILE=$FILE

        info.log "old config     : $oldConfig"
        info.log "new config path: $CONFIG_PATH"
        info.log "new config file: $CONFIG_FILE"

        export CONFIG=$CONFIG_PATH/$CONFIG_FILE
        config.save
        bash
    esac
  fi
}

config.parameter.completion.configfile() {
  echo "reset"
  c2 file.completion "$1"
}

config.parameter.completion.envVariable() {
  cat $CONFIG | line replace "export " | line replace "=.*$" | line filter "source"
}

function config.path.create() # <pathString> # (internal deprecated) creates respective paths
{
    debug.log " function ${FUNCNAME[0]}($1) $@"

    if [[ "$1" = /* ]]; then
        cd "/"
    fi

    path=""
    info.log "creating path in $(pwd): $1"

    for current in ${1//// }; do

        if [ -z "$path" ]; then
            path=$current
        else
            path=$path/$current
        fi
        debug.log "checking path: $path"

        config.folder.create $path
    done
}

function config.folder.create() # <folderName> # (internal deprecated) creates respective directory requested in parameter 
{

    local current=$1
    if [ ! -d $current ]; then
        debug.log "$current does not exist: creating it..."
        mkdir -p $current
    fi
}

config.ssh.set.config.host() # <sshConfigName> # configures the PROMPT and the name of this host for ssh config
{ 
  export OOSH_SSH_CONFIG_HOST=$1
  config.save
  echo "To apply changes immediatly type:
  
  exit $ERROR_CODE_RECONFIG"
  exit $ERROR_CODE_RECONFIG
  #reconfigure
}

config.get() # <envVariable>  # gets an environment variable from the user.env 
{
  local envVariable="$1"
  if [ -n "$envVariable" ]; then
    shift
  else
    error.log "no envVariable specified"
    return 1
  fi

  local currentDefinition="$( cat $CONFIG | line find "export $envVariable=" )"
  info.log "declaration: $currentDefinition"
  echo "${!envVariable}"
}

config.set() # <envVariable> <value> # adds or sets an environment variable to the user.env 
{
  local envVariable="$1"
  if [ -n "$envVariable" ]; then
    shift
  else
    error.log "no envVariable specified"
    return 1
  fi

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

  local currentDefinition="$( cat $CONFIG | line find "export $envVariable=" )"
  if [ -n "$currentDefinition" ]; then
    if [ -z "$value" ]; then
      warn.log "set blank value on
      $currentDefinition

      "
    fi
    cat $CONFIG | line replace.sedquoted "export $envVariable=.*" "export $envVariable=\"$value\"" | tee $CONFIG
  else 
    echo "export $envVariable=\"$value\"" >>$CONFIG
    cat $CONFIG
  fi
}


config.discover() # # lists the existing config files and where they are
{
  echo $CONFIG_PATH
  ls $CONFIG_PATH
}


function config.start() # <method> <parameter> # default start and parameter processing
{
  #echo "sourcing init"
  COMMANDS="$@"
  source this
  #check.debug.level 6

  if ! [ -f "$CONFIG" ]; then
      config.init
      # local command=$1
      # shift
      #${1#"config."}"$@"
      #this.start "$@"
      return 0
  fi
  if [ -z "$CONFIG_PATH" ]; then
      config.init
  fi
  if [ "$(type -t this.start)" = "function" ]; then
    this.start "$@"
  else
    echo "config.start: this.start not yet there"
  fi
}

config.start "$@"

