#!/bin/sh -e

term_setup() {
    stty -icanon -echo

    printf '\033[?1049h\033[?7l\033[?25l\033[2J\033[1;%sr' \
        "$((LINES - 2))"
}

term_reset() {
    stty icanon echo

    printf '\033[?7h\033[?25h\e[2J\033[;r\033[?1049l'
}

term_resize() {
    set -f
    set +f -- $(stty size)

    LINES=$1 COLUMNS=$2

    # space for status_line
    bottom=$((LINES - 2))
}

hist_search() {
    hist=
    j=1

    for file do
        [ "${PWD%%/}/$file" = "$old_pwd" ] &&
            y=$j y2=$((j > bottom ? bottom : j))

        j=$((j + 1))
    done
}

list_print() {
    printf '\033[2J\033[H'

    i=1
    end=$((bottom + 1))

    [ "$hist" ] &&
        hist_search "$@"

    [ "$y" -gt "$bottom" ] &&
        shift "$((y - bottom))"

    for file do
        [ "$i" = "$y2" ] &&
            printf '\033[31;7m'

        [ "$i" -lt "$end" ] &&
            printf '%s\033[m\033[B\r' "$file"

        i=$((i + 1))
    done

    printf '\033[%sH' "$((y > bottom ? bottom : y))"
}

redraw() {
    list_print "$@"
    status_line "$PWD ($y/$#)"
}

status_line() {
    printf '\0337\033[%sH\033[31;7m%-*s\033[m\0338' \
        "$LINES" "$COLUMNS" "$1"
}

print_line() {
    offset=$1

    [ "$offset" != "$y" ] ||
        printf '\033[31;7m'

    shift "$offset"
    printf '\033[K%s\033[m\r' "$1"

    [ "$offset" != "$y" ] ||
        cur=$1
}

main() {
    esc_char=$(printf '\033')

    # change directory to the first argument
    cd "${1:-"$PWD"}"

    set -- *

    term_resize
    term_setup
    y=1 y2=1
    redraw "$@"

    trap 'term_reset'  EXIT INT
    trap 'term_resize; term_setup; y=1 y2=1; redraw "$@"' WINCH

    while key=$(dd ibs=1 count=1 2>/dev/null); do
        case $key${esc:=0} in
            k?|A2) # ARROW UP
                [ "$y" -gt 1 ] && {
                    y=$((y - 1))

                    print_line "$((y + 1))" "$@"

                    if [ "$y2" -eq 1 ]; then
                        printf '\e[L'
                    else
                        printf '\e[A'
                        y2=$((y2 > 1 ? y2 - 1 : 1))
                    fi

                    print_line "$y" "$@"

                    status_line "$PWD ($y/$#)"
                }
            ;;

            j?|B2) # ARROW DOWN
                [ "$y" -lt "$#" ] && {
                    y=$((y + 1))
                    y2=$((y2 + 1 < bottom ? y2 + 1 : bottom))

                    print_line "$((y - 1))" "$@"
                    printf '\n'
                    print_line "$y" "$@"

                    status_line "$PWD ($y/$#)"
                }
            ;;

            l?|C2) # ARROW RIGHT
                if cd "$cur"; then
                    set -- *
                    y=1 y2=1
                else
                    "${SHFM_OPENER:="${EDITOR:=vi}"}" "$cur"
                fi

                redraw "$@"
            ;;

            h?|D2) # ARROW LEFT
                old_pwd=$PWD
                cd .. || continue
                set -- *
                y=1 y2=1 hist=1
                redraw "$@"
            ;;

            g?)
                list_reset
                redraw "$@"
            ;;

            # broken
            G?)
                y=$#
                y2=$(($# < bottom ? $# : bottom))
                redraw "$@"
            ;;

            q?)
                exit 0
            ;;

            # handle keys which emit escape sequences
            "$esc_char"*) esc=1 ;;
                    '[1') esc=2 ;;
                       *) esc=0 ;;
        esac
    done
}

main "$@"
