#!/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))
}

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

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

    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"
}

list_reset() {
    y=1
    y2=1
}

redraw() {
    list_print "$@"
    status_line
}

status_line() {
    printf '\0337\033[%sH\033[31;7m%-*s\033[m\0338' \
        "$LINES" "$COLUMNS" "${1:-$PWD (y:$y y2:$y2 #:$# bot:$bottom)}"
}

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
    list_reset
    redraw "$@"

    trap 'term_reset'  EXIT INT
    trap 'term_resize' 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:$y y2:$y2 #:$# bot:$bottom"
                }
            ;;

            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:$y y2:$y2 #:$# bot:$bottom"
                }
            ;;

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

                redraw "$@"
            ;;

            h?|D2) # ARROW LEFT
                cd .. || continue
                set -- *
                list_reset
                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 "$@"
