#!/usr/bin/env bash
# Best bashrc in history
#
# Author: Shane Peters <shane@shaner.life>

# SSH agent
SSH_ENV="$HOME/.ssh/agent-environment"
start_agent() {
    /usr/bin/ssh-agent | sed 's/^echo/#echo/' >"$SSH_ENV"
    chmod 600 "$SSH_ENV"
    . {$SSH_ENV} >/dev/null
    /usr/bin/ssh-add;
}

if [ -f "${SSH_ENV}" ]; then
    . ${SSH_ENV} >/dev/null
    ps -ef | grep $SSH_AGENT_PID | grep ssh-agent$ >/dev/null || {
        start_agent
    }
else
    start_agent
fi

#
# Alternate Paths
#
[[ -d ~/Bin ]] && export PATH=~/Bin:$PATH   # local binaries & scripts
[[ -d ~/Bin/streams ]] && export PATH=~/Bin/streams:$PATH   # radio streams
[[ -d /usr/local/cuda/bin ]] && export PATH=/usr/local/cuda-11.8/bin:$PATH
[[ -d /opt/node/node/bin ]] && export PATH=/opt/node/node/bin:$PATH   # local binaries & scripts
[[ -d /opt/zeek/bin ]] && export PATH=/opt/zeek/bin:$PATH
[[ -d ~/.local/bin ]] && export PATH=~/bin:~/.local/bin:$PATH
[[ -d ~/.cargo/bin ]] && export PATH=~/.cargo/bin:$PATH

#
# App env variables
#
export EDITOR='vim'
export HISTIGNORE="&:[ ]*:ls:ls -a:cd:cd .."    # leave commands out of history log
export HISTCONTROL='ignoredups'
export HISTSIZE=5000
export HISTFILESIZE=5000
export PAGER='less'
export TZ='America/New_York'
export VAULT_TOKEN=root
export VAULT_ADDR=http://127.0.0.1:8200
export LANG=en_US.utf8
export VISUAL=$EDITOR
[[ -x /usr/bin/lesspipe ]] && eval "$(SHELL=/bin/sh lesspipe)"  # less more friendly for non-text

#
# bash options
#
shopt -s cdspell
shopt -s extglob
shopt -s checkwinsize             # updates values of LINES and COLUMNS
shopt -s nocaseglob               # case-insensitive pathname expansion
shopt -s autocd                   # enter directory without cd
shopt -s cdspell                  # correct minor errors in directory names with cd
shopt -s dirspell                 # attempt spelling correction on directory names in completion
#shopt -s dotglob                  # includes filenames beginning with a `.' in filename expansion
shopt -s histappend               # append to history file, instead of overwriting it
shopt -s no_empty_cmd_completion  # not attempt to search for possible completions on an empty line.
shopt -s globstar                 # recursive globbing with **
shopt -s expand_aliases           # expand aliases
shopt -s cmdhist                  # save all lines of a multiline command in same entry
shopt -s lithist                  # save all lines of a multiline command in same entry
stty -ixon                        # disable ^S/^Q flow control

#
# colors
#
if [[ "$OSTYPE" == "darwin"* ]] || [[ "$OSTYPE" == "sun"* ]] ; then
    # BSD/macOS
    export LS_COLORS='ExGxbEaECxxEhEhBaDaCaD'
    echo "BSD/macOS detected: LS_COLORS set for BSD/macOS."

else
    # Linux
    export LS_COLORS='di=34:ln=36:su=31;47:sg=35;47'
    echo "Linux detected: LS_COLORS set for Linux."
fi
export GREP_COLOR='1;36'
export LESS="-R"                                            # less colored output
export LESS_TERMCAP_mb=$'\E[01;31m'                         # begin blinking
export LESS_TERMCAP_md=$'\E[01;38;5;74m'                    # begin bold
export LESS_TERMCAP_me=$'\E[0m'                             # end mode
export LESS_TERMCAP_se=$'\E[0m'                             # end standout-mode
export LESS_TERMCAP_so=$'\E[38;5;246m'                      # begin standout-mode - info box
export LESS_TERMCAP_ue=$'\E[0m'                             # end underline
export LESS_TERMCAP_us=$'\E[04;38;5;146m'                   # begin underline


