#!/usr/bin/env zsh
# Copyrights 2022 Aman Mehra.
# Check ../LICENSE_CODE, ../LICENSE_ART, and ../LICENSE_ADDENDUM_CFLA
# files to know the terms of license
# License files are also on github: https://github.com/poetaman/arttime

autoload -Uz is-at-least
if ! is-at-least "5.7"; then
    echo "Error: your zsh version $ZSH_VERSION is less than the required version: 5.7"
    exit 1
fi

arttime_version="1.9.8"

zmodload zsh/zselect
zmodload zsh/zutil
zmodload zsh/datetime
zmodload zsh/terminfo
setopt extendedglob

# NOTE: enable minute/nonblocking mode if/when its functional,
# or remove it completely as sample rate is a waste now that
# we account for execution delay in loop to set timer
#   m=minute_arg \
#   n=nonblocking_arg \
#   s:=samples_arg \
# This means all functions ending in _nonblocking, etc are unreachable for now 

# Note: To allow user to specify fully custom time format in future add
#     -tf-local:=tflocal_arg \
#     -tf-other:=tfother_arg \

if is-at-least "5.8"; then
    zparseoptscmd='zparseopts -D -E -F - '
else
    zparseoptscmd='zparseopts -D -E - '
fi

$(printf "$zparseoptscmd") \
   a:=artname_arg \
   b:=flipartname_arg \
   -random:=random_arg \
   t:=title_arg \
   g:=goal_arg \
   h=help_arg \
   -theme:=theme_arg \
   -tc:=titlecolor_arg \
   -ac:=artcolor_arg \
   -style:=style_arg \
   -pa:=pbpendingchar_arg \
   -pb:=pbcompletechar_arg \
   -pl:=pblength_arg \
   -hours:=hours_arg \
   -debug=debug_arg \
   -nolearn=nolearn_arg \
   -version=version_arg \
   -help=help_arg \
   || return

if [[ "${#@}" != "0" ]]; then
    printf "Error: arttime does not understand the following part of passed options,\n    "
    printf ' %s ' "[1m[4m$@[0m"
    printf '\n%s\n' "check the valid options by invoking arttime with --help"
    exit 1
fi

function printhelp {
read -r -d '' VAR <<-'EOF'
Name:
    arttime     beauty of text art meets functionality of clock/timer

Invocation:
    arttime [OPTIONS]...

Description:
    arttime brings curated text art to otherwise artless terminal emulators
    of starving developers, and others who can use a terminal.
    While doing so, it blends display of art with functionality of a nifty
    clock/timer/time-manager which is a retake on stereotypical alarms/timers.
    With one line of title message under the art, it also provides one line of
    opportunity for developers to display their activism (not-so-techie thing)
    or just attach a functional message for themselves for usual developer
    activity on terminal (like "debug the failing build tonight").

    Animation: Some great ascii artists of web 1.0 era have produced text
    art that when displayed one after another creates an illusion of motion.
    To support their vision, arttime can be passed two art files (called
    "a-art", and "b-art") that it flip-flops displaying every second.
    A current restriction is that both pieces of art must be of same height,
    or else only "a-art" is displayed.
    
    Call for artists: Much good ascii art was created during web 1.0, 
    but the artform declined after that. arttime intends to be a 
    platform/repository for hosting/displaying ascii/ansi art,
    as ascii/ansi does have its natural home on a terminal (instead
    of a wall of an arts museum). In doing so it also encourages sharing art,
    a not so common drift with the arrival of NFTs (where monkey stickers
    are being priced at hundreds of thousands of US dollars).
    The repository already provides a curated library of good ascii art
    (mixed at times with computer-generated text version of digital images).
    
    Read LICENSE_ART for license of art files, and LICENSE_CODE for
    license of code in arttime's repository.

    Another note: This application was created at the outskirts of
    Silicon Valley which has a buzzing scene of tarot-card readers (for 
    unknown reasons). This inspired the author to add a key-binding to
    emulate tarot-card experience. Pressing and holding a key ('o')
    on the keyboard for a couple seconds makes the application settle 
    on a randomly selected tarot art (emulating a tarot-card picking).
    Instead if one presses and holds 'j', the application settles
    on a randomly selected text art which is not a tarot card. In this way
    arttime can serve as a free source of "receving messages from the
    universe" or just a laugh. The author takes no responsibility for
    the meaning one derives from such activity of playing with The Random.

Layout:
    The layout of arttime is simple. Text art with one line of message
    and one line for clock/timer is kept horizontally and vertically
    centered on the terminal, even when terminal size is changed.

    Current time is always shown underlined, another time is either
    start time (when arttime was started), goal time, goals up time
    or paused time (when goals were paused). Layout of times shown
    in the application:
        - When the application starts, it displays:
          <start_time> | time elapsed <elapsed_time> | <current_time>
        - When one or more goals are set and pending, it shows:
          <current_time> | [goal_id] time pending <pending_time> | <goal_time>
        - When all goals have passed, it shows:
          <current_time> | [goal_id] goal passed! <goals_up_time> | <goal_time>
        - When goal/s are paused, it shows:
          <paused_time> | [goal_id] goal PAUSED! <elapsed_time> | <current_time>
        - When goal time is cleared, it shows:
          <start_time> | time elapsed <elapsed_time> | <current_time>

    Colors, underlines, and invert-coloring helps distinguish the
    between times. [goal_id] is displayed optionally when there are
    more than one goals and/or multiple sprints.

Notifications:
    Aside from inverting the colors of goal time to show that goal has been
    reached, arttime aims to issue desktop notification for as many platforms
    as possible. Currently it is tested to work work on macOS alone.
    But it should work for Linux Desktop Enviornments with notify-send.
    
    Note: arttime is a suspendable (Ctrl-z), and continuable application.
    The only caveat is that arttime won't issue notification if it is kept
    suspended around the time when goal is reached. Notification will be 
    deferred to the time when it is continued again. In future this behavior
    might change, but for now if you expect to get notification, don't
    suspend arttime.
    
Kebindings:
    arttime is a text user interface (TUI), and is controlled by pressing
    various keys on computer's keyboard. Passing/pressing 'h' to the
    application toggles between currently displayed art, and a "help" art 
    page that lists all of the keybindings. By default the application starts
    in help/learn mode, and displays the help art. To disable this behavior
    pass option --nolearn to arttime command, or put that in an alias for
    arttime in your shell's configuration file.

Options:
    There are options to control art displayed at launch, colors, string
    styling, setting a timer or pattern of timers, etc. 
    arttime looks for text art files in a directory relative to
    its own location (let's call it artdir):
        artdir="<dir_of_arttime>/../share/arttime/textart/*"

    -a          a-art art file name in artdir
                (default: butterfly)
    -b          b-art art file name in artdir
                (no default)
    -t          title message placed under art (default: line 1 in artfiles)
    --random [all|tarot|notarot]
                select and display a random art from arttime's collection
                Meaning of value passed:
                    all     : select from pool of all art files
                    tarot   : select from only tarot art files
                    notarot : select from all art except tarot
                When passed, user should not pass option -a or -b
    -g          goal time. arttime calls alarm/countdown time as "goal time"
                The reason it's both alarm/countdown time is either format
                can be specified. Goal can be 1) "10s" from current time, or 
                2) "May 3 1:15PM IST" (IST stands for Indian Standard Time).
                Both variants should be entered without quotes.
                Note: style 2) is currently only supported if "date" command
                on the system points to GNU date. macOS/BSD's default date
                command does not interpret arbitrary date time format
                strings. In future some standard formats that work out of
                the box with macOS/BSD default date command will be added.
    --hours     start with 12 or 24 hour format. Value passed must be
                either 24 or 12 (default: 12)

    --ac        art color*, value between [1,15]
    --tc        title color*, value between [1,15]
    --theme     when given it overrides above color options [light,dark]

    --style     process title and subtitle strings to certain "styles"
                For instance, value of 0 (--style 0) uses regular underline
                value of 1 (--style 1) uses curly underlines, among other
                things. Not all terminal emulators support curly underlines.
                Possible values: [0,1] (default: 0)

    --pa        a-character to denote pending part of progress bar
    --pa        b-character to denote complete part of progress bar
    --pl        progress bar length
                Note: progressbar a/b "character" can be a multi-character
                string, though both a/b "characters" should be same length.

    --nolearn   By default arttime displays a keybinding help/learn art
                page upon launch instead of showing some beautiful art.
                Passing this option turns off that behavior, and makes
                arttime display intended text art upon launch.
                The help/learn page can still be toggled by pressing 'h'
                at any time while application is running.
    --version   Print version number of arttime, and exit
    -h, --help  Print this help string, and exit

    *Note: artfile can have ANSI color sequences embedded in it.
    In that case, those escape sequences and their location override how
    art/title is colored. Also, ANSI escape sequences embedded in an art
    file can exceed the capability of underlying terminal emulator, in
    which case colors will not be displayed correctly. A future option can
    filter ANSI escape sequences for such terminals.

Examples:
    $ arttime
        prints help/learn art on launch, pressing 'h' shows default a-art
        (butterfly)
    $ arttime --nolearn --random all
        selects and displays a random art from entire arttime collection
    $ arttime --nolearn -t "Hello World - Butterfly"
        prints butterfly art with "Hello World - Butterfly" under
        the trex ASCII art
    $ arttime --nolearn -t "Hello World - Butterfly" --ac 2 --style 1
        prints "Hello World — Butterfly" (en dash replaced with em dash)
        under the butterfly ascii art colored in color 2 (green)
    $ arttime --nolearn -a winnepooh -b winnepooh2
        performs 2-framed animation, toggles between a-art and b-art
EOF
echo $VAR
}

if [[ ! -z $help_arg[-1] ]]; then
    printhelp
    exit 0
elif [[ ! -z $version_arg[-1] ]]; then
    echo "$arttime_version"
    exit 0
fi

unamestr="$(uname -s)"
case "${unamestr}" in
    Linux*)     machine=Linux;;
    Darwin*)    machine=Darwin;;
    CYGWIN*)    machine=Cygwin;;
    MINGW*)     machine=MinGw;;
    *BSD)       machine=BSD;;
    *)          machine="UNKNOWN:${unamestr}"
esac

# directories
bindir="${0:a:h}"
artdir="$bindir/../share/arttime/textart"

if [[ $machine = "Linux" ]]; then
    datecmd="date"
    dateisgnu="1"
else
    if command -v gdate &> /dev/null; then
        datecmd="gdate"
        dateisgnu="1"
    else
        datecmd="date"
        if date -d '0' '+%s' &>/dev/null; then
            dateisgnu="1"
        else
            dateisgnu="0"
        fi
    fi
fi

if command -v bash &>/dev/null; then
    bashavailable="1"
