# -*- mode: sh -*-
# $MirOS: contrib/hosted/tg/progress-bar,v 1.5 2020/04/13 16:05:20 tg Exp $
#-
# Copyright © 2018, 2020
#	mirabilos <m@mirbsd.org>
# Copyright © 2017
#	mirabilos <t.glaser@tarent.de>
# Copyright © 2015, 2017
#	mirabilos <thorsten.glaser@teckids.org>
#
# Provided that these terms and disclaimer and all copyright notices
# are retained or reproduced in an accompanying document, permission
# is granted to deal in this work without restriction, including un‐
# limited rights to use, publicly perform, distribute, sell, modify,
# merge, give away, or sublicence.
#
# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
# the utmost extent permitted by applicable law, neither express nor
# implied; without malicious intent or gross negligence. In no event
# may a licensor, author or contributor be held liable for indirect,
# direct, other damage, loss, or other issues arising in any way out
# of dealing in the work, even if advised of the possibility of such
# damage or existence of a defect, except proven that it results out
# of said person’s immediate fault when using the work as intended.
#-
# Shell library for easy display of a progress bar
#
# Usage:
# – before:	init_progress_bar $n
# – $n times:	draw_progress_bar
# – after:	done_progress_bar
#
# init_progress_bar trashes the EXIT and SIGWINCH traps, which later
# are cleared, again, by done_progress_bar; note this forces using a
# “while [[ -n $(jobs) ]]; do wait; done” loop instead of just wait.
# Use “redo_progress_bar [±]$n” to rerender updating the estimate.

# global variables used by this library
_cnt_progress_bar=0
_cur_progress_bar=0
isin_progress_bar=0
nlin_progress_bar=0
_cch_progress_bar=

[[ $KSH_VERSION = '@(''#)MIRBSD KSH R'@(4[0-9]|5[0-4])\ * ]] || \
    alias global='typeset -g'

# args: $1 = number of draw_progress_bar calls to make up 100%
function init_progress_bar {
	global -i _cnt_progress_bar=$1 _cur_progress_bar=0
	global -i nlin_progress_bar=$LINES isin_progress_bar=1
	_cch_progress_bar=

	trap 'done_progress_bar $?' EXIT
	trap 'sigwinch_progress_bar' WINCH
	# set up scrolling region, draw initial empty bar
	sigwinch_progress_bar
}

unalias global

function sigwinch_progress_bar {
	(( isin_progress_bar )) || return 0

	# get new terminal size
	nlin_progress_bar=$LINES

	# save position; clear scrolling region; restore position; newline;
	# up one line (to ensure we are not in the last line); save position;
	# clear rest of screen; set new scrolling region; restore position
	print -nu2 "\\e7\\e[0;0r\\e8\\n\\e[A\\e7\\e[J\\e[1;$((# nlin_progress_bar - 1))r\\e8"

	# redraw progress bar
	draw_progress_bar_internal
}

function done_progress_bar {
	(( isin_progress_bar )) || return 0
	isin_progress_bar=0
	_cch_progress_bar=
	# save position; clear scrolling region; restore position;
	# save position; clear rest of screen; restore position
	print -nu2 "\\e7\\e[0;0r\\e8\\e7\\e[J\\e8"
	trap - WINCH
	trap - EXIT
	[[ -z $1 ]] || return $1
	(( _cur_progress_bar == _cnt_progress_bar )) || \
	    print -ru2 W: expected $_cnt_progress_bar draw_progress_bar calls, \
	    got only $_cur_progress_bar
}

function draw_progress_bar {
	# increment current progress
	if (( ++_cur_progress_bar > _cnt_progress_bar )); then
		print -ru2 "W: too many draw_progress_bar calls"
		_cur_progress_bar=$_cnt_progress_bar
	fi
	# remaining drawing code
	draw_progress_bar_internal
}

function redo_progress_bar {
	if [[ $1 = +* ]]; then
		(( _cnt_progress_bar += ${1#+} ))
	elif [[ $1 = -* ]]; then
		(( _cnt_progress_bar -= ${1#-} ))
	else
		_cnt_progress_bar=$1
	fi
	if (( _cur_progress_bar > _cnt_progress_bar )); then
		print -ru2 W: new estimate $_cnt_progress_bar too low \
		    after $_cur_progress_bar calls
		_cur_progress_bar=$_cnt_progress_bar
	fi
	_cch_progress_bar=
	draw_progress_bar_internal
}

function draw_progress_bar_internal {
	local bar num w=$COLUMNS pct

	((# num = (_cur_progress_bar * w * 8) / _cnt_progress_bar ))
	((# pct = _cur_progress_bar * 100 / _cnt_progress_bar ))
	[[ $_cch_progress_bar != $num.$pct ]] || return 0
	while ((# num >= 8 )); do
		bar+=█
		((# num -= 8 ))
	done
	case $num {
	(7) bar+=▉ ;;
	(6) bar+=▊ ;;
	(5) bar+=▋ ;;
	(4) bar+=▌ ;;
	(3) bar+=▍ ;;
	(2) bar+=▎ ;;
	(1) bar+=▏ ;;
	}
	# fill complete line, right-align completion percentage display
	local -R$w spc="$pct%"
	# elide percentage when it stops fitting
	((# (_cur_progress_bar * w / _cnt_progress_bar) > (w - 4) )) && spc=
	# save position; go to last line; set colours;
	# output a line full of spaces (and completion percentage);
	# jump to first column; output bar (line præfix); restore position
	print -nu2 -- "\\e7\\e[$nlin_progress_bar;1H\\e[0;1;33;44m$spc\\r$bar\\e8"
	_cch_progress_bar=$num.$pct
}
