

___X_CMD_WCHAR_E="$___X_CMD_UNSEENCHAR_033"
___X_CMD_WCHAR_ENTER="$___X_CMD_UNSEENCHAR_015"
___X_CMD_WCHAR_BACKSPACE="$___X_CMD_UNSEENCHAR_010"     # ?
___X_CMD_WCHAR_DELETE="$___X_CMD_UNSEENCHAR_177"        # 7f

if [ -n "$BASH_VERSION" ] || [ "ash" = "$___X_CMD_SHELL" ]; then
___x_cmd_wchar___getchar(){
    local IFS="$___X_CMD_UNSEENCHAR_001"
    read -rsn "${1:-1}" ___X_CMD_WCHAR_GET_CHAR
    local code=$?
    return $code
}

elif [ -n "$ZSH_VERSION" ]; then

___x_cmd_wchar___getchar(){
    local IFS="$___X_CMD_UNSEENCHAR_001"
    read -rsk "${1:-1}" ___X_CMD_WCHAR_GET_CHAR
}

else

# echo "Using stty for read1" >&2
___x_cmd_wchar___getchar(){
    stty raw 2>/dev/null
    ___X_CMD_WCHAR_GET_CHAR="$(dd bs=1 count="${1:-1}" 2>/dev/null)"
    local code=$?
    stty -raw 2>/dev/null
    if [ "$___X_CMD_WCHAR_GET_CHAR" = "$___X_CMD_UNSEENCHAR_003" ]; then return 130; fi
    return $code
}

fi

# Section: leading_1_len
___x_cmd_wchar___get_leading_1_len(){
    ___X_CMD_WCHAR_GET_ORD="$(printf "%d" "'$1")"
    if [ "$ord" -ge 0 ]; then
        ___X_CMD_WCHAR___GET_LEADING_1_LEN=0
        return
    fi
    ord=$((ord + 256))
    local i
    for i in 0 1 2 3 4 5 6 7 8; do
        s=$(( (ord << i) & 128 ))
        if [ "$s" -eq 0 ]; then
            ___X_CMD_WCHAR___GET_LEADING_1_LEN="$i"
            return
        fi
    done
}
# EndSection

# Section: main: ___x_cmd_wchar_get

# Not worked in ksh. Using stty -echo

# This is how we handle UTF-8
# Reference: https://zh.wikipedia.org/wiki/UTF-8

