prompt_fresh_setup() {
  compile_git_prompt

  autoload -U add-zsh-hook
  add-zsh-hook precmd fresh_precmd

  if [ "$USERNAME" = "root" ]; then
    local user="%{%F{9}%}%n%{%f%}"
  else
    local user="%{%F{35}%}%n%{%f%}"
  fi

  if [ "$SSH_CONNECTION" != "" ]; then
    local user="%{%F{35}%}$user%{%f%}@%{%F{244}%}%M%{%f%}"
  fi

  local length=`visible_length "$user"`

  PS1="«$user» "
  PS2="${(l:$length + 1:: :)}» "

  RPS1='$(fresh_rprompt)'

  prompt_opts=(cr percent sp subst)
}

prompt_cleanup() {
  if [ -f ~/.cache/zsh/git-prompt ]; then rm ~/.cache/zsh/git-prompt; fi
}

fresh_precmd() {
  # First get the last commands exit code before doing anything
  local exit_code=$?

  # Construct the time and directory portions of the prompt
  local time_stamp="%{%F{244}%}%D{%H:%M:%S}%{%f%}"
  local directory="%{%F{37}%}%~%{%f%}"

  # Check we are in a git repository
  if `git rev-parse --git-dir > /dev/null 2>&1`; then
    # Get git branch name & exit early if not found
    local branch="`git symbolic-ref HEAD 2> /dev/null`"
    if [ "" = "$branch" ]; then
      local branch="`git rev-parse --abbrev-ref HEAD`"
      if [ "HEAD" = "$branch" ]; then
        local branch="`git rev-parse --short $branch`"
      fi
    fi

    if [ "$branch" != "" ]; then
      local branch=${branch#refs/heads/}
      # Get the number of commits ahead/behind from the remote branch
      local remote="`git config branch.$branch.remote 2> /dev/null`"
      if [ "" != "remote" ]; then
        local branches="refs/remotes/$remote/$branch...HEAD"
        local nahead=`git rev-list --right-only $branches --count 2> /dev/null`
        if [ "0" -ne "$nahead" ]; then local ahead="↑$nahead"; fi
        local nbehind=`git rev-list --left-only $branches --count 2> /dev/null`
        if [ "0" -ne "$nbehind" ]; then local behind="↓$nbehind"; fi
      fi

      # Construct the git prompt
      local git="%{%F{66}%}$branch%{%f%}$ahead$behind "

      if [ -f ~/.cache/zsh/git-prompt ]; then
        # Get the change counts from git-prompt
        local counts=(`~/.cache/zsh/git-prompt`)

        # Parse the results from git-prompt
        if [ 0 -eq ${#counts[@]} ]; then # clean
          local git="$git%{%B%F{2}%}✓%{%f%b%}";
        else
          if [ 0 != $counts[1] ]; then  # indexed
            local git="$git%{%F{2}%}*$counts[1]%{%f%}"
          fi
          if [ 0 != $counts[2] ]; then  # modified
            local git="$git%{%F{1}%}+$counts[2]%{%f%}";
          fi
          if [ 0 != $counts[3] ]; then  # deleted
            local git="$git%{%F{1}%}-$counts[3]%{%f%}";
          fi
          if [ 0 != $counts[4] ]; then  # unmerged
            local git="$git%{%B%F{1}%}×$counts[4]%{%f%b%}";
          fi
          if [ 0 != $counts[5] ]; then  # untracked
            local git="$git%{%F{1}%}…%{%f%}";
          fi
        fi
      fi
    fi
  fi

  # Construct the prompt string
  local line="$time_stamp $directory $git"

  # Print the first line of the prompt
  if [[ $exit_code -eq 0 ]]; then
    print -P "$line"
  else
    # The last command failed, display its error code at the right
    local result="%{%B%F{1}%}$exit_code%{%f%b%}"
    local length=`visible_length "$line$result"`
    print -P "$line${(l:COLUMNS - $length - 1:: :)}$result"
  fi
}

fresh_rprompt() {
  rprompt=""
  if [[ ! -z "$DOCKER_MACHINE_NAME" ]]; then
    rprompt="$rprompt %{%F{3}%}$DOCKER_MACHINE_NAME%{%f%}"
  fi
  if [[ ! -z "$VIRTUAL_ENV" ]]; then
    rprompt="$rprompt %{%F{3}%}$(basename $VIRTUAL_ENV)%{%f%}"
  fi
  echo $rprompt
}

visible_length() {
  echo $(( ${#${(S%%)1//(\%(KF1]|)\{*\}|\%[Bbkf])}} ))
}

compile_git_prompt() {
  # Compile a simple C executable which parses the output of `git status
  # --procelain` to greatly decrease the time taken to render the prompt
  if [ ! -d ~/.cache/zsh ]; then mkdir -p ~/.cache/zsh; fi
  if [ ! -f ~/.cache/zsh/git-prompt ]; then
    cc -x c -std=gnu99 -O3 -DNDEBUG -o ~/.cache/zsh/git-prompt - << EOF
#include <stdio.h>
#include <stdlib.h>

int main() {
  FILE *file = popen("git status --porcelain", "r");
  if (NULL == file) { return 0; }

  char status[2048];
  int indexed = 0, modified = 0, deleted = 0, untracked = 0, unmerged = 0;

  while (NULL != fgets(status, sizeof(status) - 1, file)) {
    char X = status[0];
    char Y = status[1];

    if (X == '?' && Y == '?') {
      ++untracked;
    } else if ((X == 'A' && (Y == 'A' || Y == 'U')) ||
               (X == 'D' && (Y == 'D' || Y == 'U')) ||
               (X == 'U' && (Y == 'A' || Y == 'D' || Y == 'D' || Y == 'U'))) {
      ++unmerged;
    } else {
      switch (X) {
      case ' ':
        switch (Y) {
          case 'M': ++modified; break;
          case 'D': ++deleted; break;
        } break;
      case 'D': ++indexed;
        switch (Y) {
          case ' ': break;
          case 'M': ++modified; break;
        } break;
      case 'M': case 'A': case 'R': case 'C': ++indexed;
        switch (Y) {
          case ' ': break;
          case 'M': ++modified; break;
          case 'D': ++deleted; break;
        } break;
      }
    }
  }

  pclose(file);

  if (indexed || modified || deleted || unmerged || untracked) {
    printf("%d %d %d %d %d", indexed, modified, deleted, unmerged, untracked);
  }

  return 0;
}
EOF
  fi
}

prompt_fresh_setup "$@"

# vim:ft=zsh