#
# Search by Konstantin Gredeskoul «github.com/kigster»
#———————————————————————————————————————————————————————————————————————————————
# This function returns list of aliases, plugins and completions in bash-it,
# whose name or description matches one of the search terms provided as arguments.
#
# Usage:
#    ❯ bash-it search term1 [[-]term2] ... [[-]termN] [ --enable | --disable ]
#
# Exmplanation:
#    Single dash, as in "-chruby", indicates a negative search term.
#    Double dash indicates a command that is to be applied to the search result.
#    At the moment only --enable and --disable are supported.
#
# Examples:
#    ❯ bash-it search ruby rbenv rvm gem rake
#    aliases     :  bundler
#    plugins     :  chruby chruby-auto rbenv ruby rvm
#    completions :  gem rake

#    ❯ bash-it search ruby rbenv rvm gem rake -chruby
#    aliases     :  bundler
#    plugins     :  rbenv ruby rvm
#    completions :  gem rake
#
# Examples of enabling or disabling results of the search:
#
#    ❯ bash-it search ruby
#    aliases  =>   bundler
#    plugins  =>   chruby chruby-auto ruby
#
#    ❯ bash-it search ruby -chruby --enable
#    aliases  =>   ✓bundler
#    plugins  =>   ✓ruby
#
#
_bash-it-search() {
  _about 'searches for given terms amongst bash-it plugins, aliases and completions'
  _param '1: term1'
  _param '2: [ term2 ]...'
  _example '$ _bash-it-search ruby rvm rake bundler'

  declare -a _components=(aliases plugins completions)

  for _component in "${_components[@]}" ; do
    _bash-it-search-component  "${_component}" "$@"
  done
}

#———————————————————————————————————————————————————————————————————————————————
# array=("something to search for" "a string" "test2000")
# _bash-it-array-contains-element "a string" "${array[@]}"
# ( prints "true" or "false" )
_bash-it-array-contains-element () {
  local e
  local r=false
  for e in "${@:2}"; do [[ "$e" == "$1" ]] && r=true; done
  echo -n $r
}

_bash-it-search-component() {
  _about 'searches for given terms amongst a given component'
  _param '1: component type, one of: [ aliases | plugins | completions ]'
  _param '2: term1 '
  _param '3: [-]term2 [-]term3 ...'
  _example '$ _bash-it-search-component aliases rake bundler -chruby'

  _component=$1

  local func=_bash-it-${_component}
  local help=$($func)
  shift

  # if one of the search terms is --enable or --disable, we will apply
  # this action to the matches further down.
  local action action_func component_singular
  declare -a _search_commands=(enable disable)
  for _search_command in "${_search_commands[@]}"; do
    if [[ $(_bash-it-array-contains-element "--${_search_command}" "$@") == "true" ]]; then
      action=$_search_command
      component_singular=${_component}
      component_singular=${component_singular/es/}  # aliases -> alias
      component_singular=${component_singular/ns/n} # plugins -> plugin
      action_func="_${action}-${component_singular}"
      break
    fi
  done

  local _grep=$((which --skip-alias grep 2> /dev/null || which grep) | tail -n 1)

  declare -a terms=($@)           # passed on the command line
  declare -a matches=()           # results that we found
  declare -a negative_terms=()    # terms that began with a dash

  for term in "${terms[@]}"; do
    # -- can only be used for the actions: enable/disable
    [[ "${term:0:2}" == "--" ]] && continue
    [[ "${term:0:1}" == "-"  ]] && negative_terms=(${negative_terms[@]} ${term:1}) && continue

    # print asterisk next to each result that is already enabled by the user
    local term_match=($(echo "${help}"| ${_grep} -i -- ${term} | ${_grep} -E '\[( |x)\]' | cut -b -30 | sed 's/ *\[ \]//g;s/ *\[x\]/*/g;' ))
    [[ "${#term_match[@]}" -gt 0 ]] && {
      matches=(${matches[@]} ${term_match[@]}) # append to the list of results
    }
  done

  # now check if we found any negative terms, and subtract them
  [[ ${#negative_terms} -gt 0 ]] && {
    declare -a filtered_matches=()
    for match in "${matches[@]}"; do
      local negations=0
      for nt in "${negative_terms[@]}"; do
        [[ "${match}" =~ "${nt}" ]] && negations=$(($negations+1))
      done
      [[ $negations -eq 0 ]] && filtered_matches=(${filtered_matches[@]} ${match})
    done
    matches=(${filtered_matches[@]})
  }

  _bash-it-search-result $action $action_func

  unset matches filtered_matches terms
}

_bash-it-search-result() {
  local action=$1; shift
  local action_func=$1; shift
  local color_component color_enable color_disable color_off

  [[ -z "$NO_COLOR" ]] && {
    color_component='\e[1;34m'
    color_enable='\e[1;32m'
    color_disable='\e[0;0m'
    color_off='\e[0;0m'
    color_sep=':'
  }

  [[ -n "$NO_COLOR" ]] && {
    color_component=''
    color_sep='  => '
    color_enable='✓'
    color_disable=''
    color_off=''
  }

  if [[ "${#matches[*]}" -gt 0 ]] ; then
    printf "${color_component}%13s${color_sep} ${color_off}" "${_component}"

    sorted_matches=($(echo "${matches[*]}" | tr ' ' '\n' | sort | uniq))

    for match in "${sorted_matches[@]}"; do
      local match_color compatible_action
      if [[ $match =~ "*" ]]; then
        match_color=$color_enable
        compatible_action="disable"
      else
        match_color=$color_disable
        compatible_action="enable"
      fi

      match_value=${match/\*/}  # remove asterisk
      len=${#match_value}
      if [[ -n $NO_COLOR ]]; then
        local m="${match_color}${match_value}"
        len=${#m}
      fi

      printf " ${match_color}${match_value}"  # print current state

      if [[ "${action}" == "${compatible_action}" ]]; then
        # oh, i see – we need to either disable enabled, or enable disabled
        # component. Let's start with the most important part: redrawing
        # the search result backwards. Because style.

        printf "\033[${len}D"
        for a in {0..30}; do
          [[ $a -gt $len ]] && break
          printf "%.*s" $a " "
          sleep 0.07 # who knew you could sleep for fraction of the cost :)
        done
        printf "\033[${len}D"
        result=$(${action_func} ${match_value})
        local temp="color_${compatible_action}"
        match_color=${!temp}
        printf "${match_color}${match_value}"
      fi

      printf "${color_off}"
    done

    printf "\n"
  fi

}
