#! bash oh-my-bash.module

PROMPT_DIRTRIM=${PROMPT_DIRTRIM:-2} # bash4 and above

######################################################################
## Configurations in Oh My Bash

OMB_PROMPT_SHOW_PYTHON_VENV=${OMB_PROMPT_SHOW_PYTHON_VENV:=true}

######################################################################
DEBUG=0
function debug {
  if [[ ${DEBUG} -ne 0 ]]; then
    >&2 echo -e "$@"
  fi
}

######################################################################
### Segment drawing
# A few utility functions to make it easy and re-usable to draw segmented prompts

CURRENT_BG='NONE'
CURRENT_RBG='NONE'
SEGMENT_SEPARATOR=''
RIGHT_SEPARATOR=''
LEFT_SUBSEG=''
RIGHT_SUBSEG=''

function text_effect {
  case "$1" in
  reset)      echo 0;;
  bold)       echo 1;;
  underline)  echo 4;;
  esac
}

# to add colors, see
# http://bitmote.com/index.php?post/2012/11/19/Using-ANSI-Color-Codes-to-Colorize-Your-Bash-Prompt-on-Linux
# under the "256 (8-bit) Colors" section, and follow the example for orange below
function fg_color {
  case "$1" in
  black)      echo 30;;
  red)        echo 31;;
  green)      echo 32;;
  yellow)     echo 33;;
  blue)       echo 34;;
  magenta)    echo 35;;
  cyan)       echo 36;;
  white)      echo 37;;
  orange)     echo 38\;5\;166;;
  esac
}

function bg_color {
  case "$1" in
  black)      echo 40;;
  red)        echo 41;;
  green)      echo 42;;
  yellow)     echo 43;;
  blue)       echo 44;;
  magenta)    echo 45;;
  cyan)       echo 46;;
  white)      echo 47;;
  orange)     echo 48\;5\;166;;
  esac;
}

