# This script applies local configuration changes from the start of your
# file hierarchy tree down to your current working directory.
#
# When you change dirctories it automatically undoes those changes by using
# the command contained in the associative array `__undo_commands` starting by
# the ones furthest down until the common ancestor with your current
#
# The '.localrc' files should add an undo command to __undo_commands which is
# keyed with the '__UNDO_DIR' variable (i.e: __undo_commands[$__UNDO_DIR]='...')
# This variable will be eval-ed to undo your local changes.
#
# Example 1: a project that needs to have the PATH pre-pended
#
#     # Taking advantage of PATH being expanded when sourcing the file
#     __undo_commands[$__UNDO_DIR]="export PATH='$PATH'"
#     export PATH="$__UNDO_DIR/bin:$PATH"
#
# Example 2: a project that needs to activate a virtualenv
#
#     __undo_commands[$__UNDO_DIR]='deactivate'
#     source env/bin/activate

# Contains the commands to undo the changes made in a directory
declare -A __undo_commands
# Contains the PWD of the previous command (and not $OLDPWD)
__LAST_PWD='/'
# Declare this variable to print some debug information
#DEBUG_LOCALRC=toto

# Source localrc files from "$1" and its parents unless we find "$2" and stop
function __source_localrc_files_rec() {
    # "$1" and "$2" should not have any trailing slashes unless they are '/'
    if [ "$1" != "$2" ]; then
        # FIXME: condition should be unnecessary, but is there to avoid loops
        [ "$1" != '/' ] && __source_localrc_files_rec "$(dirname "$1")" "$2"

        # Export the special '__UNDO_DIR' variable to key into '__undo_commands'
        export __UNDO_DIR="$1"
        local localrc="$1/.localrc"
        # The post-fix traversal sourced the parent before the child
        if [ -r "$localrc" ]; then
            source "$localrc"

            if [ -n "${DEBUG_LOCALRC+set}" ]; then
                echo "sourced $localrc with __UNDO_DIR as '$__UNDO_DIR'"
            fi
        fi
        unset -v __UNDO_DIR
    fi
}

# Undo the changes made by "$1" and its parents unless we find "$2" and stop
function __undo_localrc_changes() {
    # Undo the changes for our parent directory if we aren't at the common root
    if [ "$1" != "$2" ]; then
        # If there is a command to undo the changes for that directory, do it
        [ -n "${DEBUG_LOCALRC+set}" ] && echo "Looking at undoing $1"
        if [ -n "${__undo_commands[$1]+set}" ]; then
            eval "${__undo_commands[$1]}"

            [ -n "${DEBUG_LOCALRC+set}" ] && echo "undid $1/.localrc"
        fi

        # FIXME: condition should be unnecessary, but is there to avoid loops
        [ "$1" != '/' ] && __undo_localrc_changes "$(dirname "$1")" "$2"
    fi
}

# Return the closest common parent directory of "$1" and "$2"
function common_parent() {
    local directories=("$1")

    while [ "${directories[-1]}" != '/' ]; do
        directories+=("$(dirname "${directories[-1]}")")
    done

    for candidate in "${directories[@]}"; do
        case "$2" in
            "$candidate"*) printf "%s" "$candidate"; break;;
        esac
    done
}

function __source_localrc_files() {
    # Only do it if we changed our directory since the last command
    if [ "$__LAST_PWD" = "$PWD" ]; then
        return 0
    fi

    # Find the closest common parent directory
    local parent='/'
    if [ -n "$__LAST_PWD" ]; then
        parent=$(common_parent "$__LAST_PWD" "$PWD")
    fi

    if [ -n "${DEBUG_LOCALRC+set}" ]; then
        echo "DEBUG"
        echo "LAST:   $__LAST_PWD"
        echo "CUR:    $PWD"
        echo "PARENT: $parent"
    fi

    __undo_localrc_changes "$__LAST_PWD" "$parent"
    __source_localrc_files_rec "$PWD" "$parent"

    # Update our last seen working directory
    __LAST_PWD="$PWD"
}

# Execute this command every time
if [ -n "$BASH_VERSION" ]; then
    [ -n "${DEBUG_LOCALRC+set}" ] && echo "Loaded Bash"
    PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND; }__source_localrc_files"
elif [ -n "$ZSH_VERSION" ]; then
    [ -n "${DEBUG_LOCALRC+set}" ] && echo "Loaded Zsh"
    precmd_functions+=( __source_localrc_files)
fi