___X_CMD_WCHAR_GET_TYPE=
___X_CMD_WCHAR_GET_CHAR=
___x_cmd_wchar_get(){
    ___X_CMD_WCHAR_GET_TYPE=interrupt-read
    ___X_CMD_WCHAR_GET_CHAR=interrupt-read
    ___X_CMD_WCHAR_GET_ORD=

    ___x_cmd_wchar___getchar || return 130 # return $?

    ___X_CMD_WCHAR_GET_TYPE=special

    # TODO: only the last enter worked. check and trim
    [ "${#___X_CMD_WCHAR_GET_CHAR}" -ne 0 ] || { ___X_CMD_WCHAR_GET_CHAR=ENTER; return; }
    [ "${___X_CMD_WCHAR_GET_CHAR}" != "$___X_CMD_WCHAR_ENTER" ] || { ___X_CMD_WCHAR_GET_CHAR=ENTER; return; }
    # [ "${___X_CMD_WCHAR_GET_CHAR}" = "$(printf "\012")" ] && printf "%s\n" ENTER && return
    # [ "${___X_CMD_WCHAR_GET_CHAR}" = "$(printf "\n")" ] && printf "%s\n" ENTER && return
    [ "${___X_CMD_WCHAR_GET_CHAR}" != "$___X_CMD_UNSEENCHAR_NEWLINE" ] || { ___X_CMD_WCHAR_GET_CHAR=ENTER; return; }

    if [ "$___X_CMD_WCHAR_GET_CHAR" = "$___X_CMD_WCHAR_E" ]; then
        ___x_cmd_wchar___getchar || return 130 # return $? # read 2 more chars

        case "$___X_CMD_WCHAR_GET_CHAR" in
            \[)
                ___x_cmd_wchar___getchar || return 130 # return $? # read 2 more chars
                case "$___X_CMD_WCHAR_GET_CHAR" in
                    'A')       ___X_CMD_WCHAR_GET_CHAR=UP  ;;
                    'B')       ___X_CMD_WCHAR_GET_CHAR=DN  ;;
                    'D')       ___X_CMD_WCHAR_GET_CHAR=LEFT ;;
                    'C')       ___X_CMD_WCHAR_GET_CHAR=RIGHT ;;
                    *)         ___X_CMD_WCHAR_GET_CHAR="[$___X_CMD_WCHAR_GET_CHAR" ;;
                esac
                ;;
            q)
                ___X_CMD_WCHAR_GET_CHAR=QUITTING
                return 1
                ;;
            *)
                # ___X_CMD_WCHAR_GET_CHAR="$___X_CMD_WCHAR_GET_CHAR"
        esac
        return
    fi


    [ "$___X_CMD_WCHAR_GET_CHAR" = "$___X_CMD_WCHAR_BACKSPACE" ] && ___X_CMD_WCHAR_GET_CHAR=BACKSPACE && return
    [ "$___X_CMD_WCHAR_GET_CHAR" = "$___X_CMD_WCHAR_DELETE" ] && {
        ___X_CMD_WCHAR_GET_TYPE=ascii-delete
        ___X_CMD_WCHAR_GET_CHAR=DELETE
        return
    }

    local ___X_CMD_WCHAR___GET_LEADING_1_LEN
    ___x_cmd_wchar___get_leading_1_len "$___X_CMD_WCHAR_GET_CHAR"
    if [ "$___X_CMD_WCHAR___GET_LEADING_1_LEN" -eq 0 ]; then
        # TODO: When input space, it will give "${#___X_CMD_WCHAR_GET_CHAR}" - eq 0

        # [ "${#___X_CMD_WCHAR_GET_CHAR}" -eq 0 ] && ___X_CMD_WCHAR_GET_CHAR=ENTER && return
        if   [ "$___X_CMD_WCHAR_GET_ORD" -le 27 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-control"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 31 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-seperator"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 32 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-space"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 47 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-0"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 57 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-digit"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 64 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-1"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 90 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-letter-uppercase"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 96 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-2"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 122 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-letter-lowercase"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 126 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-3"
        elif [ "$___X_CMD_WCHAR_GET_ORD" -le 127 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-delete"
        fi

    else
        local ch1="$___X_CMD_WCHAR_GET_CHAR"
        ___X_CMD_WCHAR_GET_TYPE="UTF8-MULTI-$___X_CMD_WCHAR___GET_LEADING_1_LEN"
        ___x_cmd_wchar___getchar "$((___X_CMD_WCHAR___GET_LEADING_1_LEN-1))" || return 130 # return $?
        ___X_CMD_WCHAR_GET_CHAR="${ch1}${___X_CMD_WCHAR_GET_CHAR}"
    fi
}

___x_cmd_wchar_get_type(){
    if [ "$___X_CMD_WCHAR_GET_TYPE" = "" ]; then
        local ord; ord="$(printf "%d" "'$___X_CMD_WCHAR_GET_CHAR")"
        # ___X_CMD_WCHAR_GET_CHAR
        if   [ "$ord" -le 27 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-control"
        elif [ "$ord" -le 31 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-seperator"
        elif [ "$ord" -le 32 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-space"
        elif [ "$ord" -le 47 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-0"
        elif [ "$ord" -le 57 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-digit"
        elif [ "$ord" -le 64 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-1"
        elif [ "$ord" -le 90 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-letter-uppercase"
        elif [ "$ord" -le 96 ]; then        ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-2"
        elif [ "$ord" -le 122 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-letter-lowercase"
        elif [ "$ord" -le 126 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-symbol-3"
        elif [ "$ord" -le 127 ]; then       ___X_CMD_WCHAR_GET_TYPE="ascii-delete"
        fi
    fi
}