# TIL: declare is global not local, so best use a different name
# for codes (mycodes) as otherwise it'll clobber the original.
# this changes from BASH v3 to BASH v4.
function ansi {
  local seq
  local -a mycodes=("${!1}")

  debug "ansi: ${!1} all: $* aka ${mycodes[@]}"

  seq=""
  local i
  for ((i = 0; i < ${#mycodes[@]}; i++)); do
    if [[ -n $seq ]]; then
      seq="${seq};"
    fi
    seq="${seq}${mycodes[$i]}"
  done
  debug "ansi debug:" '\\[\\033['${seq}'m\\]'
  echo -ne '\[\033['${seq}'m\]'
  # PR="$PR\[\033[${seq}m\]"
}

function ansi_single {
  echo -ne '\[\033['$1'm\]'
}

# Begin a segment
# Takes two arguments, background and foreground. Both can be omitted,
# rendering default background/foreground.
function prompt_segment {
  local bg fg
  local -a codes

  debug "Prompting $1 $2 $3"

  # if commented out from kruton's original... I'm not clear
  # if it did anything, but it messed up things like
  # prompt_status - Erik 1/14/17

  #    if [[ -z $1 || ( -z $2 && $2 != default ) ]]; then
  codes=("${codes[@]}" $(text_effect reset))
  #    fi
  if [[ -n $1 ]]; then
    bg=$(bg_color $1)
    codes=("${codes[@]}" $bg)
    debug "Added $bg as background to codes"
  fi
  if [[ -n $2 ]]; then
    fg=$(fg_color $2)
    codes=("${codes[@]}" $fg)
    debug "Added $fg as foreground to codes"
  fi

  debug "Codes: "
  # local -p codes

  if [[ $CURRENT_BG != NONE && $1 != $CURRENT_BG ]]; then
    local -a intermediate=($(fg_color $CURRENT_BG) $(bg_color $1))
    debug "pre prompt " $(ansi intermediate[@])
    PR="$PR $(ansi intermediate[@])$SEGMENT_SEPARATOR"
    debug "post prompt " $(ansi codes[@])
    PR="$PR$(ansi codes[@]) "
  else
    debug "no current BG, codes is $codes[@]"
    PR="$PR$(ansi codes[@]) "
  fi
  CURRENT_BG=$1
  [[ -n $3 ]] && PR="$PR$3"
}

# End the prompt, closing any open segments
function prompt_end {
  if [[ -n $CURRENT_BG ]]; then
    local -a codes=($(text_effect reset) $(fg_color $CURRENT_BG))
    PR="$PR $(ansi codes[@])$SEGMENT_SEPARATOR"
  fi
  local -a reset=($(text_effect reset))
  PR="$PR $(ansi reset[@])"
  CURRENT_BG=''
}

### virtualenv prompt
function prompt_virtualenv {
  # Exclude pyenv
  [[ $PYENV_VIRTUALENV_INIT == 1 ]] && _omb_util_binary_exists pyenv && return 0

  if [[ -d $VIRTUAL_ENV ]]; then
    # Python could output the version information in both stdout and
    # stderr (e.g. if using pyenv, the output goes to stderr).
    local VERSION_OUTPUT=$("$VIRTUAL_ENV"/bin/python --version 2>&1)

    # The last word of the output of `python --version`
    # corresponds to the version number.
    local VENV_VERSION=$(awk '{print $NF}' <<< "$VERSION_OUTPUT")

    prompt_segment cyan white "[v] $(basename "$VENV_VERSION")"
  fi
}

### pyenv prompt
function prompt_pyenv {
  if [[ $PYENV_VIRTUALENV_INIT == 1 ]] && _omb_util_binary_exists pyenv; then
    # Priority is shell > local > global
    # When pyenv shell is set, the environment variable $PYENV_VERSION is set with the value we want
    if [[ ! ${PYENV_VERSION-} ]]; then
      # If not set, fall back to pyenv local/global to get the version
      local PYENV_VERSION=$(pyenv local 2>/dev/null || pyenv global 2>/dev/null)
    fi
    # If it is not the system's python, then display additional info
    if [[ "$PYENV_VERSION" != "system" ]]; then
      # It's a pyenv virtualenv, get the version number
      if [[ -d $PYENV_VIRTUAL_ENV ]]; then
        local VERSION_OUTPUT=$("$PYENV_VIRTUAL_ENV"/bin/python --version 2>&1)
        local PYENV_VENV_VERSION=$(awk '{print $NF}' <<< "$VERSION_OUTPUT")
        prompt_segment cyan white "[$PYENV_VERSION] $(basename "$PYENV_VENV_VERSION")"
      else
        prompt_segment cyan white "$PYENV_VERSION"
      fi
    fi
  fi
}

### conda env prompt
function prompt_condaenv {
  if [[ -d $CONDA_PREFIX ]]; then
    if [[ ! $CONDA_PROMPT_MODIFIER ]]; then
      CONDA_PROMPT_MODIFIER=$(basename "$CONDA_PREFIX")
    fi
    local CONDA_PYTHON_VERSION=$("$CONDA_PREFIX"/bin/python -c 'import platform;print(platform.python_version())')
    prompt_segment cyan white "[c] $CONDA_PROMPT_MODIFIER $CONDA_PYTHON_VERSION"
  fi
}

### Prompt components
# Each component will draw itself, and hide itself if no information needs to be shown

# Context: user@hostname (who am I and where am I)
function prompt_context {
  local user=$(whoami)

  if [[ $user != $DEFAULT_USER || -n $SSH_CLIENT ]]; then
    prompt_segment black default "$user@\h"
  fi
}

# prints history followed by HH:MM, useful for remembering what
# we did previously
function prompt_histdt {
  prompt_segment black default "\! [\A]"
}


function git_status_dirty {
  dirty=$(command git status -s 2> /dev/null | tail -n 1)
  [[ -n $dirty ]] && echo " ●"
}

function git_stash_dirty {
  stash=$(command git stash list 2> /dev/null | tail -n 1)
  [[ -n $stash ]] && echo " ⚑"
}

# Git: branch/detached head, dirty status
function prompt_git {
  local ref dirty
  if command git rev-parse --is-inside-work-tree &>/dev/null; then
    ZSH_THEME_GIT_PROMPT_DIRTY='±'
    dirty=$(git_status_dirty)
    stash=$(git_stash_dirty)
    ref=$(command git symbolic-ref HEAD 2> /dev/null) ||
      ref="➦ $(command git describe --exact-match --tags HEAD 2> /dev/null)" ||
      ref="➦ $(command git show-ref --head -s --abbrev | head -n1 2> /dev/null)"
    if [[ -n $dirty ]]; then
      prompt_segment yellow black
    else
      prompt_segment green black
    fi
    PR="$PR${ref/refs\/heads\// }$stash$dirty"
  fi
}

# Mercurial: clean, modified and uncomitted files
function prompt_hg {
  local rev st branch
  if hg id &>/dev/null; then
    if hg prompt &>/dev/null; then
      if [[ $(hg prompt "{status|unknown}") == '?' ]]; then
        # if files are not added
        prompt_segment red white
        st='±'
      elif [[ $(hg prompt "{status|modified}") ]]; then
        # if any modification
        prompt_segment yellow black
        st='±'
      else
        # if working copy is clean
        prompt_segment green black $CURRENT_FG
      fi
      PR="$PR$(hg prompt "☿ {rev}@{branch}") $st"
    else
      st=""
      rev=$(hg id -n 2>/dev/null | sed 's/[^-0-9]//g')
      branch=$(hg id -b 2>/dev/null)
      if hg st | grep -q "^\?"; then
        prompt_segment red white
        st='±'
      elif hg st | grep -q "^[MA]"; then
        prompt_segment yellow black
        st='±'
      else
        prompt_segment green black $CURRENT_FG
      fi
      PR="$PR☿ $rev@$branch $st"
    fi
  fi
}

# Dir: current working directory
function prompt_dir {
  prompt_segment blue black '\w'
}

# Status:
# - was there an error
# - am I root
# - are there background jobs?
function prompt_status {
  local symbols
  symbols=()
  [[ $RETVAL -ne 0 ]] && symbols+="$(ansi_single $(fg_color red))✘"
  [[ $UID -eq 0 ]] && symbols+="$(ansi_single $(fg_color yellow))⚡"
  [[ $(jobs -l | wc -l) -gt 0 ]] && symbols+="$(ansi_single $(fg_color cyan))⚙"

  [[ -n "$symbols" ]] && prompt_segment black default "$symbols"
}

######################################################################
#
# experimental right prompt stuff
# requires setting prompt_foo to use PRIGHT vs PR
# doesn't quite work per above

function rightprompt {
  printf "%*s" $COLUMNS "$PRIGHT"
}

# quick right prompt I grabbed to test things.
function __command_rprompt {
  local times= n=$COLUMNS tz
  for tz in ZRH:Europe/Zurich PIT:US/Eastern \
                              MTV:US/Pacific TOK:Asia/Tokyo; do
    [ $n -gt 40 ] || break
    times="$times ${tz%%:*}\e[30;1m:\e[0;36;1m"
    times="$times$(TZ=${tz#*:} date +%H:%M)\e[0m"
    n=$(( $n - 10 ))
  done
  [ -z "$times" ] || printf "%${n}s$times\\r" ''
}

# this doens't wrap code in \[ \]
function ansi_r {
  local seq
  local -a mycodes2=("${!1}")

  debug "ansi: ${!1} all: $* aka ${mycodes2[@]}"

  seq=""
  local i
  for ((i = 0; i < ${#mycodes2[@]}; i++)); do
    if [[ -n $seq ]]; then
      seq="${seq};"
    fi
    seq="${seq}${mycodes2[$i]}"
  done
  debug "ansi debug:" '\\[\\033['${seq}'m\\]'
  echo -ne '\033['${seq}'m'
  # PR="$PR\[\033[${seq}m\]"
}

# Begin a segment on the right
# Takes two arguments, background and foreground. Both can be omitted,
# rendering default background/foreground.
function prompt_right_segment {
  local bg fg
  local -a codes

  debug "Prompt right"
  debug "Prompting $1 $2 $3"


  #    if [[ -z $1 || ( -z $2 && $2 != default ) ]]; then
  codes=("${codes[@]}" $(text_effect reset))
  #    fi
  if [[ -n $1 ]]; then
    bg=$(bg_color $1)
    codes=("${codes[@]}" $bg)
    debug "Added $bg as background to codes"
  fi
  if [[ -n $2 ]]; then
    fg=$(fg_color $2)
    codes=("${codes[@]}" $fg)
    debug "Added $fg as foreground to codes"
  fi

  debug "Right Codes: "
  # local -p codes

  local -a intermediate2=($(fg_color $1) $(bg_color $CURRENT_RBG) )
  # PRIGHT="$PRIGHT---"
  debug "pre prompt " $(ansi_r intermediate2[@])
  PRIGHT="$PRIGHT$(ansi_r intermediate2[@])$RIGHT_SEPARATOR"
  debug "post prompt " $(ansi_r codes[@])
  PRIGHT="$PRIGHT$(ansi_r codes[@]) "
  # else
  #     debug "no current BG, codes is $codes[@]"
  #     PRIGHT="$PRIGHT$(ansi codes[@]) "
  # fi
  CURRENT_RBG=$1
  [[ -n $3 ]] && PRIGHT="$PRIGHT$3"
}

######################################################################
## Emacs prompt --- for dir tracking

function prompt_emacsdir {
  # no color or other setting... this will be deleted per above
  PR="DIR \w DIR$PR"
}

######################################################################
## Main prompt

function build_prompt {
  [[ ! -z ${AG_EMACS_DIR+x} ]] && prompt_emacsdir
  prompt_status
  #[[ -z ${AG_NO_HIST+x} ]] && prompt_histdt
  [[ -z ${AG_NO_CONTEXT+x} ]] && prompt_context
  if [[ ${OMB_PROMPT_SHOW_PYTHON_VENV-} ]]; then
    prompt_virtualenv
    prompt_pyenv
    prompt_condaenv
  fi
  prompt_dir
  prompt_git
  prompt_hg
  prompt_end
}

function _omb_theme_PROMPT_COMMAND {
  local RETVAL=$?
  local PRIGHT=""
  local CURRENT_BG=NONE
  local PR="$(ansi_single $(text_effect reset))"
  build_prompt

  # uncomment below to use right prompt
  #     PS1='\[$(tput sc; printf "%*s" $COLUMNS "$PRIGHT"; tput rc)\]'$PR
  PS1=$PR
}
_omb_util_add_prompt_command _omb_theme_PROMPT_COMMAND
