#!zsh

# This script arranges some percol-related userful zle-widgets and
# commands
#
# To use this script, put a following line into your .zshrc
#
#   source PATH_TO_THIS_FILE
#
# Then, you can bind percol's zle-widgets to some key, by calling
# bindkey.
#
#   bindkey '^R' percol_select_history
#
# Here is the list of zle-widgets provided by this script.
#
#  * percol_select_history
#  * percol_cd_sibling
#  * percol_cd_upper_dirs
#  * percol_cd_bookmark
#  * percol_cd_repository
#  * percol_insert_bookmark
#  * percol_insert_ls
#
# And this script also provides following commands.
#
#  * ppgrep
#  * ppkill
#
# Enjoy!

# ------------------------------------------------------------ #
# Initialize
# ------------------------------------------------------------ #

PERCOL_ENABLED=true

function exists() {
  which $1 &> /dev/null
}

# Define tac wrapper
exists gtac && _PERCOL_TAC="gtac" || \
    { exists tac && _PERCOL_TAC="tac" || \
    { _PERCOL_TAC="tail -r" } }
function _percol_tac() {
    eval $_PERCOL_TAC
}

# Arrange a tmp dir for percol
PERCOL_TMP_DIR=/tmp/percol.zsh.tmp
if ! [ -d ${PERCOL_TMP_DIR} ]; then
    mkdir ${PERCOL_TMP_DIR}
fi

# ------------------------------------------------------------ #
# FIFO for percol_popup
# ------------------------------------------------------------ #

# Arrange fifos
local PERCOL_IN=${PERCOL_TMP_DIR}/percol-channel-in-$$
local PERCOL_OUT=${PERCOL_TMP_DIR}/percol-channel-out-$$
function _percol_create_fifo() {
    [ -p $PERCOL_IN ] || { command rm -f $PERCOL_IN; mkfifo -m 600 $PERCOL_IN }
    [ -p $PERCOL_OUT ] || { command rm -f $PERCOL_OUT; mkfifo -m 600 $PERCOL_OUT }
}
function _percol_clean_fifos() {
    command rm -f $PERCOL_IN
    command rm -f $PERCOL_OUT
}
trap _percol_clean_fifos EXIT SIGINT SIGTERM

# ------------------------------------------------------------ #
# Arrange percol_popup on top of tmux
# ------------------------------------------------------------ #

function _percol_popup_tmux() {
    _percol_create_fifo
    tmux split-window "percol $* < ${PERCOL_IN} > ${PERCOL_OUT}"
}

function _percol_popup() {
    INPUT=$1
    PERCOL_OPTION=$2
    if [[ -n $TMUX && -n $PERCOL_USE_TMUX ]]; then
        eval "_percol_popup_tmux ${PERCOL_OPTION}; ${INPUT} > ${PERCOL_IN} &; cat ${PERCOL_OUT}"
    else
        eval "${INPUT} | percol ${(Q)PERCOL_OPTION}"
    fi
}

# ------------------------------------------------------------ #
# Common functions
# ------------------------------------------------------------ #

# TODO: I'm not sure whether this process is proper or not
function _percol_clean_prompt() {
    if [[ -n $TMUX ]]; then
        zle reset-prompt
    else
        zle -R -c
    fi
}

function _percol_list_upper_directories() {
    local dir_cursor
    dir_cursor=$(dirname ${PWD})
    local dir_prefix="../../"
    while [[ ${dir_cursor} != "/" ]]; do
        echo ${dir_prefix}$(basename ${dir_cursor})
        dir_cursor=$(dirname ${dir_cursor})
        dir_prefix="../"${dir_prefix}
    done
    echo "/"
}

function _percol_list_siblings() {
    local mydirname=$(basename $PWD)
    local parentdir=$(dirname $PWD)
    find $parentdir -maxdepth 1 -type d ! -iname ".*" -printf "%f\n" | sed -n '/${mydirname}/!p'
}

function _percol_get_destination_from_history() {
    _percol_popup 'sort ${CD_HISTORY_FILE} | uniq -c | sort -r | \
        sed -e "s/^[ ]*[0-9]*[ ]*//" | \
        sed -e s"/^${HOME//\//\\/}/~/"'
}

function _percol_go_to_repository_top() {
    cd ./$(git rev-parse --show-cdup)
    if [ $# = 1 ]; then
        cd $1
    fi
}

function _percol_get_repository_dir() {
    _percol_go_to_repository_top > /dev/null;
    destination=$(_percol_popup 'find . -path "*/.git" -prune -o -type d')
    if [[ $destination != "" ]]; then
        r > /dev/null
        cd $destination > /dev/null
        echo $PWD
    else
        echo ""
    fi
}

# ------------------------------------------------------------ #
# Define percol widgets
# ------------------------------------------------------------ #

function percol_select_history() {
    output=$(_percol_popup 'history -n 1 | _percol_tac' '--query \"${LBUFFER}\"')

    if [[ $output != "" ]]; then
        BUFFER=$output
        CURSOR=$#BUFFER
    fi

    _percol_clean_prompt
}
zle -N percol_select_history

function percol_cd_sibling() {
    destination=$(_percol_popup "_percol_list_siblings")
    if [[ $destination != "" ]]; then
        cd $destination
    fi
    _percol_clean_prompt
}
zle -N percol_cd_sibling

function percol_cd_upper_dirs() {
    destination=$(_percol_popup "_percol_list_upper_directories")
    if [[ $destination != "" ]]; then
        cd $destination
    fi
    _percol_clean_prompt
}
zle -N percol_cd_upper_dirs

function percol_insert_bookmark() {
    local destination=$(_percol_get_destination_from_history)
    if [ $? -eq 0 ]; then
        local new_left="${LBUFFER} ${destination} "
        BUFFER=${new_left}${RBUFFER}
        CURSOR=${#new_left}
    fi
    _percol_clean_prompt
}
zle -N percol_insert_bookmark

function percol_cd_bookmark() {
    local destination=$(_percol_get_destination_from_history)
    [ -n $destination ] && cd ${destination/#\~/${HOME}}
    _percol_clean_prompt
}
zle -N percol_cd_bookmark

function percol_insert_ls() {
    local basedir
    local left
    basedir=$(echo ${LBUFFER} | perl -e '<STDIN> =~ /((?:[^ ]|\\\\ )+)$/; print "$1";')
    left=$(echo ${LBUFFER} | perl -e '<STDIN> =~ /(.*)((?:[^ ]|\\\\ )+)$/; print "$1";')

    local destination
    if [ -n $basedir ]; then
        destination=$(\ls $basedir | percol)
    else
        destination=$(\ls | percol)
    fi
    if [ $? -eq 0 ]; then
        local new_left="${left}${destination} "
        BUFFER=${new_left}${RBUFFER}
        CURSOR=${#new_left}
    fi
    _percol_clean_prompt
}
zle -N percol_insert_ls

function percol_cd_repository() {
    local destination
    destination=$(_percol_get_repository_dir)
    [[ $destination != "" ]] && cd ${destination}
    _percol_clean_prompt
}
zle -N percol_cd_repository

# ------------------------------------------------------------
# Define useful commands
# ------------------------------------------------------------

function ppgrep() {
    if [[ $1 == "" ]]; then
        PERCOL=percol
    else
        PERCOL="percol --query $1"
    fi
    ps aux | eval $PERCOL | awk '{ print $2 }'
}

function ppkill() {
    if [[ $1 =~ "^-" ]]; then
        QUERY=""            # options only
    else
        QUERY=$1            # with a query
        [[ $# > 0 ]] && shift
    fi
    ppgrep $QUERY | xargs kill $*
}
