#!/usr/bin/env bash

# Based on https://github.com/undu/bash-powerline

__powerline() {

  # User config variables,
  # it's recommended to override those variables through .bashrc or similar
  #
  # Use powerline mode
  # readonly POWERLINE_FONT=''
  #
  # Always show user in the prompt
  # readonly SHOW_USER=''
  #
  # Never show a default user
  # readonly DEFAULT_USER='user'

  # Default background and foreground ANSI colours
  readonly DEFAULT_BG=0
  readonly DEFAULT_FG=7

  # Max length of full path
  readonly MAX_PATH_LENGTH=30

  # Unicode symbols
  if [ -z "${POWERLINE_FONT+x}" ]; then
    readonly GIT_BRANCH_SYMBOL='⑂'
  else
    readonly GIT_BRANCH_SYMBOL=''
  fi
  readonly GIT_BRANCH_CHANGED_SYMBOL='Δ'
  readonly GIT_NEED_PUSH_SYMBOL='↑'
  readonly GIT_NEED_PULL_SYMBOL='↓'

  # Powerline symbols
  readonly BLOCK_START=''

  # ANSI Colours
  readonly BLACK=0
  readonly RED=1
  readonly GREEN=2
  readonly YELLOW=3
  readonly BLUE=4
  readonly MAGENTA=5
  readonly CYAN=6
  readonly WHITE=7

  readonly BLACK_BRIGHT=8
  readonly RED_BRIGHT=9
  readonly GREEN_BRIGHT=10
  readonly YELLOW_BRIGHT=11
  readonly BLUE_BRIGHT=12
  readonly MAGENTA_BRIGHT=13
  readonly CYAN_BRIGHT=14
  readonly WHITE_BRIGHT=15

  # Font effects
  readonly DIM="\[$(tput dim)\]"
  readonly REVERSE="\[$(tput rev)\]"
  readonly RESET="\[$(tput sgr0)\]"
  readonly BOLD="\[$(tput bold)\]"

  # Generate terminal colour codes
  # $1 is an int (a colour) and $2 must be 'fg' or 'bg'
  __colour() {
    case "$2" in
      'fg'*)
        echo "\[$(tput setaf "$1")\]"
        ;;
      'bg'*)
        echo "\[$(tput setab "$1")\]"
        ;;
      *)
        echo "\[$(tput setab "$1")\]"
        ;;
    esac
  }

  # Generate a single-coloured block for the prompt
  __prompt_block() {
    local bg; local fg
    if [ ! -z "${1+x}" ]; then
      bg=$1
    else
      if [ ! -z "$last_bg" ]; then
        bg=$last_bg
      else
        bg=$DEFAULT_BG
      fi
    fi
    if [ ! -z "${2+x}" ]; then
      fg=$2
    else
      fg=$DEFAULT_FG
    fi

    local block

    # Need to generate a separator if the background changes
    if [[ ! -z "$last_bg" && "$bg" != "$last_bg" && ! -z "${POWERLINE_FONT+x}" ]]; then
      block+="$(__colour "$bg" 'bg')"
      block+="$(__colour "$last_bg" 'fg')"
      block+="$BLOCK_START $RESET"
      block+="$(__colour "$bg" 'bg')"
      block+="$(__colour "$fg" 'fg')"
    else
      block+="$(__colour "$bg" 'bg')"
      block+="$(__colour "$fg" 'fg')"
      block+=" "
    fi

    if [ ! -z "${3+x}" ]; then
      block+="$3 $RESET"
    fi

    last_bg=$bg

    __block_text="$block"
  }

  function __end_block() {
    __block_text=''
    if [ ! -z "$last_bg" ]; then
      if [ ! -z "${POWERLINE_FONT+x}" ]; then
        __block_text+="$(__colour $DEFAULT_BG 'bg')"
        __block_text+="$(__colour "$last_bg" 'fg')"
        __block_text+="$BLOCK_START$RESET"
        __block_text+="$(__colour $DEFAULT_BG 'bg')"
        __block_text+="$(__colour "$DEFAULT_FG" 'fg')"
      else
        __block_text+="$(__colour $DEFAULT_BG 'bg')"
        __block_text+="$(__colour "$DEFAULT_FG" 'fg')"
      fi
    fi
    __block_text+=' '
  }

  ### Prompt components

  __git_block() {
    if ! command -V git > /dev/null; then
      # git not found
      __block_text=''
      return
    fi
    # force git output in English to make our work easier
    local git_eng="env LANG=C git"

    # check if pwd is under git
    if ! git rev-parse --is-inside-git-dir > /dev/null 2> /dev/null; then
      # not in a git repo, bail out
      __block_text=''
      return
    fi

    # get current branch name or short SHA1 hash for detached head
    local branch; local ref_symbol
    branch="$($git_eng symbolic-ref --short HEAD 2>/dev/null)"
    # shellcheck disable=SC2181
    if [ $? != 0 ]; then
      branch="$($git_eng describe --tags --always 2>/dev/null)"
      ref_symbol='➦'
    else
      ref_symbol=$GIT_BRANCH_SYMBOL
    fi

    # In pcmode (and only pcmode) the contents of
    # $gitstring are subject to expansion by the shell.
    # Avoid putting the raw ref name in the prompt to
    # protect the user from arbitrary code execution via
    # specially crafted ref names (e.g., a ref named
    # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
    # 'sudo rm -rf /' when the prompt is drawn).  Instead,
    # put the ref name in a new global variable (in the
    # __git_ps1_* namespace to avoid colliding with the
    # user's environment) and reference that variable from
    # PS1.
    # note that the $ is escaped -- the variable will be
    # expanded later (when it's time to draw the prompt)
    if shopt -q promptvars; then
      export __git_ps1_block="$branch"
      ref="$ref_symbol \${__git_ps1_block}"
    else
      ref="$ref_symbol $branch"
    fi

    local marks

    # check if HEAD is dirty
    if [ -n "$($git_eng status --porcelain 2>/dev/null)" ]; then
      dirty='y'
      marks+=" $GIT_BRANCH_CHANGED_SYMBOL"
    fi

    # how many commits local branch is ahead/behind of remote?
    local stat; local aheadN; local behindN
    stat="$($git_eng status --porcelain --branch 2>/dev/null | grep '^##' | grep -o '\[.\+\]$')"
    aheadN="$(echo "$stat" | grep -o 'ahead [[:digit:]]\+' | grep -o '[[:digit:]]\+')"
    behindN="$(echo "$stat" | grep -o 'behind [[:digit:]]\+' | grep -o '[[:digit:]]\+')"
    [ -n "$aheadN" ] && marks+=" $GIT_NEED_PUSH_SYMBOL$aheadN"
    [ -n "$behindN" ] && marks+=" $GIT_NEED_PULL_SYMBOL$behindN"

    local bg; local fg
    fg=$BLACK
    if [ -z "$dirty" ]; then
      bg=$GREEN
    else
      bg=$YELLOW
    fi

    __prompt_block $bg $fg "$ref$marks"
  }

  __virtualenv_block() {
    # Copied from Python virtualenv's activate.sh script.
    # https://github.com/pypa/virtualenv/blob/a9b4e673559a5beb24bac1a8fb81446dd84ec6ed/virtualenv_embedded/activate.sh#L62
    # License: MIT
    if [ -n "$VIRTUAL_ENV" ]; then
      local venv
      # In pcmode (and only pcmode) the contents of
      # $gitstring are subject to expansion by the shell.
      # Avoid putting the raw ref name in the prompt to
      # protect the user from arbitrary code execution via
      # specially crafted ref names (e.g., a ref named
      # '$(IFS=_;cmd=sudo_rm_-rf_/;$cmd)' would execute
      # 'sudo rm -rf /' when the prompt is drawn).  Instead,
      # put the ref name in a new global variable (in the
      # __git_ps1_* namespace to avoid colliding with the
      # user's environment) and reference that variable from
      # PS1.
      # note that the $ is escaped -- the variable will be
      # expanded later (when it's time to draw the prompt)
      if shopt -q promptvars; then
        export __venv_ps1_block
        __venv_ps1_block=$(basename "$VIRTUAL_ENV")
        venv="$ref_symbol \${__venv_ps1_block}"
      else
        venv="$(basename "$VIRTUAL_ENV")"
      fi
      __prompt_block $WHITE $BLACK "$venv"
    else
      __block_text=''
    fi
  }

  __pwd_block() {
    # Use ~ to represent $HOME prefix
    local pwd; pwd=$(pwd | sed -e "s|^$HOME|~|")
    # shellcheck disable=SC1001,SC2088
    if [[ ( $pwd = ~\/*\/* || $pwd = \/*\/*/* ) && ${#pwd} -gt $MAX_PATH_LENGTH ]]; then
      local IFS='/'
      read -ra split <<< "$pwd"
      if [[ $pwd = ~* ]]; then
        pwd="~/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}"
      else
        pwd="/${split[1]}/.../${split[*]:(-2):1}/${split[*]:(-1)}"
      fi
    fi
    __prompt_block $BLACK_BRIGHT $WHITE_BRIGHT "$pwd"
  }

  # superuser or not, here I go!
  __user_block() {
    # Colours to use
    local fg=$WHITE_BRIGHT
    local bg=$BLUE

    if [[  ! -z "$SSH_CLIENT" ]]; then
      local show_host="y"
      bg=$CYAN
    fi

    if [ -z "$(id -u "$USER")" ]; then
      bg=$RED
    fi

    # shellcheck disable=SC2153
    if [[ ! -z "${SHOW_USER+x}" || (  ! -z "${DEFAULT_USER+x}" && "$DEFAULT_USER" != "$(whoami)" ) ]]; then
      local show_user="y"
    fi

    local text
    if [ ! -z ${show_user+x} ]; then
      text+="$BOLD$(whoami)"
    fi
    if [ ! -z ${show_host+x} ]; then
      if [ ! -z "${text+x}" ]; then
        text+="@"
      fi
      text+="\h"
    fi

    if [ ! -z ${text+x} ]; then
      __prompt_block $bg $fg $text
    fi
  }

  __status_block() {
    local text
    if [ "$exit_code" != 0 ]; then
      __prompt_block $BLACK $RED '✘'
      text+=$__block_text
    fi

    if [ "$(id -u "$USER")" == 0 ]; then
      __prompt_block $BLACK $YELLOW '⚡'
      text+=$__block_text
    fi

    if [ "$(jobs -l | wc -l)" != 0 ]; then
      __prompt_block $BLACK $CYAN '⚙'
      text+=$__block_text
    fi

    if [ ! -z "$text" ]; then
      __block_text=$text
    else
      __block_text=''
    fi
  }

  # Build the prompt
  prompt() {
    # I don't like bash; execute first to capture correct status code
    local exit_code=$?
    # shellcheck disable=SC2091
    $(history -a ; history -n)

    last_bg=''

    PS1=''

    __status_block
    PS1+=$__block_text

    __virtualenv_block
    PS1+=$__block_text

    __user_block
    PS1+=$__block_text

    __pwd_block
    PS1+=$__block_text

    __git_block
    PS1+=$__block_text

    __end_block
    PS1+=$__block_text
  }

  PROMPT_COMMAND=prompt
}

__powerline
unset __powerline
