
# Section: read_char

if [ -n "$KSH_VERSION" ]; then

___x_cmd_ui_region_read_char(){
    ___x_cmd_cmds stty -echo 2>/dev/null
    read -s -n 1 "$@"
    local code=$?
    ___x_cmd_cmds stty echo 2>/dev/null
    return $code
}

else

___x_cmd_ui_region_read_char(){
    read -s -n 1 "$@"
}

fi

# EndSection

# Section: getchar_read1

___X_CMD_UI_REGION_E="$___X_CMD_UNSEENCHAR_033"
___X_CMD_UI_REGION_ENTER="$___X_CMD_UNSEENCHAR_015"
___X_CMD_UI_REGION_BACKSPACE="$___X_CMD_UNSEENCHAR_010"
___X_CMD_UI_REGION_DELETE="$___X_CMD_UNSEENCHAR_177" # 7f

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

elif [ -n "$ZSH_VERSION" ]; then

___x_cmd_ui_region_getchar_read1(){
    local IFS="$___X_CMD_UNSEENCHAR_001"
    read -rsk "${1:-1}" ___X_CMD_UI_GETCHAR_CHAR
}

else

___X_CMD_UI_REGION_ENTER="$(printf "\015")"
# echo "Using stty for read1" >&2
___x_cmd_ui_region_getchar_read1(){
    ___x_cmd_cmds stty raw 2>/dev/null
    ___X_CMD_UI_GETCHAR_CHAR="$(dd bs=1 count="${1:-1}" 2>/dev/null)"
    local code=$?
    ___x_cmd_cmds stty -raw 2>/dev/null
    ___x_cmd_cmds stty -istrip 2>/dev/null
    if [ "$___X_CMD_UI_GETCHAR_CHAR" = "$___X_CMD_UNSEENCHAR_003" ]; then return 130; fi
    return $code
}

fi

# EndSection

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

# Section: main: ___x_cmd_ui_getchar

# Not worked in ksh. Using stty -echo

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

___X_CMD_UI_GETCHAR_TYPE=
___X_CMD_UI_GETCHAR_CHAR=
___x_cmd_ui_getchar(){
    ___X_CMD_UI_GETCHAR_TYPE=interrupt-read
    ___X_CMD_UI_GETCHAR_CHAR=interrupt-read

    ___x_cmd_ui_region_getchar_read1 || return 130 # return $?

    ___X_CMD_UI_GETCHAR_TYPE=special

    # TODO: only the last enter worked. check and trim
    [ "${#___X_CMD_UI_GETCHAR_CHAR}" -eq 0 ] && ___X_CMD_UI_GETCHAR_CHAR=ENTER && return
    [ "${___X_CMD_UI_GETCHAR_CHAR}" = "$___X_CMD_UI_REGION_ENTER" ] && ___X_CMD_UI_GETCHAR_CHAR=ENTER && return
    # [ "${___X_CMD_UI_GETCHAR_CHAR}" = "$(printf "\012")" ] && printf "%s\n" ENTER && return
    [ "${___X_CMD_UI_GETCHAR_CHAR}" = "
" ] && ___X_CMD_UI_GETCHAR_CHAR=ENTER && return

    if [ "$___X_CMD_UI_GETCHAR_CHAR" = "$___X_CMD_UI_REGION_E" ]; then
        ___x_cmd_ui_region_getchar_read1 || return 130 # return $? # read 2 more chars

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


    [ "$___X_CMD_UI_GETCHAR_CHAR" = "$___X_CMD_UI_REGION_BACKSPACE" ] && ___X_CMD_UI_GETCHAR_CHAR=BACKSPACE && return
    [ "$___X_CMD_UI_GETCHAR_CHAR" = "$___X_CMD_UI_REGION_DELETE" ] && {
        ___X_CMD_UI_GETCHAR_TYPE=ascii-delete
        ___X_CMD_UI_GETCHAR_CHAR=DELETE
        return
    }

    local num
    num=$(___x_cmd_ui_region_get_leading_1_len "$___X_CMD_UI_GETCHAR_CHAR")
    if [ "$num" -eq 0 ]; then
        # TODO: When input space, it will give "${#___X_CMD_UI_GETCHAR_CHAR}" - eq 0

        # [ "${#___X_CMD_UI_GETCHAR_CHAR}" -eq 0 ] && ___X_CMD_UI_GETCHAR_CHAR=ENTER && return

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

    else
        local ch1="$___X_CMD_UI_GETCHAR_CHAR"
        ___X_CMD_UI_GETCHAR_TYPE="UTF8-MULTI-$num"
        ___x_cmd_ui_region_getchar_read1 "$((num-1))" || return 130 # return $?
        ___X_CMD_UI_GETCHAR_CHAR="${ch1}${___X_CMD_UI_GETCHAR_CHAR}"
    fi
}

# shellcheck disable=SC2120
# region_getchar(){
#     if [ "$#" -eq 0 ]; then
#         _region_getchar
#     else
#         local s
#         s="$(_region_getchar)" || return
#         eval "$1=\$s"
#     fi
# }

# EndSection
