#!/usr/bin/env bash
# Summary: Configure the shell environment for pyenv-virtualenv
# Usage: eval "$(pyenv virtualenv-init - [<shell>])"
#
# Automatically activates a Python virtualenv environment based on current
# pyenv version.
#

set -e
[ -n "$PYENV_DEBUG" ] && set -x

# Detect stat format for mtime: GNU uses -c %Y, BSD uses -f %m
# -L follows symlinks: a symlinked .python-version reflects target changes
if stat -L -c %Y / >/dev/null 2>&1; then
  _stat_fmt="-L -c %Y"
else
  _stat_fmt="-L -f %m"
fi

# Check for version-name hooks at init time. Hooks can alter version
# resolution in ways the mtime cache cannot track. If present, the hook
# falls back to upstream behavior (no caching). Restart shell after
# installing or removing pyenv plugins.
_has_version_hooks=""
if [ -n "$(pyenv hooks version-name 2>/dev/null)" ]; then
  _has_version_hooks=1
fi

resolve_link() {
  $(type -p greadlink readlink | head -1) "$1"
}

abs_dirname() {
  local cwd="$(pwd)"
  local path="$1"

  while [ -n "$path" ]; do
    cd "${path%/*}"
    local name="${path##*/}"
    path="$(resolve_link "$name" || true)"
  done

  pwd
  cd "$cwd"
}

PYENV_VIRTUALENV_INSTALL_PREFIX="$(dirname "$(abs_dirname "$0")")"

print=""
for args in "$@"
do
  if [ "$args" = "-" ]; then
    print=1
    shift
  fi
done

shell="${1:-$PYENV_SHELL}"
if [ -z "$shell" ]; then
  shell="$(ps -p "$PPID" -o 'args=' 2>/dev/null || true)"
  shell="${shell##-}"
  shell="${shell%% *}"
  shell="${shell:-$SHELL}"
  shell="${shell##*/}"
  shell="${shell%%-*}"
fi

if [ -z "$print" ]; then
  case "$shell" in
  bash )
    profile='~/.bashrc'
    ;;
  zsh )
    profile='~/.zshrc'
    ;;
  ksh )
    profile='~/.profile'
    ;;
  fish )
    profile='~/.config/fish/config.fish'
    ;;
  * )
    profile='your profile'
    ;;
  esac

  { echo "# Load pyenv-virtualenv automatically by adding"
    echo "# the following to ${profile}:"
    echo
    case "$shell" in
    fish )
      echo 'status --is-interactive; and source (pyenv virtualenv-init -|psub)'
      ;;
    * )
      echo 'eval "$(pyenv virtualenv-init -)"'
      ;;
    esac
    echo
  } >&2

  exit 1
fi

case "$shell" in
fish )
  cat <<EOS
while set index (contains -i -- "${PYENV_VIRTUALENV_ROOT:-${PYENV_VIRTUALENV_INSTALL_PREFIX}}/shims" \$PATH)
set -eg PATH[\$index]; end; set -e index
set -gx PATH '${PYENV_VIRTUALENV_ROOT:-${PYENV_VIRTUALENV_INSTALL_PREFIX}}/shims' \$PATH;
set -gx PYENV_VIRTUALENV_INIT 1;
EOS
  ;;
* )
  cat <<EOS
export PATH="${PYENV_VIRTUALENV_ROOT:-${PYENV_VIRTUALENV_INSTALL_PREFIX}}/shims:\${PATH}";
export PYENV_VIRTUALENV_INIT=1;
EOS
  ;;
esac

case "$shell" in
fish )
  cat <<EOS
function _pyenv_virtualenv_hook --on-event fish_prompt;
  set -l ret \$status
EOS

  if [ -z "$_has_version_hooks" ]; then
    cat <<EOS
  if test "\$PYENV_VERSION" = "\$_PYENV_VH_VERSION" \\
    -a "\$VIRTUAL_ENV" = "\$_PYENV_VH_VENV"
    if test -n "\$PYENV_VERSION"
      return \$ret
    end
    if test "\$PWD" = "\$_PYENV_VH_PWD" \\
      -a "(stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)" = "\$_PYENV_VH_MTIMES"
      return \$ret
    end
  end
EOS
  fi
  
  cat <<EOS
  if [ -n "\$VIRTUAL_ENV" ]
    pyenv activate --quiet; or pyenv deactivate --quiet; or true
  else
    pyenv activate --quiet; or true
  end
