#!/usr/bin/env ksh
#
# Change directory and push the directory on the stack. This also recognizes some extensions such as
# `cd -2` to cd to the second most recently visited directory.
#
# shellcheck disable=SC2102  # .sh.var refs are legal as an array index
# shellcheck disable=SC2154  # .sh._push_stack[@] is referenced but not assigned
# shellcheck disable=SC2211  # .sh.var assignments are not "glob used as a command name"

function cd {
    typeset dir=''
    integer n=0
    integer type=4

    case $1 in
    -|-1|2)  # cd -
        n=.sh._push_top
        type=1
        ;;
    -[1-9]*([0-9]))  # cd -n
        n=.sh._push_top+${1#-}-1
        type=2
        ;;
    1)  # keep present directory
        [[ $- == *i* && -t 1 ]] && print -r - "$PWD"
        return 0
        ;;
    [1-9]*([0-9]))  # cd n
        n=.sh._push_top+${1}-2
        type=2
        ;;
    *)
        if (( .sh._push_top <= 0 ))
        then
            n=.sh._push_max
            type=3
        fi
        ;;
    esac

    if (( type == 1 ))  # cd -
    then
        dir="$OLDPWD"
    elif (( type == 2 ))  # cd -n or cd n
    then
        # shellcheck disable=SC2154
        if (( n >= .sh._push_max + 1 ))
        then
            print -u2 cd: Directory stack not that deep.
            return 1
        else
            dir="${.sh._push_stack[n]}"
        fi
    fi

    case $dir in
    \~*) dir=$HOME${dir#\~}
    esac

    (( $# == 0 )) && set -- ~/
    command cd "${dir:-$@}" || return

    dir="${OLDPWD#$HOME/}"

    # TODO: Generalize this so it works on every terminal that supports setting its title string.
    case $TERM in
    630)
        [[ $- == *i* && -t 1 ]] && print "\033[?${#PWD};2v$PWD\c"
        ;;
    esac

    case "$dir" in
    $HOME)
        dir="~"
        ;;
    /*) ;;
    *)  # shellcheck disable=SC2088
        dir="~/$dir"
        ;;
    esac

    case "$type" in
    1)  # swap first two elements
        .sh._push_stack[.sh._push_top]="$dir"
        print -r - "$PWD"
        ;;
    2|3)  # put $dir on top and shift down by one until top
        integer i=.sh._push_top
        for dir in "$dir" "${.sh._push_stack[@]}"
        do (( i > n )) && break
            .sh._push_stack[i]="$dir"
            (( i = i + 1 ))
        done
        [[ $- == *i* && -t 1 ]] && print -r - "$PWD"
        ;;
    4)  # push name
        (( .sh._push_top = .sh._push_top - 1 ))
        .sh._push_stack[.sh._push_top]="$dir"
        [[ $- == *i* && -t 1 ]] && print -r - "$PWD"
        ;;
    esac
    return 0
}
