# shellcheck shell=bash
# shellcheck disable=SC2034 # Expected behavior for themes.
# vim: ft=bash ts=2 sw=2 sts=2
#
# agnoster's Theme - https://gist.github.com/3712874
# A Powerline-inspired theme for BASH
#
# (Converted from ZSH theme by Kenny Root)
# https://gist.github.com/kruton/8345450
#
# Updated & fixed by Erik Selberg erik@selberg.org 1/14/17
# Tested on MacOSX, Ubuntu, Amazon Linux
# Bash v3 and v4
#
# # README
#
# In order for this theme to render correctly, you will need a
# [Powerline-patched font](https://gist.github.com/1595572).
# I recommend: https://github.com/powerline/fonts.git
# > git clone https://github.com/powerline/fonts.git fonts
# > cd fonts
# > install.sh

# In addition, I recommend the
# [Solarized theme](https://github.com/altercation/solarized/) and, if you're
# using it on Mac OS X, [iTerm 2](http://www.iterm2.com/) over Terminal.app -
# it has significantly better color fidelity.

# Install:

# I recommend the following:
# $ cd home
# $ mkdir -p .bash/themes/agnoster-bash
# $ git clone https://github.com/speedenator/agnoster-bash.git .bash/themes/agnoster-bash

# then add the following to your .bashrc:

# export THEME=$HOME/.bash/themes/agnoster-bash/agnoster.bash
# if [[ -f $THEME ]]; then
#     export DEFAULT_USER=`whoami`
#     source $THEME
# fi

#
# # Goals
#
# The aim of this theme is to only show you *relevant* information. Like most
# prompts, it will only show git information when in a git working directory.
# However, it goes a step further: everything from the current user and
# hostname to whether the last call exited with an error to whether background
# jobs are running in this shell will all be displayed automatically when
# appropriate.

# Generally speaking, this script has limited support for right
# prompts (ala powerlevel9k on zsh), but it's pretty problematic in Bash.
# The general pattern is to write out the right prompt, hit \r, then
# write the left. This is problematic for the following reasons:
# - Doesn't properly resize dynamically when you resize the terminal
# - Changes to the prompt (like clearing and re-typing, super common) deletes the prompt
# - Getting the right alignment via columns / tput cols is pretty problematic (and is a bug in this version)
# - Bash prompt escapes (like \h or \w) don't get interpolated
#
# all in all, if you really, really want right-side prompts without a
# ton of work, recommend going to zsh for now. If you know how to fix this,
# would appreciate it!

# note: requires bash v4+... Mac users - you often have bash3.
# 'brew install bash' will set you free
PROMPT_DIRTRIM=2 # bash4 and above

######################################################################
DEBUG=0
debug() {
	if [[ ${DEBUG} -ne 0 ]]; then
		echo >&2 -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=''

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
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
}

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.
ansi() {
	local seq
	declare -a mycodes=("${!1}")

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

	seq=""
	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\]"
}

ansi_single() {
	echo -ne '\[\033['"$1"'m\]'
}

# Begin a segment
# Takes two arguments, background and foreground. Both can be omitted,
# rendering default background/foreground.
prompt_segment() {
	local bg fg
	declare -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: "
	# declare -p codes

	if [[ $CURRENT_BG != NONE && $1 != "$CURRENT_BG" ]]; then
		declare -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
prompt_end() {
	if [[ -n $CURRENT_BG ]]; then
		declare -a codes=("$(text_effect reset)" "$(fg_color "$CURRENT_BG")")
		PR="$PR $(ansi codes[@])$SEGMENT_SEPARATOR"
	fi
	declare -a reset=("$(text_effect reset)")
	PR="$PR $(ansi reset[@])"
	CURRENT_BG=''
}

### virtualenv prompt
prompt_virtualenv() {
	if [[ -n $VIRTUAL_ENV ]]; then
		color=cyan
		prompt_segment $color "$PRIMARY_FG"
		ve=$(basename "$VIRTUAL_ENV")
		prompt_segment $color white "$ve"
	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)
prompt_context() {
	local user="${USER:-${LOGNAME:?}}"

	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
prompt_histdt() {
	prompt_segment black default "\! [\A]"
}

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

# Git: branch/detached head, dirty status
prompt_git() {
	local ref dirty
	if git rev-parse --is-inside-work-tree > /dev/null 2>&1; then
		ZSH_THEME_GIT_PROMPT_DIRTY='±'
		dirty=$(git_status_dirty)
		ref=$(git symbolic-ref HEAD 2> /dev/null) || ref="➦ $(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\// }$dirty"
	fi
}

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

# Status:
# - was there an error
# - am I root
# - are there background jobs?
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

rightprompt() {
	printf "%*s" $COLUMNS "$PRIGHT"
}

# quick right prompt I grabbed to test things.
__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" ''
}
# PROMPT_COMMAND=__command_rprompt

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

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

	seq=""
	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.
prompt_right_segment() {
	local bg fg
	declare -a codes

	debug "Prompt right"
	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 "Right Codes: "
	# declare -p codes

	# right always has a separator
	# if [[ $CURRENT_RBG != NONE && $1 != $CURRENT_RBG ]]; then
	#     $CURRENT_RBG=
	# fi
	declare -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
# stick the following in your .emacs if you use this:

# (setq dirtrack-list '(".*DIR *\\([^ ]*\\) DIR" 1 nil))
# (defun dirtrack-filter-out-pwd-prompt (string)
#   "dirtrack-mode doesn't remove the PWD match from the prompt.  This does."
#   ;; TODO: support dirtrack-mode's multiline regexp.
#   (if (and (stringp string) (string-match (first dirtrack-list) string))
#       (replace-match "" t t string 0)
#     string))
# (add-hook 'shell-mode-hook
#           #'(lambda ()
#               (dirtrack-mode 1)
#               (add-hook 'comint-preoutput-filter-functions
#                         'dirtrack-filter-out-pwd-prompt t t)))

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

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

build_prompt() {
	[[ -n ${AG_EMACS_DIR+x} ]] && prompt_emacsdir
	prompt_status
	#[[ -z ${AG_NO_HIST+x} ]] && prompt_histdt
	[[ -z ${AG_NO_CONTEXT+x} ]] && prompt_context
	prompt_virtualenv
	prompt_dir
	prompt_git
	prompt_end
}

# from orig...
# export PS1='$(ansi_single $(text_effect reset)) $(build_prompt) '
# this doesn't work... new model: create a prompt via a PR variable and
# use that.

set_bash_prompt() {
	RETVAL=$?
	PR=""
	PRIGHT=""
	CURRENT_BG=NONE
	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
}

PROMPT_COMMAND=set_bash_prompt
