#!/bin/bash
# Brought from gugod's perlbrew.
# Authors:
#   - Yo-An Lin
#   - Márcio Almada

# PHPBrew defaults:
# PHPBREW_HOME: contains the phpbrew config (for users)
# PHPBREW_ROOT: contains installed php(s) and php source files.
# PHPBREW_SKIP_INIT: if you need to skip loading config from the init file.
# PHPBREW_PHP:  the current php version.
# PHPBREW_PATH: the bin path of the current php.
# PHPBREW_SYSTEM_PHP: the path to the system php binary.

[[ -z "$PHPBREW_ROOT" ]] && export PHPBREW_ROOT="$HOME/.phpbrew"
[[ -z "$PHPBREW_HOME" ]] && export PHPBREW_HOME="$HOME/.phpbrew"

# The minimal PHP version that PhpBrew supports as interpreter
MIN_PHP_VERSION="7.2.0"
MIN_PHP_VERSION_ID=70200

# Returns the absolute path corresponding to the command excluding the alias
function __phpbrew_which()
{
    command which "$1"
}

# Executes the given command via the PHP implementation
function __phpbrew_php_exec()
{
    local cmd

    # Check if we are in a PHPBrew source directory (this is only for development)
    if [[ -e bin/phpbrew ]] ; then
        cmd=bin/phpbrew
    else
        cmd="$(__phpbrew_which phpbrew)"
    fi

    # Force the usage of the system PHP interpreter if it's set
    if [[ -n "$PHPBREW_SYSTEM_PHP" ]] ; then
        command "$PHPBREW_SYSTEM_PHP" "$cmd" "$@"
    else
        command "$cmd" "$@"
    fi
}

# Normalizes a PHP build by adding the "php-" prefix if it's missing
function __phpbrew_normalize_build()
{
    if [[ ! -d "$PHPBREW_ROOT/php/$1" && "$1" =~ ^([[:digit:]]+\.){2}[[:digit:]]+(-dev|((alpha|beta|RC)[[:digit:]]+))?$ ]] ; then
        echo "php-$1"
    else
        echo "$1"
    fi
}

