# !/bin/bash


# MIT license, explained here:
#   http://www.tldrlegal.com/l/mit

# Copyright (c) 2020 Cefan Daniel Rubin
#
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

# Latest:
#   https://github.com/cdrubin/bash_ui



# internal variables
_stdin=""
_options=""
_display_options=""
_mode="single"
_selected_line=1
_chosen=""
_line_count=0
_top=0
_row=0

# ansi handling
ESC="\0033["
SEP=";"
ELLIPSIS="…"
_sx=0; _sy=0

# shell
_oldIFS=IFS
IFS=$'\n'


# read from stdin and don't block if empty
if [ ! -t 0 ]; then
    read -d '' _stdin
fi

# default way of handling options
_options=$_stdin


# look for mode parameter
if [ $# -gt 0 ]; then
    if [[ $1 =~ multi ]]; then
        # set mode and add multiple select line to options
        _mode=multiple
        _options=$_stdin$'\n'">"    
    elif [[ $1 =~ clean ]]; then
        # leave less trace of ui in terminal
        _mode=clean
    fi
fi

# count number of lines now in _options
_line_count=$(($(printf "%s\n" "$_options" | wc -l)))
#echo $_line_count


function gotoXY() {
    x=$1; y=$2
	echo -en "${ESC}$((y))${SEP}$((x))H"
}


function setXY() {
    exec < /dev/tty
    local _oldstty=$(stty -g)
    stty raw -echo min 0
    echo -en "\033[6n" > /dev/tty
    IFS=';' read -r -d R -a pos
    stty $_oldstty
    _sx=$((${pos[1]} - 1))
    _sy=$((${pos[0]:2}))
}


function hideCursor() {
	echo -en "${ESC}?25l"
}


function showCursor() {
	echo -en "${ESC}?25h"
}


function set_row() {
	setXY
	_row=$((_sy - 1))
}


function refresh() {
    # jump to top
    hideCursor
    gotoXY 0 $((_top + 1))

    # disable wordwrap
    echo -ne "\033[?7l"
    
    _count=0
    for _line in $_options; do
        _count=$((_count+1))

        if [ $_count -eq $_selected_line ]; then
            echo -ne "\033[7m"; echo -n "$_line"; echo -e "\033[0m"
        else
            echo -ne "\033[K"; echo $_line
        fi
    done

    # enable wordwrap
    echo -ne "\033[?7h"
    showCursor
}



# print out the choices
echo -ne "\x1b[?7l"          # disable wordwrap
printf "%s\n" "$_options"
echo -ne "\x1b[?7h"          # enable wordwrap


# initialize
set_row
_top=$((_row - _line_count))
refresh


_selected_line=1

while read -sn1 key; do

    if [ "$key" == $'\e' ]; then
        read -sn1 -t 1 k1; read -sn1 -t 1 k2;
    elif [ "$key" == "" ]; then
        if [ "$_mode" == "multiple" ]; then

            # if on the last option (multiple) then break
            if [ $_selected_line -eq $_line_count ]; then
                break
            # if on an option then add/remove it
            else
                _option_chosen=$( echo "${_options}" | sed "${_selected_line}q;d" )
                
                # if _chosen contains _option_chosen then remove it
                if echo "$_chosen" | grep "^${_option_chosen}$" > /dev/null; then
                    _chosen=$( echo "${_chosen}" | grep -v "^$_option_chosen\$" )
                # otherwise add it
                else
                    _chosen="$_chosen"$'\n'"$_option_chosen"
                    # remove empty lines
                    _chosen=$( echo "${_chosen}" | sed '/^$/d' )
                fi
                
                _chosen_commas=$( echo "$_chosen" | awk -v ORS=', ' '1' | sed 's/, $//' )
                
                _options=$_stdin$'\n'"> ${_chosen_commas}"
                refresh
            fi
        else
            _chosen=$( echo "${_options}" | sed "${_selected_line}q;d" )
            break
        fi
    fi
    key+=${k1}${k2}

    case "$key" in
        $'\e[A'|$'\e0A')  # up arrow
            if [ $_selected_line -gt 1 ]; then ((_selected_line--)); refresh; fi;;

        $'\e[B'|$'\e0B')  # down arrow
            if [ $_selected_line -lt $_line_count ]; then ((_selected_line++)); refresh; fi;;
    esac
done

if [ "$_mode" == "clean" ]; then
    echo -e "\033[${_top};0H"               # jump to beginning of line _top
    for i in $(seq ${_line_count}); do
      echo -e "\033[2K"                     # clear line
    done
    echo -e "\033[${_top};0H"               # jump to beginning of line _top
    echo "$_chosen"
fi

echo "$_chosen" > /dev/stderr

IFS=_oldIFS