#!/usr/bin/env bash
#
# fff - fucking fast file-manager.

refresh() {
    printf '\e[?7l\e[?25l\e[2J\e[H'
    LINES=; (:;:); ((LINES==0)) && read -r LINES _ < <(stty size)
    ((m=LINES-3,j=l>m/2?l>=c-m/2?c+1:l+m/2+1:m,k=k>=0?j-m>=0?j-m:k:k,l=l>c?k:l))
}

get_dir() {
    d=(); f=()
    for p in "$PWD"/*; do [[ -d $p ]] && d+=("$p") || f+=("$p"); done
    f=("${d[@]}" "${f[@]}"); ((${#f[@]}==0)) && f[0]=$'\e[27mempty'
}

f_print() {
    ((c=${#f[@]},j=j>c?c:j))

    for((i=${k:=0};i<j;i++)); {
        ((c<=0)) && { get_dir; f_print; return; } || path="${f[i]##*/}"
        [[ -d ${f[i]} ]] && { fo+='\e[1m\e[3'"${FFF_COL1:-2}m"; path+=/; }
        [[ ${f[i]} == "${f[l]}" ]]  && fo+='\e[7m\e[1m\e[3'"${FFF_COL4:-6}m"
        [[ ${co[i]} == "${f[i]}" ]] && fo+='\e[3'"${FFF_COL3:-1}m "
        printf '\e[K%b%s\e[m\n' "$fo" "$path"; fo=
    }
    printf '\e[3%sm\e[%s;H\e[K\n\e[K%s\e[m\e[H' "${FFF_COL2:-7}" "$((LINES-2))" \
           "${PWD/\/\///} ($l/$((c-1))) ${co[*]:+${pr[*]}: ${#co[@]}^ [p]}"
}

hist() {
    l2[((n=n<0?0:++n))]="$l:$k"
}

mark() {
   [[ "$PWD" != "$copwd" ]] && co=()
   [[ ${co[l]} == "${f[l]}" ]] && unset 'co[l]' || co[l]="${f[l]}"; copwd="$PWD"
}

open() {
    [[ -d $1/ ]] && { l=0; refresh; PWD="${1:-/}"; get_dir; f_print; }
    [[ -f $1  ]] && {
        [[ "$OSTYPE" == darwin* ]] && { oc=open; fa="bIL"; }
        case "$(file "-${fa:-biL}" "$1")" in
            text/*|*x-empty*|*json*) "${EDITOR:-vi}" "$1"; printf '\e[?25l' ;;
            *) nohup "${FFF_OPENER:-${oc:-xdg-open}}" "$1" &>/dev/null & disown
        esac
    }
}

prompt() {
    printf '\e[999B\e[?25h'
    case "${1: -1}" in
        r) read -erp "rename ${f[l]##*/}: "; [[ $REPLY ]] && \
           mv "${f[l]}" "$PWD/$REPLY" ;;
        d) read -n 1 -rp "trash ${f[l]##*/}? [y/n]: " y; [[ $y == y ]] && {
           mv "${f[l]}" "$FFF_TRASH"; ((l>0?l--:l)); } ;;
        n) read -erp "mkdir: ";   [[ $REPLY ]] && mkdir -p "$PWD/$REPLY" ;;
        f) read -erp "mkfile: ";  [[ $REPLY ]] && : > "$PWD/$REPLY" ;;
        /) g="$PWD"; IFS= read -n 1 -rsp $'\e[2K\r'"/$s2" s1
           [[ $s1 == $'\177' ]] && s2="${s2%?}" || s2+="$s1"
           [[ $s1 == $'\e' ]] && { refresh; get_dir; return; }
           f=("$PWD"/*"$s2"*); l=0; refresh; f_print; [[ $s1 ]] && prompt /
           s2=; [[ "${f[*]}" ]] && return
    esac
    refresh; get_dir
}

key() {
    case "${1: -1}" in
        C|l|"") [[ -d "${f[l]}" ]] && hist; open "${f[l]}"; g= ;;
        D|h) open "${g:-${PWD%/*}}";
             [[ -z $g ]] && ((n=n<0?0:n,l=${l2[n]/:*},k=${l2[n-1]/*:},n--))
             [[ $PWD == / ]] && l=0; g=; refresh ;;

        g) l=0; refresh ;;
        G) ((l=c-1)); refresh; ((k=k<0?0:k)) ;;
        .) a=(u s); shopt -"${a[((h=h>0?0:++h))]}" dotglob
           l=0; refresh; get_dir ;;

        y|m) mark; [[ $1 == y ]] && pr=(cp -R) || pr=(mv) ;;
        p) [[ ${co[*]} ]] && { cd "$PWD" && "${pr[@]}" "${co[@]}" .; } && {
           refresh; get_dir; co=(); copwd=; } ;;
        c) co=() ;;

        r|d|n|f|/) prompt "$1" ;;
        \~) g="$PWD"; hist; open ~ ;;
        t)  g="$PWD"; hist; open "$FFF_TRASH" ;;

        [1-9]) fav="FFF_FAV${1}"; fav="${!fav}"
               [[ $fav ]] && { g="$PWD"; hist; open "$fav"; } ;;

        s) printf '\e[?7h\e[?25h\e[2J\e[H'; cd "$PWD" && "$SHELL"; refresh ;;
        q) exit ;;

        B|j) ((l=l==c-1?l:++l,j!=c&&l==j-m/2+1))&&((k=k>=j?k:++k,j=j<c?++j:j)) ;;
        A|k) ((l=l<1?l:--l,k>0&&l==k+m/2-2))&&((k=k<=j?k>0?--k:0:j,j=j>0?--j:j))
    esac
}

main() {
    shopt -s nocaseglob nullglob checkwinsize
    mkdir -p "${FFF_TRASH:=$HOME/.cache/fff/trash/}"
    pushd "$1" &>/dev/null ||:; refresh; get_dir; f_print

    trap "refresh; printf '\\e[m\\e[?25h\\e[?7h'" EXIT
    trap 'refresh; f_print' SIGWINCH

    for ((;;)); { read -rsn 1; key "$REPLY"; f_print; }
}

main "$@"