# Returns the PHP binary path for a given version
function __phpbrew_get_version_bin()
{
    if [[ ! "$1" == /* ]] ; then
        echo "$PHPBREW_ROOT/php/$(__phpbrew_normalize_build $1)/bin/php"
    else
        echo "$1"
    fi
}

# Validates the PHP binary that is to be used as interpreter
function __phpbrew_validate_interpreter()
{
    if [[ -d "$1" ]] ; then
        echo "$1 is a directory"
        return 1
    fi

    if [[ ! -f "$1" ]] ; then
        echo "$1 not found"
        return 1
    fi

    if [[ ! -x "$1" ]] ; then
        echo "$1 is not executable"
        return 1
    fi

    local PHP_VERSION_ID=$(command "$1" -r "echo PHP_VERSION_ID;")

    if [[ $? -ne 0 ]] ; then
        return 1
    fi

    if [[ $PHP_VERSION_ID -lt $MIN_PHP_VERSION_ID ]] ; then
        echo "Only PHP $MIN_PHP_VERSION or newer can be used as PHPBrew interpreter"
        return 1
    fi
}

# Checks whether the given PHP build can be currently used or switched to
function __phpbrew_can_use_build()
{
    if [[ ! -z "$PHPBREW_SYSTEM_PHP" ]] ; then
        # Can use any version since the system interpreter is set
        return
    fi

    __phpbrew_validate_interpreter "$(__phpbrew_get_version_bin $1)"

    if [[ $? -ne 0 ]] ; then
        echo "The system interpreter is not currently set"
        echo "Please execute 'phpbrew system' using PHP $MIN_PHP_VERSION or newer before using an older one"
        return 1
    fi
}

function __phpbrew_set_path()
{
    local PATH_WITHOUT_PHPBREW

    if [[ -n "$PHPBREW_ROOT" ]] ; then
        PATH_WITHOUT_PHPBREW=$(p=$(echo $PATH | tr ":" "\n" | grep -v "^$PHPBREW_ROOT" | tr "\n" ":"); echo ${p%:})
    else
        PATH_WITHOUT_PHPBREW=$PATH
    fi

    if [[ -z "$PHPBREW_PATH" ]]
    then
        export PATH=$PATH_WITHOUT_PHPBREW
    else
        export PATH=$PHPBREW_PATH:$PATH_WITHOUT_PHPBREW
    fi
}

function __phpbrew_load_user_config()
{
    # load user-defined config
    if [[ -f $PHPBREW_HOME/init ]]; then
        . $PHPBREW_HOME/init
        __phpbrew_set_path
    fi
}

if [[ -z "$PHPBREW_SKIP_INIT" ]]; then
    __phpbrew_load_user_config
fi

[[ -e "$PHPBREW_ROOT" ]] || mkdir "$PHPBREW_ROOT"
[[ -e "$PHPBREW_HOME" ]] || mkdir "$PHPBREW_HOME"

# When setting lookup prefix, the alias name will be translated into the real
# path.
#
# Homebrew uses it's own prefix system with cellars, it's more dynamic then
# others.
#
# Detect package system automatically
function __phpbrew_set_lookup_prefix ()
{
    case $1 in
        debian|ubuntu|linux)
            # echo /usr/lib/x86_64-linux-gnu:/usr/lib/i386-linux-gnu
            echo /usr
        ;;
        macosx)
            echo /usr
        ;;
        macports)
            echo /opt/local
        ;;
        homebrew)
            # TODO: make this more dynamic. with brew --prefix call.
            echo /usr/local/opt:/usr/local
        ;;
        *)
            if [[ -e $1 ]] ; then
                echo $1
            else
                for dir in /opt/local /usr/local/Cellar /usr/local /usr ; do
                    if [[ -e $dir ]] ; then
                        echo $dir
                        return
                    fi
                done
            fi
        ;;
    esac
}

function phpbrew ()
{
    local short_option
    if [[ `echo $1 | awk 'BEGIN{FS=""}{print $1}'` = '-' ]]
    then
        short_option=$1
        shift
    else
        short_option=""
    fi

    case $1 in
        use)
            if [[ -z "$2" ]] ; then
                if [[ -z "$PHPBREW_PHP" ]]
                then
                    echo "Currently using system php"
                else
                    echo "Currently using $PHPBREW_PHP"
                fi

                return
            fi

            if ! output="$(__phpbrew_php_exec env $(__phpbrew_normalize_build $2))"; then
                echo "$output"
                return 1
            fi

            eval "$output"
            __phpbrew_set_path
            ;;
        cd-src)
            local SOURCE_DIR=$PHPBREW_HOME/build/$PHPBREW_PHP
            if [[ -d $SOURCE_DIR ]] ; then
                builtin cd $SOURCE_DIR
            fi
            ;;
        switch)
            if [[ -z "$2" ]]; then
                echo "Please specify the php version."
                return 1
            fi

            __phpbrew_reinit $(__phpbrew_normalize_build $2)
            ;;
        lookup-prefix)
            if [[ -z "$2" ]] ; then
                if [[ -n $PHPBREW_LOOKUP_PREFIX ]] ; then
                    echo $PHPBREW_LOOKUP_PREFIX
                fi
            else
                export PHPBREW_LOOKUP_PREFIX=$(__phpbrew_set_lookup_prefix $2)
                echo $PHPBREW_LOOKUP_PREFIX
                __phpbrew_update_config
            fi
            ;;
        cd)
            case $2 in
                var)
                    local chdir=$PHPBREW_ROOT/php/$PHPBREW_PHP/var
                    ;;
                etc)
                    local chdir=$PHPBREW_ROOT/php/$PHPBREW_PHP/etc
                    ;;
                dist)
                    local chdir=$PHPBREW_ROOT/php/$PHPBREW_PHP
                    ;;
                build)
                    local chdir=$PHPBREW_ROOT/build/$PHPBREW_PHP
                    ;;
                *)
                    echo "$2 not found"
                    return 0
                ;;
            esac
            echo "Switching to $chdir, run 'cd -' to go back."
            builtin cd $chdir
            return 0
            ;;
        each)
            shift
            __phpbrew_each "$@"
            ;;
        fpm)
            if ! [[ -z $3 ]]; then
                PHP_BUILD=$3
            else
                PHP_BUILD=${PHPBREW_PHP}
            fi

            mkdir -p ${PHPBREW_ROOT}/php/${PHP_BUILD}/var/run
            PHPFPM_BIN=${PHPBREW_ROOT}/php/${PHP_BUILD}/sbin/php-fpm
            PHPFPM_PIDFILE=${PHPBREW_ROOT}/php/${PHP_BUILD}/var/run/php-fpm.pid

            function fpm_start()
            {
              echo "Starting php-fpm..."
              local regex="^php-5\.2.*"

              if [[ ${PHP_BUILD} =~ ${regex} ]]; then
                ${PHPFPM_BIN} start
              else
                ${PHPFPM_BIN} --php-ini ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/fpm/php.ini \
                  --fpm-config ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf \
                  --pid ${PHPFPM_PIDFILE} \
                  "$@"
              fi

              if [[ $? != "0" ]] ; then
                echo "php-fpm start failed."
              fi
            }
            function fpm_stop()
            {
              local regex="^php-5\.2.*"

              if [[ ${PHPBREW_PHP} =~ ${regex} ]]; then
                ${PHPFPM_BIN} stop
              elif [[ -e ${PHPFPM_PIDFILE} ]] ; then
                echo "Stopping php-fpm..."
                kill $(cat ${PHPFPM_PIDFILE})
                rm -f ${PHPFPM_PIDFILE}
              fi
            }
            case $2 in
                start)
                    fpm_start "${@:4}"
                    ;;
                stop)
                    fpm_stop
                    ;;
                setup)
                    __phpbrew_php_exec $short_option "$@"
                    ;;
                restart)
                    fpm_stop
                    fpm_start "${@:4}"
                    ;;
                current)
                    if [[ -f $PHPFPM_PIDFILE ]] ; then
                        ps ux -p "$(cat $PHPFPM_PIDFILE)"
                    fi
                    ;;
                module)
                    $PHPFPM_BIN --php-ini ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/fpm/php.ini \
                            --fpm-config ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf \
                            -m | less
                    ;;
                info)
                    $PHPFPM_BIN --php-ini ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/fpm/php.ini \
                            --fpm-config ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf \
                            -i
                    ;;
                config)
                    if [[ -n $EDITOR ]] ; then
                        $EDITOR ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf
                    else
                        echo "Please set EDITOR environment variable for your favor."
                        nano ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf
                    fi
                    ;;
                which)
                    echo $PHPFPM_BIN
                    ;;
                help)
                    $PHPFPM_BIN --php-ini ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/fpm/php.ini \
                            --fpm-config ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf --help
                    ;;
                test)
                    $PHPFPM_BIN --php-ini ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/fpm/php.ini \
                            --fpm-config ${PHPBREW_ROOT}/php/${PHP_BUILD}/etc/php-fpm.conf --test
                    ;;
                *)
                    echo "Usage: phpbrew fpm [start|stop|restart|module|test|config|setup|info|current|which|help]"
                    ;;
            esac
            ;;
        info)
            __phpbrew_php_exec info | php
            ;;
        off)
            unset PHPBREW_PHP
            unset PHPBREW_PATH
            eval $(__phpbrew_php_exec env)
            __phpbrew_set_path
            ;;
        switch-off)
            unset PHPBREW_PHP
            unset PHPBREW_PATH
            eval $(__phpbrew_php_exec env)
            __phpbrew_reinit
            echo "phpbrew is switched off."
            ;;
        system)
            if [[ -z "$2" ]] ; then
                __phpbrew_php_exec system
            else
                local bin="$(__phpbrew_get_version_bin $2)"

                __phpbrew_validate_interpreter "$bin" || return 1

                export PHPBREW_SYSTEM_PHP="$bin"
                __phpbrew_update_config
            fi
            ;;
        system-off)
            __phpbrew_validate_interpreter "$(__phpbrew_which php)"

            if [[ $? -ne 0 ]] ; then
                echo "The currently used PHP build $PHPBREW_PHP cannot be used as PhpBrew interpreter"
                echo "Please execute `phpbrew switch` using PHP $MIN_PHP_VERSION or newer before switching the system interpreter off"
                return 1
            fi

            unset PHPBREW_SYSTEM_PHP
            __phpbrew_update_config
            ;;
        rehash)
            echo "Rehashing..."
            . ~/.phpbrew/bashrc
            ;;
        purge)
            if [[ -z "$2" ]]
            then
                __phpbrew_php_exec help
            else
                shift
                __phpbrew_purge "$@"
            fi
            ;;
        *)
            __phpbrew_php_exec $short_option "$@"
            ;;
    esac

    local exit_status=$?

    if [[ $exit_status -ne 0 ]]; then
        return $exit_status
    fi

    hash -r
}

function __phpbrew_update_config ()
{
    if ! output="$(__phpbrew_php_exec env $(__phpbrew_normalize_build $1))"; then
        echo "$output"
        return 1
    fi

    (
        echo "# DO NOT EDIT THIS FILE"
        echo "$output"
    ) > "$PHPBREW_HOME/init"

    . "$PHPBREW_HOME/init"
}

function __phpbrew_reinit ()
{
    __phpbrew_update_config "$@" && __phpbrew_set_path
}

function __phpbrew_each()
{
    local result=0

    current="$PHPBREW_PHP"
    for build in ${PHPBREW_ROOT}/php/* ; do
        if [ -x "$build/bin/php" ]; then
            phpbrew use $(basename "$build")
            eval "$@" || result=$?
        fi
    done;

    if ! [[ -z "$current" ]]
        then
            phpbrew use $current
        else
            phpbrew off
    fi

    return $result
}

function __phpbrew_purge()
{
    local result=0

    for arg in "$@"
    do
        __phpbrew_purge_build "$arg" || result=$?
    done

    return $result
}

function __phpbrew_purge_build ()
{
    local PHP_BUILD=$(__phpbrew_normalize_build $1)

    if [[ "$PHP_BUILD" = "$PHPBREW_PHP" ]]
    then
        echo "php version: $PHP_BUILD is already in use."
        return 1
    fi

    local bin="$(__phpbrew_get_version_bin $PHP_BUILD)"

    if [[ "$bin" = "$PHPBREW_SYSTEM_PHP" ]] ; then
        echo "PHP build $PHP_BUILD is used as the system interpreter"
        return 1
    fi

    _PHP_BIN_PATH=$PHPBREW_ROOT/php/$PHP_BUILD

    if [ ! -d $_PHP_BIN_PATH ]; then
        echo "php version: $PHP_BUILD not installed."
        return 1
    fi

    _PHP_SOURCE_FILE=$PHPBREW_ROOT/build/$PHP_BUILD.tar.bz2
    _PHP_BUILD_PATH=$PHPBREW_ROOT/build/$PHP_BUILD

    rm -fr $_PHP_SOURCE_FILE $_PHP_BUILD_PATH $_PHP_BIN_PATH

    echo "php version: $PHP_BUILD is removed and purged."

    return 0
}

function phpbrew_current_php_version() {
  if type "php" > /dev/null; then
    local version=$(php -v | grep -E "PHP [578]" | sed 's/.*PHP \([^-]*\).*/\1/' | cut -c 1-6)
    if [[ -z "$PHPBREW_PHP" ]]; then
      echo "php:$version-system"
    else
      echo "php:$version-phpbrew"
    fi
  else
     echo "php:not-installed"
  fi
}

if [[ -n "$PHPBREW_SET_PROMPT" && "$PHPBREW_SET_PROMPT" == "1" ]]; then
    export PS1="\w > \u@\h [\$(phpbrew_current_php_version)]\n\\$ "
fi

# the _phpbrewrc_load will be called after *every simple command* executed in bash,
# this will make the start up process slower if you have many commands to be run in your .bashrc or .zshrc
# so this function simply compares the directory and return if nothing neccessary to do.
#
# here is the description of trap from bash manual page:
#
#   If one of the signals is DEBUG, the list of COMMANDS is executed after
#   every simple command.
#
function _phpbrewrc_load ()
{
    # check if working dir has changed
    if [[ "$PWD" == "$PHPBREW_LAST_DIR" ]]; then
        return
    fi
    local curr_dir="$PWD"
    local prev_dir=""
    local curr_fs=0
    local prev_fs=0

    while [[ -n "$curr_dir" && -d "$curr_dir" ]] ; do
        prev_fs=$curr_fs
        curr_fs=$(stat -c %d "$curr_dir" 2>/dev/null)  # GNU version
        if [ $? -ne 0 ]; then
            curr_fs=$(stat -f %d "$curr_dir" 2>/dev/null)  # BSD version
        fi

        # check if top level directory or filesystem boundary is reached
        if [[ "$curr_dir" == "/" ]] || [ -z "$PHPBREW_RC_DISCOVERY_ACROSS_FILESYSTEM" -a $prev_fs -ne 0 -a $curr_fs -ne $prev_fs ]; then
            # check if there's a previously loaded .phpbrewrc
            if [[ ! -z "$PHPBREW_LAST_RC_DIR" ]]; then
                unset PHPBREW_LAST_RC_DIR
                __phpbrew_load_user_config
            fi
            break
        fi

        # check if .phpbrewrc present
        if [[ -r "$curr_dir/.phpbrewrc" ]]; then
            # check if it's not the same .phpbrewrc which was previously loaded
            if [[ "$curr_dir" != "$PHPBREW_LAST_RC_DIR" ]]; then
                __phpbrew_load_user_config
                PHPBREW_LAST_RC_DIR="$curr_dir"
                source "$curr_dir/.phpbrewrc"
            fi
            break
        fi
        curr_dir=$(dirname "$curr_dir")
    done
    PHPBREW_LAST_DIR="$PWD"
}

if [[ -n "$PHPBREW_RC_ENABLE" ]]; then
    if [[ -n "$BASH_VERSION" ]]; then
        trap "_phpbrewrc_load" DEBUG
    fi

    # zsh has easy-to-extend cd hooks, so we migth as well use them instead
    # of running _phpbrewrc_load after EVERY command.
    if [[ -n "$ZSH_VERSION" ]]; then
        if [[ -n "$chpwd_functions" ]]; then
            if [[ ${chpwd_functions[(ie)_phpbrewrc_load]} -gt ${#chpwd_functions} ]]; then
                chpwd_functions+=_phpbrewrc_load
            fi
        else
            chpwd_functions=( _phpbrewrc_load )
        fi
    fi
fi