#
# Aliases
#
alias ..='echo "cd .."; cd ..'
alias ag='rg' # sorry silver searcher
alias chomd='chmod'
alias curl='curl -A "Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0"'
alias externalip='curl -sS https://www.daveeddy.com/ip'
alias gerp='grep'
alias hl='rg --passthru'
alias ls='ls --color=auto'                                 # ls colored output
alias grep='grep --color=auto'                              # grep colored output
alias l='ls'
alias ll='ls -lrtha'
alias suod='sudo'
alias gl='git log -p'
alias wget='wget --user-agent="Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/81.0"'
alias vrd='VAULT_UI=true VAULT_REDIRECT_ADDR=http://127.0.0.1:8200 vault server -log-level=trace -dev -dev-root-token-id=root'
# Git Aliases
alias nb='git checkout -b "$USER-$(date +%s)"' # new branch
alias ga='git add . --all'
alias gb='git branch'
alias gc='git clone'
alias gci='git commit -a'
alias gco='git checkout'
alias gd="git diff ':!*lock'"
alias gdf='git diff' # git diff (full)
alias gi='git init'
alias gl='git log'
alias gp='git push origin HEAD'
alias gr='git rev-parse --show-toplevel' # git root
alias gs='git status'
alias gt='git tag'
alias gu='git pull' # gu = git update

# Prompt
# Store `tput` colors for future use to reduce fork+exec
# the array will be 0-255 for colors, 256 will be sgr0
# and 257 will be bold
COLOR256=()
COLOR256[0]=$(tput setaf 1)
COLOR256[256]=$(tput sgr0)
COLOR256[257]=$(tput bold)

# Colors for use in PS1 that may or may not change when
# set_prompt_colors is run
PROMPT_COLORS=()

# Change the prompt colors to a theme, themes are 0-29
set_prompt_colors() {
	local h=${1:-0}
	local color=
	local i=0
	local j=0
	for i in {22..231}; do
		((i % 30 == h)) || continue

		color=${COLOR256[$i]}
		# cache the tput colors
		if [[ -z $color ]]; then
			COLOR256[$i]=$(tput setaf "$i")
			color=${COLOR256[$i]}
		fi
		PROMPT_COLORS[$j]=$color
		((j++))
	done
}

# Construct the prompt
# [(exit code)] <user> - <hostname> <uname> <cwd> [git branch] <$|#>

# exit code of last process
PS1='$(ret=$?;(($ret!=0)) && echo "\[${COLOR256[0]}\]($ret) \[${COLOR256[256]}\]")'

# username (red for root)
PS1+='\[${PROMPT_COLORS[0]}\]\[${COLOR256[257]}\]$(((UID==0)) && echo "\[${COLOR256[0]}\]")\u\[${COLOR256[256]}\]@'


# hostname
PS1+='\[${PROMPT_COLORS[3]}\]\h '

# zonename (global zone warning)
# PS1+='\[${COLOR256[0]}\]\[${COLOR256[257]}\]'"$(zonename 2>/dev/null | grep -q '^global$' && echo 'GZ:')"'\[${COLOR256[256]}\]'

# uname
#PS1+='\[${PROMPT_COLORS[2]}\]'"$(uname | tr '[:upper:]' '[:lower:]')"' '

# cwd
PS1+='\[${PROMPT_COLORS[5]}\]\w '

# optional git branch
PS1+='$(branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); [[ -n $branch ]] && echo "\[${PROMPT_COLORS[2]}\](\[${PROMPT_COLORS[3]}\]git:$branch\[${PROMPT_COLORS[2]}\]) ")'

# prompt character
PS1+='\[${PROMPT_COLORS[0]}\]\$\[${COLOR256[256]}\] '

# set the theme
set_prompt_colors 24

# Prompt command
_prompt_command() {
        local user=$USER
        local host=${HOSTNAME%%.*}
        local pwd=${PWD/#$HOME/\~}
        local ssh=
        [[ -n $SSH_CLIENT ]] && ssh='[ssh] '
        printf "\033]0;%s%s@%s:%s\007" "$ssh" "$user" "$host" "$pwd"
}
PROMPT_COMMAND=_prompt_command

PROMPT_DIRTRIM=6


#
# Functions
#
sscreate() {
    openssl req \
        -x509 \
        -nodes \
        -newkey rsa:4096 \
        -keyout $1_key.pem \
        -out $1_cert.pem \
        -days 365 \
        -subj "/C=US/ST=Ohio/L=Columbus/O=ColumOps/OU=OPS/CN=$1"
}

compress() {
  FILE=$1
  case $FILE in
    *.tar.bz2) shift && tar cjf $FILE $* ;;
    *.tar.gz) shift && tar czf $FILE $* ;;
    *.tgz) shift && tar czf $FILE $* ;;
    *.zip) shift && zip $FILE $* ;;
    *.rar) shift && rar $FILE $* ;;
    *.7z)  shift && 7za a $FILE $* ;;
  esac
}