EOS

  if [ -z "$_has_version_hooks" ]; then
    cat <<EOS
  set -g _PYENV_VH_PWD "\$PWD"
  set -g _PYENV_VH_VERSION "\$PYENV_VERSION"
  set -g _PYENV_VH_VENV "\$VIRTUAL_ENV"
  set -l d "\$PWD"
  set -l _pvh_found_local 0
  set -g _PYENV_VH_PATHS
  while true
    if test -f "\$d/.python-version"; or test -L "\$d/.python-version"
      set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d/.python-version"
      if test -f "\$d/.python-version" 
        set _pvh_found_local 1
        break
      end
    else
      set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$d"
    end
    test "\$d" = "/"; and break
    set d (string replace -r '/[^/]*\$' '' -- "\$d")
    test -z "\$d"; and set d "/"
  end
  if test "\$_pvh_found_local" = "0"
    set -g _PYENV_VH_PATHS \$_PYENV_VH_PATHS "\$PYENV_ROOT/version"
  end
  set -g _PYENV_VH_MTIMES (stat ${_stat_fmt} \$_PYENV_VH_PATHS 2>/dev/null)
EOS
  fi
  
  cat <<EOS
  return \$ret
end
EOS
  ;;

bash|zsh )
  cat <<EOS
_pyenv_virtualenv_hook() {
  local ret=\$?
EOS

  if [ -z "$_has_version_hooks" ]; then
    cat <<EOS
  if [ "\${PYENV_VERSION-}" = "\${_PYENV_VH_VERSION-}" ] \\
    && [ "\${VIRTUAL_ENV-}" = "\${_PYENV_VH_VENV-}" ]; then
    if [ -n "\${PYENV_VERSION-}" ]; then
      return \$ret
    fi
    if [ "\${PWD}" = "\${_PYENV_VH_PWD-}" ] \\
      && [ "\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)" = "\${_PYENV_VH_MTIMES-}" ]; then
      return \$ret
    fi
  fi
EOS
  fi

  cat <<EOS
  if [ -n "\${VIRTUAL_ENV-}" ]; then
    eval "\$(pyenv sh-activate --quiet || pyenv sh-deactivate --quiet || true)" || true
  else
    eval "\$(pyenv sh-activate --quiet || true)" || true
  fi
EOS

  if [ -z "$_has_version_hooks" ]; then
    cat <<EOS
  _PYENV_VH_PWD="\${PWD}"
  _PYENV_VH_VERSION="\${PYENV_VERSION-}"
  _PYENV_VH_VENV="\${VIRTUAL_ENV-}"
  local _pvh_d="\${PWD}" _pvh_found_local=0
  _PYENV_VH_PATHS=()
  while :; do
    if [ -f "\${_pvh_d}/.python-version" ] || [ -L "\${_pvh_d}/.python-version" ]; then
      _PYENV_VH_PATHS+=("\${_pvh_d}/.python-version")
      if [ -f "\${_pvh_d}/.python-version" ]; then 
        _pvh_found_local=1
        break
      fi
    else
      _PYENV_VH_PATHS+=("\${_pvh_d}")
    fi
    [ "\${_pvh_d}" = "/" ] && break
    _pvh_d="\${_pvh_d%/*}"
    [ -z "\${_pvh_d}" ] && _pvh_d="/"
  done
  if [ "\${_pvh_found_local}" = "0" ]; then
    _PYENV_VH_PATHS+=("\${PYENV_ROOT}/version")
  fi
  _PYENV_VH_MTIMES="\$(stat ${_stat_fmt} "\${_PYENV_VH_PATHS[@]}" 2>/dev/null)"
EOS
  fi

  cat <<EOS
  return \$ret
};
EOS
  ;;
esac

case "$shell" in
bash )
  cat <<EOS
if ! [[ "\${PROMPT_COMMAND-}" =~ _pyenv_virtualenv_hook ]]; then
  PROMPT_COMMAND="_pyenv_virtualenv_hook;\${PROMPT_COMMAND-}"
fi
EOS
  ;;
zsh )
  cat <<EOS
typeset -g -a precmd_functions
if [[ -z \$precmd_functions[(r)_pyenv_virtualenv_hook] ]]; then
  precmd_functions=(_pyenv_virtualenv_hook \$precmd_functions);
fi
EOS
  ;;
* )
  # No prompt command support or it's installed elsewhere
  ;;
esac