else
    bashavailable="0"
fi

function getaart {
    if [[ $helpactive = "1" ]]; then
        echo $restoreartname
    else
        echo $artname
    fi
}

function getbart {
    if [[ $helpactive = "1" ]]; then
        if [[ -z $restoreflipartname ]]; then
            return 1
        else
            echo $restoreflipartname
        fi
    else
        if [[ -z $flipartname ]]; then
            return 1
        else
            echo $flipartname
        fi
    fi
}

function notetimezone {
    if [[ -z $TZ ]]; then
        if [[ -L /etc/localtime ]]; then
            tzlong=$(readlink /etc/localtime | sed -n 's/^.*zoneinfo\/\(.*\)$/\1/p')
        else
            echo "W: make /etc/localtime a symlink for faster load time" >/dev/stderr
            tzlong=$(find /usr/share/zoneinfo/* -type f | while read fname; do cmp -s /etc/localtime "$fname" && sed -n 's/^.*zoneinfo\/\(.*\)$/\1/p'<<<"$fname" && break; done)
        fi
    else
        if [[ -f $TZ ]]; then
            tzlong=$(sed -n 's/^.*zoneinfo\/\(.*\)$/\1/p' <<<${TZ:a})
        else
            tzlong=$TZ
        fi
        #if [[ -z $tzlong ]]; then
        #    tzlong="$TZ"
        #fi
    fi
    tzshort=$(strftime '%Z')
}
notetimezone
tzlonginit="$tzlong"
tzshortinit="$tzshort"
tzlongcurrent="$tzlong"
tzshortcurrent="$tzshort"

# Function to push desktop notifications
function notifydesktop {
    local notifytitle="ARTTIME"
    local notifysubtitle=$(strftime "%a %b %d, %Y $hms %Z")
    if [[ $printsprints = "1" ]]; then
        if [[ $goalmaxsprint != "-" ]]; then
            local notifymessage="sprint-$goalsprint/$goalmaxsprint "
        else
            local notifymessage="sprint-$goalsprint "
        fi
    else
        local notifymessage=""
    fi
    if [[ $goalmaxptreff -gt 1 ]]; then
        local goalptrofmaxstr="-$goalptr/$goalmaxptreff"
    else
        local goalptrofmaxstr=""
    fi
    notifymessage+="goal$goalptrofmaxstr: $goalstr"
    if bart=$(getbart); then
        notifymessage+="\rarts: $(getaart), $(getbart)"
    else
        notifymessage+="\rart: $(getaart)"
    fi
    notifymessage+="\rID: $$"
    if [[ $machine = "Darwin" ]]; then
        local notifystr="display notification \"$notifymessage\" with title \"$notifytitle\" subtitle \"$notifysubtitle\" sound name \"Blow\""
        osascript -e $notifystr 2>/dev/null
    elif [[ $machine = "Linux" || $machine = "BSD" ]]; then
        [[ -z $XDG_DATA_DIRS ]] && export XDG_DATA_DIRS="$HOME/.local/share:/usr/share/xfce4:/usr/local/share:/usr/share"
        read -r freedesk_message_sound <<(print -rC1 -- ${(s[:])^XDG_DATA_DIRS}/sounds/freedesktop/stereo/message-new-instant.oga(ND))
        if command -v paplay &>/dev/null && pulseaudio --check &>/dev/null; then
            soundcommand="paplay"
        elif command -v pw-play &>/dev/null && {pactl info 2>/dev/null | grep "Server Name" 2>/dev/null | grep "PipeWire" &>/dev/null}; then
            soundcommand="pw-play" 
        else
            soundcommand="ogg123"
        fi
        [[ -n $freedesk_message_sound && -e $freedesk_message_sound ]] && $soundcommand $freedesk_message_sound &>/dev/null &
        notify-send -u critical $notifytitle "$notifysubtitle\r$notifymessage" &>/dev/null
    fi
}

# Look at TODO note on lines 10-16, minute/nonblocking mode, sample rate is disabled
#updateonminute="${minute_arg[-1]}"
#nonblocking="${nonblocking_arg[-1]}"
#samples="${samples_arg[-1]}"
updateonminute=""
nonblocking=""
# The following options are functional, and complete
artname="${artname_arg[-1]}"
title="${title_arg[-1]}"
flipartname="${flipartname_arg[-1]}"
randomarg="${random_arg[-1]}"
theme="${theme_arg[-1]}"
goal="${goal_arg[-1]}"
termwidth="$COLUMNS"
termheight="$LINES"
artcolorarg="${artcolor_arg[-1]}"
titlecolorarg="${titlecolor_arg[-1]}"
style="${style_arg[-1]}"
pbcompletechar="${pbcompletechar_arg[-1]}"
pbpendingchar="${pbpendingchar_arg[-1]}"
pblength="${pblength_arg[-1]}"
debug="${debug_arg[-1]}"
nolearn="${nolearn_arg[-1]}"
#tflocal="${tflocal_arg[-1]}"
#tfother="${tfother_arg[-1]}"
hoursfmt="$hours_arg[-1]"

hm12="%I:%M%p"
hms12="%I:%M:%S%p"
hm24="%H:%M"
hms24="%H:%M:%S"

# This function sets hm, hms, tflocal, tfother used in rest of the program
function settimeformats {
    if [[ $hoursfmt = "12" || $hoursfmt = "" ]]; then
        hoursfmt="12"
        hm="$hm12"
        hms="$hms12"
    elif [[ $hoursfmt = "24"  ]]; then
        hm="$hm24"
        hms="$hms24"
    else
        printf "E: --hours can only be passed a value of 24 or 12 (default)\n"
        exit 1
    fi
    tflocal="%s|%b %d, $hm"
    tfother="%s|%b %d, $hm %Z"
}
settimeformats

timeformat=$tflocal

if [[ ! -z $debug ]]; then
    tchar="|"
    debugopt="--debug"
else
    tchar=" "
    debugopt=""
fi


if [[ -z $artname && ! -z $flipartname ]]; then
    printf "E: if b-art is passed, a-art should also be passed\n"
    exit 1
fi

if [[ ! -z $artname || ! -z $flipartname ]] && [[ ! -z $randomarg ]]; then
    printf "E: if --random [all|tarot|notarot] is passed, a-art/b-art should not be passed\n"
    exit 1
fi

if [[ $randomarg != "" && $randomarg != "all" && $randomarg != "tarot" && $randomarg != "notarot" ]]; then
    printf "E: --random was passed \"$randomarg\", it can only be passed vales \"all\" or \"tarot\" or \"notarot\" \n"
    exit 1
fi

function checkartfile {
    local artfile=""
    if [[ ! -z "$1" ]]; then
        if [[ -f "${1/#\~/$HOME}" ]]; then
            artfile="${1/#\~/$HOME}"
        else
            artfile="$artdir/${1}"
        fi
        if [[ ! -f "$artfile" ]]; then
            return 1
        fi
    fi
    return 0
}
checkartfile $artname
if [[ "$?" != "0" ]]; then
    printf "E: artfile not found for a-art: $artname\n"
    exit 1
fi
checkartfile $flipartname
if [[ "$?" != "0" ]]; then
    printf "E: artfile not found for b-art: $flipartname\n"
    exit 1
fi

if [[ ! -z $randomarg ]]; then
    prevdir=$PWD
    cd $artdir
    RANDOM="${EPOCHREALTIME#*.}"
    if [[ $randomarg == "all" ]]; then
        defaultshuffle=(*(.Nnoe['REPLY=$RANDOM']))
    elif [[ $randomarg == "tarot" ]]; then
        defaultshuffle=(tarot-*(.Nnoe['REPLY=$RANDOM']))
    else
        defaultshuffle=(^tarot-*(.Nnoe['REPLY=$RANDOM']))
    fi
    cd $prevdir
    defaultart="$defaultshuffle[1]"
else
    defaultart="butterfly"
fi

if [[ ! -z "$nolearn" ]]; then
    helpactive="0"
    if [[ -z "$artname" ]]; then
        artname="$defaultart"
    fi
    restoreartname="help"
    if [[ ! -z "$flipartname" ]]; then
        restoreflipartname="$flipartname"
    fi
else
    helpactive="1"
    if [[ -z "$artname" ]]; then
        restoreartname="$defaultart"
    else
        restoreartname="$artname"
    fi
    if [[ ! -z "$flipartname" ]]; then
        restoreflipartname="$flipartname"
    fi
    artname="help"
    flipartname=""
fi


if [[ $artcolorarg = "" ]]; then
    artcolorarg="1"
elif ! [[ $artcolorarg =~ ^([0-9]|1[0-5])$ ]]; then
    echo "W: artcolor (--ac) accepts takes range [0:15], defaulting to 1" >&2
    artcolorarg="1"
fi

if [[ $titlecolorarg = "" ]]; then
    titlecolorarg="2"
elif ! [[ $titlecolorarg =~ ^([0-9]|1[0-5])$ ]]; then
    echo "W: titlecolor (--tc) accepts takes range [0:15] defaulting to 2" >&2
    titlecolorarg="2"
fi

tputset_str=$(echoti smcup; echoti civis; echoti rmam)
function tputset { printf "$tputset_str"; }
tputreset_str=$(echoti smam; echoti cnorm; echoti rmcup)
function tputreset { printf "$tputreset_str"; }

regexpart='[[:space:]]*([0-9]+)([dhms])[[:space:]]*'
regex1='^'$regexpart'$'
regex2='^'$regexpart$regexpart'$'
regex3='^'$regexpart$regexpart$regexpart'$'
regex4='^'$regexpart$regexpart$regexpart$regexpart'$'

function getseconds {
    amount="$1"
    suffix="$2"
    if [[ $suffix = "d" ]]; then
        echo $(( amount*86400 ))
    elif [[ $suffix = "h" ]]; then
        echo $(( amount*3600 ))
    elif [[ $suffix = "m" ]]; then
        echo $(( amount*60 ))
    elif [[ $suffix = "s" ]]; then
        echo $amount
    fi
}

function setgoaltime {
    local currenttime="$2"
    local goal="$1"
    local goaltime=""
    local goaltimestr=""
    local goalstatus="0"
    if [[ $goal =~ $regex1 ]]; then
        goaltime=$(getseconds $match[1] $match[2])
        goaltime=$(( currenttime+goaltime ))
    elif [[ $goal =~ $regex2 ]]; then
        local goaltime1=$(getseconds $match[1] $match[2])
        local goaltime2=$(getseconds $match[3] $match[4])
        goaltime=$(( currenttime+goaltime1+goaltime2 ))
    elif [[ $goal =~ $regex3 ]]; then
        local goaltime1=$(getseconds $match[1] $match[2])
        local goaltime2=$(getseconds $match[3] $match[4])
        local goaltime3=$(getseconds $match[5] $match[6])
        goaltime=$(( currenttime+goaltime1+goaltime2+goaltime3 ))
    elif [[ $goal =~ $regex4 ]]; then
        local goaltime1=$(getseconds $match[1] $match[2])
        local goaltime2=$(getseconds $match[3] $match[4])
        local goaltime3=$(getseconds $match[5] $match[6])
        local goaltime4=$(getseconds $match[7] $match[8])
        goaltime=$(( currenttime+goaltime1+goaltime2+goaltime3+goaltime4 ))
    elif goaltime=$($datecmd -d $goal '+%s' 2>/dev/null ); then
        if [[ $goaltime -gt $currenttime ]]; then
        elif goaltime=$($datecmd -d "$goal +1days" '+%s' 2>/dev/null); then
            if [[ $goaltime -gt $currenttime ]]; then
                goalstatus="1"
                #echoti cup 0 0
                #echoti el
                #printf "Today's $goal has passed, setting goal time to tomorrow's $goal"
                #sleep 2
            else
                goaltime='-'
                goalstatus="2"
            fi
        fi
    else
        goaltime='-'
        goalstatus="2"
    fi
    #echo "$ goal:$goal, goaltime:$goaltime"
    if [[ $goaltime != '-' ]]; then
        local tmpgoaltime=$(strftime $timeformat $goaltime)
        local tmpgoaltimearray=(${(s/|/)tmpgoaltime})
        goaltimestr=$tmpgoaltimearray[2]
        #if goaltimestr=$($datecmd -d "@$goaltime" "+%b %d, %I:%M%p" 2>/dev/null); then
        #elif goaltimestr=$($datecmd -jf "%s" $goaltime "+%b %d, %I:%M%p" 2>/dev/null); then
        #else
        #    goaltimestr=""
        #fi
    else
        goaltimestr=""
    fi
    returngoaltime="$goaltime"
    returngoaltimestr="$goaltimestr"
    returngoalstatus="$goalstatus"
    #return $returnval
    #echo "$ goal:$goal, goaltime:$goaltime, goaltimestr:$goaltimestr"
}

#goaltimes=()
#goaltimesstrs=()
#goalvalids=()
goalarray=()
goalarraysorted=()
goalentry=""
goalsprint="1"

function setnextgoal {
    if [[ $goalptr != $goalmaxptr ]] then;
        local lastgoaltime="$goaltime"
        local lastgoaltimestr="$goaltimestr"
        goalptr=$((goalptr+1))
        goalentry="$goalarraysorted[$goalptr]"
        #$returngoaltime;$numgoals;$returngoalstatus;$returngoaltimestr;$goal
        local goalentryarray=(${(s/;/)goalentry})
        goaltime="$goalentryarray[1]"
        goalnum="$goalentryarray[2]"
        local localgoalnum="$goalnum"
        goalstatus="$goalentryarray[3]"
        goaltimestr="$goalentryarray[4]"
        local localgoaltimestr="$goaltimestr"
        goalstr="$goalentryarray[5]"
        if [[ $goalstr = "loop" ]]; then
            if [[ $localgoaltimestr = "-" ]]; then
                local lastgoalptr="$goalptr"
                goalsprint=$((goalsprint+1))
                accumulatedsprinttime="0"
                sprintstarttime=$(strftime '%s')
                setgoals $sprintstarttime
                goaldone="0"
                #goalarraysorted[-1]=("loop;$localgoalnum;0;-;loop")
            elif [[ $localgoaltimestr -gt 1 ]]; then
                local lastgoalptr="$goalptr"
                goalsprint=$((goalsprint+1))
                accumulatedsprinttime="0"
                sprintstarttime=$(strftime '%s')
                setgoals $sprintstarttime
                goaldone="0"
                goalarraysorted[-1]=("loop;$localgoalnum;0;$((localgoaltimestr-1));loop")
            else
                goaltime="$lastgoaltime"
                goaltimestr="$lastgoaltimestr"
                goalptr="$((goalmaxptr-1))"
                goaldone="1"
            fi
        else
            goaldone="0"
        fi
    fi
}

function setgoals {
    goalmaxptr="0"
    goalptr="0"
    local tmpgoals=(${(s/;/)goal})
    local goal=""
    local numgoals="0"
    local errdetected="0"
    local tmpgoalarray=()
    for goal in "$tmpgoals[@]"; do
        numgoals=$((numgoals+1))
        if [[ $goal =~ ^[[:space:]]*(loop|sprint|x)(.*)$ ]]; then
            goal="loop"
            local match2="$match[2]"
            if [[ $match2 =~ ^([0-9]+)[[:space:]]*$ ]]; then
                returngoaltimestr="$match[1]"
            elif [[ $match2 =~ ^[[:space:]]*$ ]]; then
                returngoaltimestr="-"
            else
                returngoaltimestr=" "
                errdetected="1"
            fi
            if [[ $numgoals = "1" ]]; then
                errdetected="1"
            fi
            returngoaltime="loop"
            tmpgoalarray+=("$returngoaltime;$numgoals;0;$returngoaltimestr;$goal")
            break
        else
            #[[ $goal =~ ^[[:space:]]*(.*)$ ]] && goal="$match[1]"
            goal=${${goal%${goal##*[^[:blank:]]}}#${${goal%${goal##*[^[:blank:]]}}%%[^[:blank:]]*}};
            setgoaltime $goal $1
            if [[ $returngoalstatus = "2" ]]; then
                errdetected="1"
            fi
            tmpgoalarray+=("$returngoaltime;$numgoals;$returngoalstatus;$returngoaltimestr;$goal")
        fi
    done
    if [[ $numgoals -gt 0 ]]; then
        if [[ $errdetected = "0" ]]; then
            goalarray=("${tmpgoalarray[@]}")
            goalarraysorted=("${(o)goalarray[@]}")
            errgoalarray=()
            goalptr="0"
            goalmaxptr="$numgoals"
            setnextgoal
            if [[ $returngoaltime = "loop" && ( $returngoaltimestr = "-" || $returngoaltimestr =~ ^[0-9]+$ ) ]]; then
                printsprints="1"
                goalmaxptreff="$((goalmaxptr-1))"
                goalmaxsprint="$returngoaltimestr"
            else
                printsprints="0"
                goalmaxsprint="1"
                goalmaxptreff="$goalmaxptr"
            fi
            goalentry=$goalarraysorted[$goalmaxptreff]
            goalentryarray=(${(s/;/)goalentry})
            sprintendtime=$goalentryarray[1]
        else
            errgoalarray=("${tmpgoalarray[@]}")
            goalptr="0"
            goalmaxptr="0"
            goalentry=""
            goaltime=""
        fi
    fi
    return $errdetected
}

if [[ ! -z $goal ]]; then
    accumulatedsprinttime="0"
    sprintstarttime=$(strftime '%s')
    setgoals $sprintstarttime
    if [[ "$?" != "0" ]]; then
        echo "E: Goal time must be a valid time format representing future time.\n   Start application, press 'g', enter 'help' to learn more."
        exit 0
    else
    fi
else
    goaltime=""
fi

function exitpause {
    local goalentry
    local goalentryarray
    local goaltimeloc
    local goalnum
    local goalstatus
    local goalstr
    local tmpgoaltime
    local tmpgoaltimearray
    local goaltimestrloc
    local i
    for (( i=$goalptr; i<=$goalmaxptreff; i++ )) do
        goalentry=$goalarraysorted[$i]
        goalentryarray=(${(s/;/)goalentry})
        goaltimeloc=$((goalentryarray[1]+pausedelta))
        goalnum="$goalentryarray[2]"
        goalstatus="$goalentryarray[3]"
        goalstr="$goalentryarray[5]"
        tmpgoaltime=$(strftime $timeformat $goaltimeloc)
        tmpgoaltimearray=(${(s/|/)tmpgoaltime})
        goaltimestrloc=$tmpgoaltimearray[2]
        goalarraysorted[$i]=("$goaltimeloc;$goalnum;$goalstatus;$goaltimestrloc;$goalstr")
        if [[ $i = $goalptr ]]; then
            goaltime="$goaltimeloc"
            goaltimestr="$goaltimestrloc"
        fi
    done
}


if [[ ! -z $updateonminute ]]; then
    sleeptime="60"
    nonblocking="1"
else
    sleeptime="1"
    if [[ ! -z $nonblocking ]]; then
        nonblocking="1"
    else
        nonblocking="0"
    fi
fi

#sleeptimecenti=""
#sleeptimefloat=""
#function setsleeptimecenti {
#    if [[ -z $samples || ! $samples =~ ^[1-9]\|[1-9][0-9]+$ ]]; then
#        samples="1"
#    fi
#    sleeptimecenti=$((sleeptime*100/samples))
#    sleeptimefloat=$((sleeptime*1.0/samples))
#    if [[ $sleeptimecenti = "0" || ! $sleeptimecenti -gt 10 ]]; then
#        sleeptimecenti="10"
#        sleeptimefloat="0.1"
#    fi
#}

#setsleeptimecenti

sttycmd='/bin/stty'
sttyorig=$($sttycmd -g)
# NOTE: disable showing characters typed by user while showing dino
$sttycmd -echo
$sttycmd -icanon min 0 time 0 

function setcolors {
    if [[ -z "$theme" || "$theme" = "dark" ]]; then
        artcolor="$artcolorarg"
        titlecolor="$titlecolorarg"
        subtitlecolor="6"
        starttimecolor="5"
        deltatimecolor="3"
        currenttimecolor="5"
        theme="dark"
    else
        artcolor=""
        titlecolor="8"
        subtitlecolor="8"
        starttimecolor="4"
        deltatimecolor="1"
        currenttimecolor="4"
    fi
}
setcolors

prevartname=""
artstringht=""
flipartstringht=""
belowstr=""
artht=""
flipartht=""
function setartstring {
    local maxartstringht=$((termheight-2))
    ((maxartstringht<0)) && maxartstringht="0"
    artstring=$($bindir/artprint -a "$artname" -t "$title" --ac "$artcolor" --tc "$titlecolor" --theme "$theme" $debugopt --width "$termwidth" --height "$termheight" --style "$style"| tail -n $maxartstringht)
    if [[ ! -z $flipartname ]]; then
        flipartstring=$($bindir/artprint -a "$flipartname" -t "$title" --ac "$artcolor" --tc "$titlecolor" --theme "$theme" $debugopt --width "$termwidth" --height "$termheight" --style "$style" | tail -n $maxartstringht)
    fi
    if [[ $artname != $prevartname ]]; then
        #echo "here, $artname, $prevartname"
        if [[ -f "${artname/#\~/$HOME}" ]]; then
            artfile="${artname/#\~/$HOME}"
        else
            artfile="$artdir/${artname}"
        fi
        if [[ ! -f "$artfile" ]]; then
            artfile="/dev/null"
        fi
        artht=$(tail -n +2 $artfile | wc -l)
        artstringht=$(wc -l <<<$artstring)
        belowcurpos=$(($artstringht+2))
        lastlinepos=$(($termheight-1))
        belowstr=""
        if [[ $belowcurpos = $lastlinepos ]]; then
            #belowstr=$(printf "%$(($termwidth-1))s$tchar" " ")
            printf -v tempstr "\n%$(($termwidth-1))s$tchar" " "
            belowstr+=$tempstr
        elif [[ $lastlinepos -gt $belowcurpos ]]; then
            tempstr=""
            for i in {$belowcurpos..$lastlinepos}; do
                printf -v tempstr "\n%$(($termwidth-1))s$tchar" " "
                belowstr+=$tempstr
            done
        else
            belowstr=""
        fi
        if [[ ! -z "$flipartname" ]]; then
            if [[ -f "${flipartname/#\~/$HOME}" ]]; then
                flipartfile="${flipartname/#\~/$HOME}"
            else
                flipartfile="$artdir/${flipartname}"
            fi
            if [[ ! -f "$flipartfile" ]]; then
                flipartfile="/dev/null"
            fi
            flipartht=$(tail -n +2 $flipartfile | wc -l)
            flipartstringht=$(wc -l <<<$flipartstring)
            if [[ "$flipartht" != "$artht" ]]; then
                flipartname=""
                prevartname="$artname"
                return 1
            fi
        fi
        prevartname="$artname"
    fi
    return 0
    #echo "$flipartname, $flipartstringht, $artname, $artstringht, $belowstr"
    #exit
}

# NOTE: tput sequences of at least printart and printtime
# are better cached, rest is a low priority
tput_cup00=$(echoti cup 0 0)
tput_sc=$(echoti sc)
tput_rc=$(echoti rc)
tput_civis=$(echoti civis)
tput_cnorm=$(echoti cnorm)
tput_smcup=$(echoti smcup)
tput_cuu1=$(echoti cuu1)
tput_cub1=$(echoti cub1)

toggleflipart="0"
function printart {
    #usr1detected=""
    #trap 'trapusr1detected' USR1
    if [[ ! -z $flipartname && $toggleflipart = "1" ]]; then
        toggleflipart="0"
        if [[ -z $1 ]]; then
            printf "${tput_cup00}\e[38;5;${artcolor}m%s\n" "${flipartstring}"
        else
            # NOTE: For now unreachable, might be needed in future
            printf "${tput_cup00}%s%$((termwidth-${#1}-1))s$tchar\n\e[38;5;${artcolor}m%s\n" "$1" " " "${flipartstring#*$'\n'}"
        fi
    else
        toggleflipart="1"
        if [[ -z $1 ]]; then
            printf "${tput_cup00}\e[38;5;${artcolor}m%s\n" "${artstring}"
        else
            printf "${tput_cup00}%s%$((termwidth-${#1}-1))s$tchar\n\e[38;5;${artcolor}m%s\n" "$1" " " "${artstring#*$'\n'}"
        fi
    fi
    #if [[ "$usr1detected" = "1" ]]; then
    #    trapusr1_both
    #fi
    #trap 'trapusr1_both' USR1
}
if ! setartstring && [[ $helpactive != "1" ]]; then
    printf "W: height of b art does not match that of a art. Try setting b art again."
fi

function setstyle {
    if [[ $style = "0" || $style = "" ]]; then
        currenttimestyle="\e[4m" # regular underline
        passedgoalstyle="\e[7m" # reversed
        style="0"
        [[ -z $pbpendingchar ]] && pbpendingchar='-'
        [[ -z $pbcompletechar ]] && pbcompletechar='>'
        [[ -z $pblength ]] && pblength="10"
    else
        currenttimestyle="\e[4:3m" # curly underline
        passedgoalstyle="\e[9m" # strike-through
        [[ -z $pbpendingchar ]] && pbpendingchar='□'
        [[ -z $pbcompletechar ]] && pbcompletechar='■'
        [[ -z $pblength ]] && pblength="10"
    fi
}
setstyle

function printprogressbar {
    #local probar='**********'
    #local probar='■■■■□□□□□□'
    #local probar='..........'
    #local probar='>>>>>.....'
    if (($1>100)); then
        local percentdone="100.0"
    elif (($1<0)); then
        local percentdone="0.0"
    else
        local percentdone="$1"
    fi
    if [[ $2 =~ ^[0-9]+$ && $2 -gt "1" ]]; then
        local barlen="${2%.*}"
    else
        local barlen="1"
    fi
    local numdone=$(((percentdone/100.0)*barlen))
    if ((numdone<1.0)); then
        numdone="0"
    else
        numdone="${numdone%.*}"
    fi
    local numnotdone=$((barlen-numdone))
    local probar=''
    if [[ $numdone -gt 0 ]]; then
        probar+=$(printf "%.s$pbcompletechar" {1..$numdone})
    fi
    if [[ $numnotdone -gt 0 ]]; then
        probar+=$(printf "%.s$pbpendingchar" {1..$numnotdone})
    fi
    printf "$probar\n"
}
cacheprogressbar100=$(printprogressbar 100.0 $pblength)

ignorewinch="0"
resetstarttime="0"
lasttime=""
function printtime {
    # NOTE: strftime is a builtin replacement for date command
    currenttime=$(strftime $timeformat)
    if [[ "$currenttime" = "$lasttime" ]]; then
        return
    else
        #usr1detected=""
        #trap 'trapusr1detected' USR1
        if [[ ! -z $flipartname && ! -z $lasttime ]]; then
            printart
        fi
    fi
    lasttime="$currenttime"
    currenttimearray=(${(s/|/)currenttime})
    if [[ "$resetstarttime" == "1" ]]; then
        starttime="$currenttime"
        starttimearray=(${(s/|/)currenttime})
    fi
    if [[ ! -z $goaltime ]]; then
        difftime=$((goaltime-currenttimearray[1]))
        timestring0="$currenttimearray[2]"
        timestring0style="$currenttimestyle"
        timestring4="$goaltimestr"
        if [[ ! $difftime -gt 0 ]]; then
            difftime=$(( -difftime ))
            if [[ $goaldone = "0" ]]; then
                local lastgoaltime="$goaltime"
                while ((goaltime == lastgoaltime && goaldone == 0)); do
                    notifydesktop &
                    goaldone="1"
                    setnextgoal
                done
                difftime=$((goaltime-currenttimearray[1]))
                timestring4="$goaltimestr"
            fi
            if [[ $goalmaxptreff -gt 1 ]]; then
                local goalptrofmaxstr="-$goalptr/$goalmaxptreff"
            else
                local goalptrofmaxstr=""
            fi
            if [[ $printsprints = "1" ]]; then
                if [[ $goalmaxsprint != "-" ]]; then
                    timestring1=" | sprint-${goalsprint}/$goalmaxsprint "
                else
                    timestring1=" | sprint-${goalsprint} "
                fi
            else
                timestring1=" | "
            fi
            if [[ $goaldone = "0" ]]; then
                timestring1+="goal$goalptrofmaxstr pending "
                timestring4style=""
            else
                timestring1+="goal$goalptrofmaxstr passed! "
                timestring4style="$passedgoalstyle"
            fi
        else
            if [[ $goalmaxptreff -gt 1 ]]; then
                local goalptrofmaxstr="-$goalptr/$goalmaxptreff"
            else
                local goalptrofmaxstr=""
            fi
            if [[ $printsprints = "1" ]]; then
                if [[ $goalmaxsprint != "-" ]]; then
                    timestring1=" | sprint-${goalsprint}/$goalmaxsprint "
                else
                    timestring1=" | sprint-${goalsprint} "
                fi
            else
                timestring1=" | "
            fi
            timestring1+="goal$goalptrofmaxstr pending "
            timestring4style=""
        fi
    elif [[ $pausegoals = "1" ]]; then
        difftime=$((currenttimearray[1]-pausestart))
        timestring0="$pausestartstr"
        timestring4="$currenttimearray[2]"
        timestring4style="$currenttimestyle"
        timestring0style=""
        if [[ $goalmaxptreff -gt 1 ]]; then
            local goalptrofmaxstr="-$goalptr/$goalmaxptreff"
        else
            local goalptrofmaxstr=""
        fi
        if [[ $printsprints = "1" ]]; then
            if [[ $goalmaxsprint != "-" ]]; then
                timestring1=" | sprint-${goalsprint}/$goalmaxsprint "
            else
                timestring1=" | sprint-${goalsprint} "
            fi
        else
            timestring1=" | "
        fi
        timestring1+="goal$goalptrofmaxstr PAUSED! "
    else
        difftime=$((currenttimearray[1]-starttimearray[1]))
        timestring0="$starttimearray[2]"
        timestring4="$currenttimearray[2]"
        timestring4style="$currenttimestyle"
        timestring0style=""
        timestring1=" | time elapsed "
    fi
    days=$((difftime/86400))
    hours=$((difftime%86400/3600))
    minutes=$((difftime%3600/60))
    seconds=$((difftime%60))
    if [[ $days != "0" ]]; then
        #timestring2="${(l:3::0:)days}d "
        timestring2="${days}d "
    else
        timestring2=""
    fi
    timestring2="$timestring2${(l:2::0:)hours}h "
    timestring2="$timestring2${(l:2::0:)minutes}m "
    if [[ -z $updateonminute ]]; then
        timestring2="$timestring2${(l:2::0:)seconds}s"
    fi
    timestring3=" | "
    #if [[ ! -z $TMUX ]]; then
    #    #ignorewinch=$(tmux show-options -vp -t $TMUX_PANE @arttimeterm-ignorewinch 2>/dev/null)
    #    if [ "$ignorewinch" != "1" ]; then
    #        termwidth="$COLUMNS"
    #    fi
    #else
    #    termwidth="$COLUMNS"
    #fi
    shiftwidth=$((($termwidth-${#timestring0}-${#timestring1}-${#timestring2}-${#timestring3}-${#timestring4})/2))
    # NOTE: following checks are needed so following math expressions do not fail during runtime
    # when the term-width is changed to be too narrow for instance...
    if [[ ! $shiftwidth -gt 0 ]]; then
        shiftwidth=0
    fi
    padwidth=$(($termwidth-${#timestring0}-${#timestring1}-${#timestring2}-${#timestring3}-${#timestring4}-${shiftwidth}-1))
    if [[ ! $padwidth -gt 0 ]]; then
        padwidth=0
    fi
    printf "\r%${shiftwidth}s\e[0m\e[38;5;${starttimecolor}m${timestring0style}$timestring0\e[0m\e[38;5;${subtitlecolor}m$timestring1\e[0m\e[38;5;${deltatimecolor}m$timestring2\e[0m\e[38;5;${subtitlecolor}m$timestring3\e[0m\e[38;5;${currenttimecolor}m${timestring4style}$timestring4\e[0m%${padwidth}s$tchar" " " " "
    if [[  $goaltime != "" && $goaldone = "1" && $goalmaxsprint != "-" ]]; then
        percentdone="100.0"
        progressbar="$cacheprogressbar100"
        printprogressbarflag="1"
    elif [[ $goaltime != "" && $goalmaxsprint != "-" ]]; then
        percentdone=$((((goalsprint-1)*100.0/goalmaxsprint) + (1.0/goalmaxsprint)*(accumulatedsprinttime+currenttimearray[1]-sprintstarttime)*100.0/(accumulatedsprinttime+sprintendtime-sprintstarttime)))
        progressbar=$(printprogressbar $percentdone $pblength)
        printprogressbarflag="1"
    elif [[ $pausegoals = "1" && $goalmaxsprint != "-" ]]; then
        printprogressbarflag="1"
    elif [[ $pausegoals = "1" || $goaltime != "" ]] && [[ $goalmaxsprint = "-" ]]; then
        printprogressbarflag="1"
        progressbar=">--------|"
    else
        printprogressbarflag="0"
    fi
    if [[ $belowstr = "" || $1 != "1"  ]]; then
        if [[ $printprogressbarflag = "1" ]]; then
            #printprogressbar
            shiftwidth=$(((termwidth-${#progressbar})/2))
            [[ ! $shiftwidth -gt 0 ]] && shiftwidth=0
            padwidth=$((termwidth-${#progressbar}-shiftwidth-1))
            [[ ! $padwidth -gt 0 ]] && padwidth=0
            printf "\n%${shiftwidth}s\e[38;5;${artcolor}m%s\e[0m%${padwidth}s$tchar$tput_cuu1" " " "$progressbar" " "
        else
            #printemptyline
            shiftwidth=$((termwidth-1))
            printf "\n%${shiftwidth}s$tchar$tput_cuu1" " "
        fi
    else
        #printprogressbar
        #printbelowstr
        if [[ $printprogressbarflag = "1" ]]; then
            #printprogressbar
            shiftwidth=$(((termwidth-${#progressbar})/2))
            [[ ! $shiftwidth -gt 0 ]] && shiftwidth=0
            padwidth=$((termwidth-${#progressbar}-shiftwidth-1))
            [[ ! $padwidth -gt 0 ]] && padwidth=0
            printf "$tput_sc\n%${shiftwidth}s\e[38;5;${artcolor}m%s\e[0m%${padwidth}s$tchar$belowstr$tput_rc" " " "$progressbar" " "
        else
            #printemptyline
            shiftwidth=$((termwidth-1))
            printf "$tput_sc\n%${shiftwidth}s$tchar$belowstr$tput_rc" " "
        fi
    fi
    #if [[ "$usr1detected" = "1" ]]; then
    #    trapusr1_both
    #fi
    #trap 'trapusr1_both' USR1
}

killdone="0"

function trapint_nonblocking {
    trap -; kill ${1}; command -v pkill &>/dev/null && pkill -P $$; killdone="1"; tputreset; $sttycmd $sttyorig; exit 0;
}

function trapint_blocking {
    #while read dummy ; do : ; done
    # exec 0>&- 2>dev/null;
    #trap -; command -v pkill &>/dev/null && pkill -P $$; tputreset; $sttycmd $sttyorig; set -e; (exit 130);
    trapintactive="1"
    trapusr1_both "q"
    trapintactive="0"
}

function trapexit_nonblocking {
    trap -;
    command -v pkill &>/dev/null && pkill -P $$;
    if [[ "$killdone" = "0" ]]; then
        kill ${1}; killdone="1"; tputreset; $sttycmd $sttyorig; exit 0;
    fi
}

function trapexit_blocking {
    rm ~/.cache/arttime/$UID-$$.morestr 2>/dev/null
    trap -; command -v pkill &>/dev/null && pkill -P $$; tputreset; $sttycmd $sttyorig;  set -e; (exit 1);
}

function trapstop_blocking {
    tputreset; $sttycmd $sttyorig; trap - TSTP; suspend; # kill -TSTP $$
}

function trapstop_nonblocking {
    tputreset; $sttycmd $sttyorig; trap - TSTP; suspend; # kill -TSTP $$
}

function trapcont_blocking {
    trap "trapstop_blocking" TSTP;
    #killdone="0"; 
    sttyorig=$($sttycmd -g)
    $sttycmd -echo
    $sttycmd -icanon min 0 time 0 
    tputset
    printart
    printtime
}

function trapcont_nonblocking {
    trap "trapstop_nonblocking" TSTP;
    #killdone="0"; 
    sttyorig=$($sttycmd -g)
    $sttycmd -echo
    $sttycmd -icanon min 0 time 0 
    tputset
    printart
    printtime
}


function askforconfirmation {
    local response=""
    printf "Do you really want to $1? [y/n]:  ${tput_cub1}${tput_cnorm}"
    read -k1 response
    if [[ $response = "y" || $response = "Y" ]]; then
        return 0
    else
        return 1
    fi
}

#setopt nomenucomplete
#setopt menu_complete
#zmodload -i zsh/complist
tmpartname=""
function artselector {
    local curcontext=artselector:::
    tmpartname="";
    printf "Select a-art. Press 'Enter' without typing anything to cancel.\nPress a letter, followed by tab key to see possible artnames.\nPress Ctrl-d to see all possible artnames.\nAnswer statements ending in '?' with pressing 'y' or 'n'.\n";
    vared -p "Enter artname:${tput_cnorm} " -c tmpartname;
}
function _artselector {
    _files -W $artdir
}
zstyle ':completion:artselector:*:' completer _artselector
#zstyle ':completion:::*:default' completer _artselector menu no select

function artselector2 {
    local restoredir=$PWD
    cd $artdir
    local tmpaart=$(getaart)
    local aartheight=$(wc -l "${tmpaart}" | sed -n 's/^[[:space:]]*\([0-9]*\)[[:space:]][[:space:]]*.*$/\1/p')
    artnametemp=($(wc -l * | sed -n '$d;s/^[[:space:]]*'"${aartheight}"'[[:space:]][[:space:]]*\(.*\)$/\1/p'))
    cd $restoredir
    local curcontext=artselector2:::
    tmpartname="";
    if command -v column &>/dev/null; then
        echo $artnametemp  | tr ' ' '\n' | column -c$(echoti cols)
        printf '\n'
    else
        echoti smam
        printf '%s  ' $artnametemp
        printf '\n\n'
    fi
    vared -p "Enter artname:${tput_cnorm} " -c tmpartname;
}
function _artselector2 {
    _describe 'possible b-art' artnametemp
}
zstyle ':completion:artselector2:*:' completer _artselector2

function zoneselector {
    local restorepwd=$PWD
    cd /usr/share/zoneinfo
    local curcontext=zoneselector:::
    tmpzonename="./"
    printf "Press tab key to see possible zonename completions.\nExamples: US/Hawaii, America/Indiana/Knox, Asia/Kolkata, Europe/Athens\nAnswer statements ending in '?' with pressing 'y' or 'n'.\nEnter 'orig' to reset timezone\n";
    while [[ ! -f /usr/share/zoneinfo/$tmpzonename && ! -z $tmpzonename && $tmpzonename != "./orig" && $tmpzonename != "orig" ]]; do
        if [[ $tmpzonename =~ ^[./]+$ ]]; then
            tmpzonename="./"
            vared -p "Enter zonename (press tab): " -c tmpzonename;
        elif [[ -d /usr/share/zoneinfo/$tmpzonename ]]; then
            if [[ $tmpzonename[-1] != "/" ]]; then
                tmpzonename=$tmpzonename'/'
            fi
            vared -p "Enter zonename (press tab): " -c tmpzonename;
        else
            tmpzonename="./"
            vared -p "Enter zonename (press tab): " -c tmpzonename;
        fi
    done
    if [[ $tmpzonename = "./orig" || $tmpzonename = "orig" ]]; then
        tmpzonename=$tzlonginit
    fi
    tmpzonename=$(sed -n 's/^.*zoneinfo\/\(.*\)$/\1/p' <<<${tmpzonename:a})
    cd $restorepwd
}
function _zoneselector {
    _files -W /usr/share/zoneinfo
}
zstyle ':completion:zoneselector:*:' completer _zoneselector

zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}'
autoload -Uz compinit && compinit

function formattimedelta {
    if [[ ! $1 -gt 0 ]]; then
        local timedelta="$1"
        local timedelta=$(( -timedelta ))
        local timestate=" passed: "
    elif [[ $3 = "1" ]]; then
        local timedelta="$1"
        local timestate=" PAUSED: "
    else
        local timedelta="$1"
        local timestate="pending: "
    fi
    local days=$(($timedelta/86400))
    local hours=$(($timedelta%86400/3600))
    local minutes=$(($timedelta%3600/60))
    local seconds=$(($timedelta%60))
    if [[ $days != "0" || $2 != "0" ]]; then
        if [[ $2 != "0" ]]; then
            local formattedtime="${(l:$2::0:)days}d "
        else
            local formattedtime="${days}d "
        fi
    else
        local formattedtime=""
    fi
    formattedtime="$formattedtime${(l:2::0:)hours}h "
    formattedtime="$formattedtime${(l:2::0:)minutes}m "
    if [[ -z $updateonminute ]]; then
        formattedtime="$formattedtime${(l:2::0:)seconds}s"
    fi
    echo "$timestate$formattedtime"
}

function usr1input_handler {
    case "$1" in
        "a"|"b")
            local initialartname="$artname"
            local initialflipartname="$flipartname"
            if [[ $helpactive = "1" ]]; then
                artname="$restoreartname"
                flipartname="$restoreflipartname"
            fi
            $sttycmd $sttyorig
            printf "$tput_smcup"
            echoti clear
            echoti cup 0 0
            if [[ $1 = "a" ]]; then
                artselector
            else
                printf "Set b-art, finding art that matches height of a-art ($artname)...\nPress 'Enter' without typing anything to [31mCLEAR[0m b-art.\nPress a letter, followed by tab key to see possible artnames.\nPress Ctrl-d to see all possible artnames.\nAnswer statements ending in '?' with pressing 'y' or 'n'.\n\nAlternatives:\n";
                artselector2
            fi
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            tputset
            if [[ $tmpartname = "" ]]; then
                if [[ $1 = "b" ]]; then
                    flipartname=""
                fi
            elif [[ ! -f "$artdir/$tmpartname" ]]; then
                printf "E: No file found for art: $tmpartname.\nPress any key to continue..."
                read -t3 -k1
                while read dummy ; do : ; done 
            else
                if [[ $1 = "a" ]]; then
                    artname="$tmpartname"
                else
                    flipartname="$tmpartname"
                fi
            fi
            prevartname=""
            if ! setartstring; then
                printf "W: Height of b art does not match that of a art, try setting b art again.\nPress any key to continue..."
                if [[ $1 = "b" ]]; then
                    artname="$initialartname"
                    flipartname="$initialflipartname"
                    setartstring
                fi
                read -t5 -k1
                while read dummy ; do : ; done
            else
                if [[ $tmpartname = "" || ! -f "$artdir/$tmpartname" ]]; then
                    if [[ $helpactive = "1" ]]; then
                        artname="$initialartname"
                        flipartname="$initialflipartname"
                        setartstring
                    fi
                else
                    helpactive="0"
                fi
            fi
            #printart
            #echoti sc
            ;;
        "x"|"y")
            if ! command -v fzf-tmux; then
                echoti cup 0 0
                echo "$(echoti el)fzf binary not found, please install it"
                zselect -t 200
                while read dummy ; do : ; done 
                return
            fi
            local initialartname="$artname"
            local initialflipartname="$flipartname"
            if [[ $helpactive = "1" ]]; then
                artname="$restoreartname"
                flipartname="$restoreflipartname"
            fi
            tputreset
            $sttycmd $sttyorig
            if [[ $1 = "x" ]]; then
                artnametemp=$(ls $artdir | fzf-tmux --ansi --preview="$bindir/artprint -a {}  --ac $artcolor --tc $titlecolor  --style $style --theme $theme" -p 80%,80%  --preview-window=right,85%)
            else
                local restoredir=$PWD
                cd $artdir
                local tmpaart=$(getaart)
                local aartheight=$(wc -l "${tmpaart}" | sed -n 's/^[[:space:]]*\([0-9]*\)[[:space:]]*.*$/\1/p')
                artnametemp=$(wc -l * | sed -n '$d;s/^[[:space:]]*'"${aartheight}"'[[:space:]][[:space:]]*\(.*\)$/\1/p' | fzf-tmux --ansi --preview="$bindir/artprint -a {}  --ac $artcolor --tc $titlecolor  --style $style --theme $theme" -p 80%,80%  --preview-window=right,85%)
                cd $restoredir
            fi
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            tputset
            if [[ $artnametemp = "" ]]; then
            elif [[ ! -f "$artdir/$artnametemp" ]]; then
                printf "${tput_cup00}E: No file found for art: $artnametemp.\nPress any key to continue..."
                read -t3 -k1
                while read dummy ; do : ; done 
            else
                if [[ $1 = "x" ]]; then
                    artname="$artnametemp"
                else
                    flipartname="$artnametemp"
                fi
            fi
            prevartname=""
            if ! setartstring; then
            printf "${tput_cup00}W: Height of b art does not match that of a art, try setting b art again.\nPress any key to continue..."
                if [[ $1 = "y" ]]; then
                    artname="$initialartname"
                    flipartname="$initialflipartname"
                    setartstring
                fi
                read -t5 -k1
                while read dummy ; do : ; done
            else
                if [[ $artnametemp = "" || ! -f "$artdir/$artnametemp" ]]; then
                    if [[ $helpactive = "1" ]]; then
                        artname="$initialartname"
                        flipartname="$initialflipartname"
                        setartstring
                    fi
                else
                    helpactive="0"
                fi
            fi
            #printart
            #echoti sc
            ;;
        "j"|"o")
            jrestoreartname="$artname"
            jrestoreflipartname="$flipartname"
            flipartname=""
            local readresponse="$1"
            while [[ $readresponse == "$1" ]]; do
                RANDOM="${EPOCHREALTIME#*.}"
                if [[ $1 = "j" ]]; then
                    local shuffledart=($artdir/^tarot-*(.Nnoe['REPLY=$RANDOM']))
                else
                    local shuffledart=($artdir/tarot-*(.Nnoe['REPLY=$RANDOM']))
                fi
                for pathname in "$shuffledart[@]"; do
                    if [[ $winchdetected = "1" ]]; then
                        termwidth="$COLUMNS"
                        termheight="$LINES"
                        winchdetected="0"
                    fi
                    artname=$(basename "$pathname")
                    prevartname=""
                    setartstring
                    local questionstr="Select this? (c:cancel, $1:next, y:select), name: $artname "
                    printart $questionstr
                    lasttime=""
                    printtime "1"
                    # NOTE: now this pain of overlaying the same string twice is not necessary
                    #printf "${tput_cup00}%s%$((termwidth-${#questionstr}-1))s$tchar\n" "$questionstr" " "
                    readresponse=""
                    read -k1 readresponse
                    if [[ $readresponse = "y" || $readresponse = "c" ]]; then
                        break
                    fi
                done
            done
            # If response is not 'y', then restore the original state
            if [[ $readresponse != "y" ]]; then
                artname="$jrestoreartname"
                flipartname="$jrestoreflipartname"
                prevartname=""
                setartstring
                printart
                lasttime=""
                printtime "1"
            else
                # NOTE: commented and redundant "if" statement
                # but makes the case we care for clear.
                #if [[ $helpactive = "1" ]]; then
                helpactive="0"
                #fi
            fi
            ;;
        "c")
            if [[ -z $artcolor ]]; then
                artcolor="0"
            elif [[ $artcolor -eq 16 ]]; then
                artcolor=""
            else
                artcolor=$(( (artcolor+1)%16 ))
            fi
            setartstring
            #printart
            ;;
        #"d")
        #    echo "cliwallpaper[$$]: flipartname=$flipartname, lasttime=$lasttime, toggleflipart:$toggleflipart, artname:$artname, flipartht:$flipartht, artht:$artht, artstringht:$artstringht, flipartstringht:$flipartstringht" >> ~/arttime_debug.log
        #    ;;
        "p")
            if [[ $goaltime != "" && $goaldone = "0" || $goaltime = "" && $pausegoals = "1" ]]; then
                $sttycmd $sttyorig
                if [[ $pausegoals = "" || $pausegoals = "0" ]]; then
                    if askforconfirmation "pause goals"; then
                        pausegoals="1"
                        pausestart="$currenttimearray[1]"
                        pausestartstr="$currenttimearray[2]"
                        accumulatedsprinttime=$((currenttimearray[1]-sprintstarttime))
                        goaltime=""
                    fi
                else
                    if askforconfirmation "un-pause goals"; then
                        pauseend=$(strftime '%s')
                        pausedelta=$((pauseend-pausestart))
                        exitpause
                        sprintstarttime="$pauseend"
                        pausegoals="0"
                        goalentry=$goalarraysorted[$goalmaxptreff]
                        goalentryarray=(${(s/;/)goalentry})
                        sprintendtime=$goalentryarray[1]
                    fi
                fi
                $sttycmd -echo
                $sttycmd -icanon min 0 time 0
                echoti civis
            else
                echo "No goals set, so nothing to pause"
                zselect -t 200
                while read dummy ; do : ; done 
            fi
            ;;
        "e")
            if [[ ! -z $goal ]]; then
                $sttycmd $sttyorig
                if askforconfirmation "restart previous goal '$goal'"; then
                    goalsprint="1"
                    pausegoals="0"
                    accumulatedsprinttime="0"
                    sprintstarttime=$(strftime '%s')
                    setgoals $sprintstarttime
                fi
                $sttycmd -echo
                $sttycmd -icanon min 0 time 0
                echoti civis
            else
                echo "No goals set, so nothing to restart"
                zselect -t 200
                while read dummy ; do : ; done 
            fi
            ;;
        "g")
            echoti cup 0 0
            echoti el
            tempgoal=""
            $sttycmd $sttyorig
            if [[ $bashavailable = "1" ]]; then
                printf "${tput_cup00}${tput_cnorm}"
                tempgoal=$(bash -c 'read -e -p "Enter goal (\"help\" to learn): " retvar; echo $retvar');
                readstatus="0"
            else
                toggleflipart=$(((toggleflipart+1)%2))
                local tempscreen=$(printart;lasttime="";printtime '1')
                vared -p "$tempscreen${tput_cup00}Enter goal ('help' to learn):${tput_cnorm} " -c tempgoal;
            fi
            printf "$tput_civis"
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            if [[ ! -z $tempgoal ]]; then
                goal="$tempgoal"
                goalsprint="1"
                pausegoals="0"
                accumulatedsprinttime="0"
                sprintstarttime=$(strftime '%s')
                setgoals $sprintstarttime
                if [[ -z $goaltime ]]; then
                    if [[ -z $datehelpstr ]]; then
                        if [[ $dateisgnu = "1" ]]; then
read -r -d '' datehelpstr <<-'EOF'
You reached this help page because you entered an [31mincorrect[0m
value for goal time or used an [31munrecognized[0m format or entered 'help'.

arttime supports two types of time formats: 1) Native, 2) External.
External formats are enabled by GNU date, which is [32mINSTALLED[0m on your
system, and should be preferred (as they can be very expressive).

One can set multiple goals, and even sprint (loop) over a pattern of goals
multiple times. Multiple goals or a pattern of goals can be specified by
separating them with a semi-colon (;). Goals can be repeated by specifying
the last goal as 'loop'/'sprint' (to sprint forever) or 'loopN'/'sprintN'
(to sprint N number of times). See section 3) below to learn how to set
multiple goals, and/or a pattern of goals to repeat.

1) Native (simple, relative) formats:
    1m (1 minute from now)
    30s (30 seconds from now)
    48h (48 hours from now)
    2d 10s (2 days 10 seconds from now)
    1h 1m 1s (1 hour 1 minute 1 seconds from now)
    500m (500 minutes from now)
    400m 100s (400 minutes and 100 seconds from now)
    etc...

Native format can have any of: Ad Bh Cm Ds, where A/B/C/D are numbers.

2) External (simple/complex, absolute/relative, very expressive) formats:
    10AM (upcoming 10AM, local timezone unless specified)
    10:30AM IST (upcoming 10:30AM Indian Standard Time)
    May 26 10:30AM IST (10:30AM on May 26 in Indian Standard Time)
        Note: there is no comma after date 'May 26'
    next friday (upcoming friday)
    1min, +1min, 1 min, +1 minute, +1 minutes (1 minute from now)
    1min 30sec, 1min 30sec (1 minute 30 sec from now)
    next friday +4 hours (4AM on next friday in local time zone)
    next monday 1PM (1PM on next monday in local time zone)
    next monday 1PM IST (1PM on next monday in Indian Standard Time)
    and much more...
    
For detailed documentation on External date/time formats please refer:
    Web:      https://www.gnu.org/software/coreutils/date 
    Locally:  $ info '(coreutils) date invocation'
and jump directly to the section on "Date input formats".

3) Multiple and/or pattern of repeating goals:
    10s;20s (10 seconds and 20 seconds from now)
    10s;20s;1PM (10 seconds and 20 seconds from now, at 1PM in local time zone)
    10s;loop, 10s;sprint (every 10 seconds, repeat forever)
    10s;loop4, 10s;sprint4 (after 10 seconds, sprint 4 times)
    10s;1m;loop2 (after 10 seconds and 1m from now, sprint 2 times)
    5m;1PM;2PM;3PM;4PM;loop (after 5m from now and at 1-4 PM, repeat forever)
    25m;30m;55m;1h;1h25m;1h30m;1h55m;2h25m;loop (pattern of Pomodoro Technique)
EOF
                    else
read -r -d '' datehelpstr <<-'EOF'
You reached this help page because you entered an [31mincorrect[0m
value for goal time or used an [31munrecognized[0m format or entered 'help'.

arttime supports two types of time formats: 1) Native, 2) External.
External formats are enabled by GNU date, which is [31mNOT INSTALLED[0m on your
please search the web to find if/how to install GNU date on your system.
If installed, it will open up the possibility of being very expressive in
specifying goal date/time. After installing GNU date, restart arttime.

One can set multiple goals, and even sprint (loop) over a pattern of goals
multiple times. Multiple goals or a pattern of goals can be specified by
separating them with a semi-colon (;). Goals can be repeated by specifying
the last goal as 'loop'/'sprint' (to sprint forever) or 'loopN'/'sprintN'
(to sprint N number of times). See section 3) below to learn how to set
multiple goals, and/or a pattern of goals to repeat.

1) Native (simple, relative, should work on your system) formats:
    1m (1 minute from now)
    30s (30 seconds from now)
    48h (48 hours from now)
    2d 10s (2 days 10 seconds from now)
    1h 1m 1s (1 hour 1 minute 1 seconds from now)
    500m (500 minutes from now)
    400m 100s (400 minutes and 100 seconds from now)
    etc...

Native format can have any of: Ad Bh Cm Ds, where A/B/C/D are numbers.

2) External (very expressive, though won't work on your system) formats:
    10AM (upcoming 10AM, local timezone unless specified)
    10:30AM IST (upcoming 10:30AM Indian Standard Time)
    May 26 10:30AM IST (10:30AM on May 26 in Indian Standard Time)
        Note: there is no comma after date 'May 26'
    next friday (upcoming friday)
    1min, +1min, 1 min, +1 minute, +1 minutes (1 minute from now)
    1min 30sec, 1min 30sec (1 minute 30 sec from now)
    next friday +4 hours (4AM on next friday in local time zone)
    next monday 1PM (1PM on next monday in local time zone)
    next monday 1PM IST (1PM on next monday in Indian Standard Time)
    and much more...
    
For detailed documentation on External date/time formats please refer:
    Web:      https://www.gnu.org/software/coreutils/date 
    Locally:  $ info '(coreutils) date invocation'
and jump directly to the section on "Date input formats".

3) Multiple and/or pattern of repeating goals:
    10s;20s (10 seconds and 20 seconds from now)
    10s;20s;1PM (10 seconds and 20 seconds from now, at 1PM in local time zone)
    10s;loop, 10s;sprint (every 10 seconds, repeat forever)
    10s;loop4, 10s;sprint4 (after 10 seconds, sprint 4 times)
    10s;1m;loop2 (after 10 seconds and 1m from now, sprint 2 times)
    5m;1PM;2PM;3PM;4PM;loop (after 5m from now and at 1-4 PM, repeat forever)
    25m;30m;55m;1h;1h25m;1h30m;1h55m;2h25m;loop (pattern of Pomodoro Technique)
EOF
                        fi
                    fi
                    echoti clear
                    if command -v less &>/dev/null; then
                        pagernavstr="Press 'q' key to quit this page, scroll keys to scroll till you see [7m(END)[0m."
                        pagerprintstr=$(printf "$pagernavstr\n$datehelpstr")
                        less -R <<<"$pagerprintstr" 2>/dev/null
                    elif command -v more &>/dev/null; then
                        pagernavstr="Note: 'less' [31mnot found[0m, using 'more'. Press 'q' key to quit this page,\npress 'Enter' to scroll-forward till you see [7m(END)[0m, 'b' to scroll-backwards."
                        pagerprintstr=$(printf "$pagernavstr\n$datehelpstr")
                        #more <<<"$pagerprintstr"
                        mkdir -p ~/.cache/arttime
                        printf "$pagerprintstr" >~/.cache/arttime/$UID-$$.morestr
                        more ~/.cache/arttime/$UID-$$.morestr
                        rm ~/.cache/arttime/$UID-$$.morestr
                    fi
                    echoti civis
                    echoti smcup
                else
                    if [[ $returngoaltime = "loop" && ( $returngoaltimestr = "-" || $returngoaltimestr =~ ^[0-9]+$ ) ]]; then
                        printsprints="1"
                        goalmaxptreff="$((goalmaxptr-1))"
                        goalmaxsprint="$returngoaltimestr"
                    else
                        printsprints="0"
                        goalmaxsprint="1"
                        goalmaxptreff="$goalmaxptr"
                    fi
                    goalentry=$goalarraysorted[$goalmaxptreff]
                    goalentryarray=(${(s/;/)goalentry})
                    sprintendtime=$goalentryarray[1]
                fi
            fi
            ;;
        "f")
            if [[ $hoursfmt = "12" ]]; then
                hoursfmt="24"
            else
                hoursfmt="12"
            fi
            settimeformats
            if [[ $tzshortcurrent != $tzshortinit || $tzlongcurrent != $tzlonginit ]]; then
                timeformat=$tfother
            else
                timeformat=$tflocal
            fi
            starttime=$(strftime $timeformat $starttimearray[1])
            starttimearray=(${(s/|/)starttime})
            if [[ ! -z $goaltime ]]; then
                local tmpgoaltime=$(strftime $timeformat $goaltime)
                local tmpgoaltimearray=(${(s/|/)tmpgoaltime})
                goaltimestr=$tmpgoaltimearray[2]
                local goalentryarray
                local goaltimestrupdated
                for ((i=1;i<=goalmaxptreff;i=i+1)) do
                    #$returngoaltime;$numgoals;$returngoalstatus;$returngoaltimestr;$goal
                    goalentryarray=(${(s/;/)goalarraysorted[$i]})
                    goaltimestrupdated=$(strftime $timeformat $goalentryarray[1])
                    goaltimestrupdated="${goaltimestrupdated#*|}"
                    goalarraysorted[$i]=("$goalentryarray[1];$goalentryarray[2];$goalentryarray[3];$goaltimestrupdated;$goalentryarray[5]")
                done
            fi
            ;;
        "h")
            if [[ $helpactive = "0" ]]; then
                helpactive="1"
                restoreartname="$artname"
                restoreflipartname="$flipartname"
                artname="help"
                flipartname=""
            else
                helpactive="0"
                artname="$restoreartname"
                flipartname="$restoreflipartname"
            fi
            prevartname=""
            setartstring
            local setartstringerr="$?"
            printart;
            lasttime="";
            printtime "1"
            if [[ $setartstringerr = "1" ]]; then
                printf "${tput_cup00}W: Height of b art does not match that of a art, try setting b art again.\nPress any key to continue..."
                read -t5 -k1
                while read dummy ; do : ; done
            fi
            ;;
        "i"|"I")
            local tmpcurtime=$(strftime '%s')
            local infostring=$(strftime "%a %b %d, %Y $hms %Z")
            infostring+=" ($tzlongcurrent)"
            infostring+="$(strftime ' | week %W')"
            infostring+=" | ID: $$ "
            if [[ $helpactive = "1" ]]; then
                if [[ $restoreflipartname != "" ]]; then
                    infostring+=" \narts: a) $restoreartname, b) $restoreflipartname "
                else
                    infostring+=" \narts: a) $restoreartname, b) none "
                fi
            else
                if [[ $flipartname != "" ]]; then
                    infostring+=" \narts: a) $artname, b) $flipartname "
                else
                    infostring+=" \narts: a) $artname, b) none "
                fi
            fi
            if [[ $goaltime != "" && $goaldone = "0" || $goaltime = "" && $pausegoals = "1" ]]; then
                infostring+="\nGoals: "
                # NOTE: first query the last entry to find width of days 
                local goalentryarray=(${(s/;/)goalarraysorted[$goalmaxptreff]})
                local goaltimeunix="$goalentryarray[1]"
                local timepending=$((goaltimeunix-currenttimearray[1]))
                timepending=$((timepending/86400))
                if [[ $timepending != "0" ]]; then
                    local datewidth="${#timepending}"
                else
                    local datewidth="0"
                fi
                for ((i=1;i<=goalmaxptr;i=i+1)) do
                    #$returngoaltime;$numgoals;$returngoalstatus;$returngoaltimestr;$goal
                    goalentryarray=(${(s/;/)goalarraysorted[$i]})
                    local goalnum="$goalentryarray[2]"
                    local goalstatus="$goalentryarray[3]"
                    local goalstr="$goalentryarray[5]"
                    if [[ $goalstr != "loop" ]]; then
                        if ((i>=goalptr && pausegoals==1)); then
                            goaltimeunix=$((currenttimearray[1]+goalentryarray[1]-pausestart))
                            local goaltimestr=$(strftime "%b %d, $hms" $goaltimeunix)
                            timepending=$(formattimedelta $((goaltimeunix-currenttimearray[1])) $datewidth "1")
                        else
                            goaltimeunix="$goalentryarray[1]"
                            local goaltimestr=$(strftime "%b %d, $hms" $goaltimeunix)
                            timepending=$(formattimedelta $((goaltimeunix-currenttimearray[1])) $datewidth "0")
                        fi
                        if ((i>goalptr && pausegoals==1)); then
                            infostring+="\n    $i. est.: $goaltimestr ($timepending), goal: $goalstr "
                        elif ((i==goalptr && pausegoals==1)); then
                            infostring+="\n  > $i. [31mest.: $goaltimestr ($timepending), goal: $goalstr [0m"
                        elif [[ $i = $goalptr ]]; then
                            infostring+="\n  > $i. [31mtime: $goaltimestr ($timepending), goal: $goalstr [0m"
                        else
                            infostring+="\n    $i. time: $goaltimestr ($timepending), goal: $goalstr "
                        fi
                    else
                        if [[ $goalmaxsprint != "-" ]]; then
                            infostring+="\n       sprint $goalsprint/$goalmaxsprint "
                        else
                            infostring+="\n       sprint $goalsprint/infinity "
                        fi
                    fi
                done
            fi
            if [[ $1 = "I" ]] || (((termheight <= (goalmaxptr+4)) && goaldone==0 )); then
                echoti clear
                if command -v less &>/dev/null; then
                    pagernavstr="Press 'q' key to quit this page, scroll keys to scroll till you see [7m(END)[0m."
                    pagerprintstr=$(printf "$pagernavstr\n$infostring")
                    less -R <<<"$pagerprintstr"  2>/dev/null
                elif command -v more &>/dev/null; then
                    pagernavstr="Note: 'less' [31mnot found[0m, using 'more'. Press 'q' key to quit this page,\npress 'Enter' to scroll-forward till you see [7m(END)[0m, 'b' to scroll-backwards."
                    pagerprintstr=$(printf "$pagernavstr\n$infostring")
                    #more <<<"$pagerprintstr"
                    mkdir -p ~/.cache/arttime
                    printf "$pagerprintstr" >~/.cache/arttime/$UID-$$.morestr
                    more ~/.cache/arttime/$UID-$$.morestr
                    rm ~/.cache/arttime/$UID-$$.morestr
                fi
                echoti civis
                echoti smcup
            else
                infostring+="\nPress any key to continue "
                printf "$infostring"
                read -t5 -k1
            fi
#tput -S <<HERE
#            cup 1 0
#            el
#HERE
            while read dummy ; do : ; done
            ;;
        "l")
            if [[ ! -z $goaltime ]]; then
                $sttycmd $sttyorig
                if askforconfirmation "clear goals"; then
                    goaltime=""
                    pausegoals="0"
                fi
                $sttycmd -echo
                $sttycmd -icanon min 0 time 0
                echoti civis
            else
                echo "No active goal to clear"
                zselect -t 200
                while read dummy ; do : ; done 
            fi
            ;;
        "m")
            echoti cup 0 0
            echoti el
            temptitle=""
            $sttycmd $sttyorig
            #printf "Enter message (Ctrl-d to cancel): "
            #read temptitle
            if [[ $bashavailable = "1" ]]; then
                printf "${tput_cup00}${tput_cnorm}"
                temptitle=$(bash -c 'read -e -p "Enter title message (\"orig\" to reset): " retvar; echo $retvar');
                readstatus="0"
            else
                toggleflipart=$(((toggleflipart+1)%2))
                local tempscreen=$(printart;lasttime="";printtime '1')
                vared -p "$tempscreen${tput_cup00}Enter title message ('orig' to reset):${tput_cnorm} " -c temptitle;
                readstatus="$?"
            fi
            printf "$tput_civis"
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            if [[ $temptitle = "" || $readstatus != "0" ]]; then
            elif [[ $temptitle = "-" ]]; then
                title=' - '
            elif [[ $temptitle = "orig" ]]; then
                title=""
            else
                title="$temptitle"
            fi
            prevartname=""
            setartstring
            #printart
            ;;
        "r")
            $sttycmd $sttyorig
            if askforconfirmation "reset start time, and goal"; then
                resetstarttime="1"
                goaltime=""
                pausegoals="0"
            fi
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            echoti civis
            ;;
        "q")
            $sttycmd $sttyorig
            if askforconfirmation "quit arttime"; then
                trap -;
                command -v pkill &>/dev/null && pkill -P $$;
                echoti cup $LINES 0
                printf "\n"
                tputreset;
                if [[ $trapintactive = "1" ]]; then
                    set -e
                    (exit 130)
                else
                    exit 0
                fi
            fi
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            echoti civis
            ;;
        #"s")
        #    echoti cup 0 0
        #    echoti el
        #    tempsamples=""
        #    $sttycmd $sttyorig
        #    echoti cnorm
        #    if [[ ! -z $updateonminute ]]; then
        #        # XXX: look here
        #        printf "Enter samples per minute (current value = $samples): "
        #    else
        #        printf "Enter samples per seconds (current value = $samples): "
        #    fi
        #    read tempsamples
        #    $sttycmd -echo
        #    $sttycmd -icanon min 0 time 0
        #    echoti civis
        #    if [[ -z $tempsamples || ! $tempsamples =~ ^([1-9]\|[1-9][0-9]+)$ ]]; then
        #        echoti cup 0 0
        #        printf "Error: samples should be an integer > 0"
        #        zselect -t 200
        #    else
        #        samples="$tempsamples"
        #        setsleeptimecenti
        #        #echo "Samples: $samples, sleeptime: $sleeptime, sleeptimecenti: $sleeptimecenti"
        #        #zselect -t 300
        #    fi
        #    ;;
        "t")
            if [[ -z $theme || $theme = "dark" ]]; then
                theme="light"
            else
                theme="dark"
            fi
            setcolors
            setartstring
            #printart
            ;;
        "z")
            $sttycmd $sttyorig
            printf "$tput_smcup"
            echoti cup 0 0
            echoti cnorm
            echoti clear
            zoneselector
            $sttycmd -echo
            $sttycmd -icanon min 0 time 0
            tputset
            if [[ ! -z $tmpzonename ]]; then
                TZ=$tmpzonename
                notetimezone
                tzlongcurrent="$tzlong"
                tzshortcurrent="$tzshort"
                if [[ $tzshortcurrent != $tzshortinit || $tzlongcurrent != $tzlonginit ]]; then
                    timeformat=$tfother
                else
                    timeformat=$tflocal
                fi
                starttime=$(strftime $timeformat $starttimearray[1])
                starttimearray=(${(s/|/)starttime})
                if [[ ! -z $goaltime ]]; then
                    local tmpgoaltime=$(strftime $timeformat $goaltime)
                    local tmpgoaltimearray=(${(s/|/)tmpgoaltime})
                    goaltimestr=$tmpgoaltimearray[2]
                    local goalentryarray
                    local goaltimestrupdated
                    for ((i=1;i<=goalmaxptreff;i=i+1)) do
                        #$returngoaltime;$numgoals;$returngoalstatus;$returngoaltimestr;$goal
                        goalentryarray=(${(s/;/)goalarraysorted[$i]})
                        goaltimestrupdated=$(strftime $timeformat $goalentryarray[1])
                        goaltimestrupdated="${goaltimestrupdated#*|}"
                        goalarraysorted[$i]=("$goalentryarray[1];$goalentryarray[2];$goalentryarray[3];$goaltimestrupdated;$goalentryarray[5]")
                    done
                fi
            fi
            ;;
        *)
            echo "Press 'h' to see which keys do what"
            zselect -t 200
            while read dummy ; do : ; done 
            ;;
    esac
}

#usr1detected=""
#function trapusr1detected {
#    usr1detected="1"
#}

function trapusr1_both {
    # NOTE: disable C-z when executing this trap
    trap '' TSTP
#tput -S <<HERE
#    sc
#    cup 0 0
#HERE
    printf "${tput_sc}${tput_cup00}"
    # slurp previous user input
    userinput="$1"
    if [[ -z $userinput ]]; then
        #while read dummy ; do : ; done 
        printf "\r ⌨ ";
        read -t 2 -k userinput
        printf "$userinput "
    else
        #while read dummy ; do : ; done 
    fi
    if [[ ! -z "$userinput" ]]; then
        usr1input_handler $userinput
    fi
#tput -S <<HERE
#    cup 0 0
#    el
#    rc
#HERE
    printart
    lasttime=""
    printtime "1"
    resetstarttime="0"
    userinput=""
    if [[ $nonblocking = "1" ]]; then
        trap "trapstop_nonblocking" TSTP
    else
        trap "trapstop_blocking" TSTP
    fi
}

function arttime_blocking {
    difftime="0"
    hours="0"
    minutes="0"
    seconds="0"
    starttime=$(strftime $timeformat)
    starttimearray=(${(s/|/)starttime})
    printtime "1"
    #trap "trapint_blocking" INT
    trap "" INT
    trap "trapexit_blocking" EXIT TERM HUP QUIT
    #trap "trapusr1_both" USR1
    trap "trapstop_blocking" TSTP
    trap "trapcont_blocking" CONT
    while true; do
        #zselect -t "$sleeptimecenti"
        if [[ $winchdetected = "1" ]]; then
            trapwinch
        fi
        epochtimereal=$EPOCHREALTIME
        epochtimerealarray=("${(@s/./)epochtimereal}")
        timetoepoch=$((1.0 - 0.$epochtimerealarray[2] + 0.01))
        read -t "$timetoepoch" -k tmpusrinput
        if [[ $? = "0" ]]; then
            trapusr1_both "$tmpusrinput"
        fi
        #if [[ -z $artcolor ]]; then
        #    artcolor="0"
        #elif [[ $artcolor -eq 16 ]]; then
        #    artcolor=""
        #else
        #    artcolor=$(( (artcolor+1)%16 ))
        #fi
        #setartstring
        if [[ $winchdetected = "1" ]]; then
            trapwinch
        else
            #printart
            printtime
        fi
    done
}


function arttime_nonblocking {
    difftime="0"
    hours="0"
    minutes="0"
    seconds="0"
    starttime=$(strftime $timeformat)
    starttimearray=(${(s/|/)starttime})
    printtime
    #trap "trapusr1_both" USR1
    trap "trapcont_nonblocking" CONT
    trap "trapstop_nonblocking" TSTP
    while true; do
        #ARGV0="arttimesleep" sleep $sleeptime &
        zselect -t $sleeptimecenti &
        sleeppid="$!"
        trap "trapint_nonblocking $sleeppid" INT QUIT
        trap "trapexit_nonblocking $sleeppid" EXIT TERM HUP
        wait $sleeppid
        printtime
    done
}

winchdetected="0"

function setwinch {
    winchdetected="1"
}

function trapwinch {
    winchdetected="0"
    if [[ ! -z $TMUX ]]; then
        ignorewinch=$(tmux show-options -vp -t $TMUX_PANE @arttimeterm-ignorewinch 2>/dev/null)
        if [[ "$ignorewinch" != "1" ]]; then
            if [[ ! "$termwidth" -eq "$COLUMNS" || ! "$termheight" -eq "$LINES"  ]]; then
                termwidth="$COLUMNS"
                termheight="$LINES"
                echoti clear; prevartname=""; setartstring; printart; lasttime=""; printtime "1"
            fi
        fi
    else
        if [[ ! "$termwidth" -eq "$COLUMNS" || ! "$termheight" -eq "$LINES"  ]]; then
            termwidth="$COLUMNS"
            termheight="$LINES"
            echoti clear; prevartname=""; setartstring; printart; lasttime=""; printtime "1"
        fi
    fi
    if [[ $winchdetected = "1" ]]; then
        trapwinch
    fi
}

tputset
printart
trap 'setwinch' WINCH
if [[ $nonblocking = "1" ]]; then
    arttime_nonblocking
else
    arttime_blocking
fi
tputreset
$sttycmd $sttyorig