extract() {
    local e=0 i c
    for i; do
    if [ -f $i && -r $i ]; then
        c=
        case $i in
            *.tar.bz2) c='tar xjf'    ;;
            *.tar.gz)  c='tar xzf'    ;;
            *.bz2)     c='bunzip2'    ;;
            *.gz)      c='gunzip'     ;;
            *.tar)     c='tar xf'     ;;
            *.tbz2)    c='tar xjf'    ;;
            *.tgz)     c='tar xzf'    ;;
            *.7z)      c='7z x'       ;;
            *.Z)       c='uncompress' ;;
            *.exe)     c='cabextract' ;;
            *.rar)     c='unrar x'    ;;
            *.xz)      c='unxz'       ;;
            *.zip)     c='unzip'      ;;
            *)     echo "$0: cannot extract \`$i': Unrecognized file extension" >&2; e=1 ;;
        esac
        [ $c ] && command $c "$i"
    else
        echo "$0: cannot extract \`$i': File is unreadable" >&2; e=2
    fi
    done
    return $e
}


mkiso() {
    mkisofs -V $2 -J -r $1 -o isoimage.iso
}


# because `master` is sometimes `main` (or others), these must be functions.
gmb() { # git main branch
	local main
	main=$(git symbolic-ref --short refs/remotes/origin/HEAD)
	main=${main#origin/}
	[[ -n $main ]] || return 1
	echo "$main"
}

# show the diff from inside a branch to the main branch
gbd() { # git branch diff
	local mb=$(gmb) || return 1
	git diff "$mb..HEAD"
}

# checkout the main branch and update it
gcm() { # git checkout $main
	local mb=$(gmb) || return 1
	git checkout "$mb" && git pull
}

# merge the main branch into our branch
gmm() { # git merge $main
	local mb=$(gmb) || return 1
	git merge "$mb"
}


# Print all 256 colors
colors() {
	local i
	for i in {0..255}; do
		printf "\x1b[38;5;${i}mcolor %d\n" "$i"
	done
	tput sgr0
}

# Copy stdin to the clipboard
copy() {
	pbcopy 2>/dev/null ||
	    xsel 2>/dev/null ||
	    clip.exe

}

# Convert epoch to human readable (print current date if no args)
epoch() {
	local num=${1:--1}
	printf '%(%B %d, %Y %-I:%M:%S %p %Z)T\n' "$num"
}

# Open the current path or file in GitHub
gho() {
	local file=$1
	local remote=${2:-origin}

	# get the git root dir, branch, and remote URL
	local gr=$(git rev-parse --show-toplevel)
	local branch=$(git rev-parse --abbrev-ref HEAD)
	local url=$(git config --get "remote.$remote.url")

	[[ -n $gr && -n $branch && -n $remote ]] || return 1

	# construct the path
	local path=${PWD/#$gr/}
	[[ -n $file ]] && path+=/$file

	# extract the username and repo name
	local a
	IFS=:/ read -a a <<< "$url"
	local len=${#a[@]}
	local user=${a[len-2]}
	local repo=${a[len-1]%.git}

	url="https://github.com/$user/$repo/tree/$branch$path"
	echo "$url"
	open "$url"
}

# Platform-independent interfaces
interfaces() {
	node <<-EOF
	var os = require('os');
	var i = os.networkInterfaces();
	Object.keys(i).forEach(function(name) {
		i[name].forEach(function(int) {
			if (int.family === 'IPv4') {
				console.log('%s: %s', name, int.address);
			}
		});
	});
	EOF
}

# Calculate CPU load / Core Count
load() {
	node -p <<-EOF
	var os = require('os');
	var c = os.cpus().length;
	os.loadavg().map(function(l) {
		return (l/c).toFixed(2);
	}).join(' ');
	EOF
}

# Platform-independent memory usage
meminfo() {
	node <<-EOF
	var os = require('os');
	var free = os.freemem();
	var total = os.totalmem();
	var used = total - free;
	console.log('memory: %dmb / %dmb (%d%%)',
	    Math.round(used / 1024 / 1024),
	    Math.round(total / 1024 / 1024),
	    Math.round(used * 100 / total));
	EOF
}

# print lines over X columns (defaults to 80)
over() {
	awk -v c="${1:-80}" 'length($0) > c {
		printf("%4d %s\n", NR, $0);
	}'
}

# print a rainbow if truecolor is available to the terminal
truecolor-rainbow() {
	local i r g b
	for ((i = 0; i < 77; i++)); do
		r=$((255 - (i * 255 / 76)))
		g=$((i * 510 / 76))
		b=$((i * 255 / 76))
		((g > 255)) && g=$((510 - g))
		printf '\033[48;2;%d;%d;%dm ' "$r" "$g" "$b"
	done
	tput sgr0
	echo
}

# Follow redirects to untiny a tiny url
untiny() {
	local location=$1
	local last_location=''

	while [[ -n $location ]]; do
		[[ -n $last_location ]] && echo " -> $last_location"
		last_location=$location
		read -r _ location < \
		    <(curl -sI "$location" | grep 'Location: ' | tr -d '[:cntrl:]')
	done
	echo "$last_location"
}

# load completions
source /usr/share/bash-completion/completions/fzf
source /usr/share/doc/fzf/examples/key-bindings.bash
source /etc/bash/bash_completion 2>/dev/null

# done
true
