#!/bin/mksh
# $MirOS: contrib/hosted/tg/deb/mksh/debian/uhr,v 1.14 2013/02/19 18:07:46 tg Exp $
#-
# Copyright © 2012, 2013
#	Thorsten Glaser <tg@mirbsd.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.
#-
# Analoguhr mit Digitalanzeige. Grundlegende Annahme: schnelles Ter‐
# minal, d.h. keine Voroptimierung der Darstellung durch das Skript;
# Font im Seitenverhältnis 1:2 (z.B. 9x18 aus XFree86® fixed-misc).

if [[ $KSH_VERSION != @(\@\(#\)MIRBSD KSH R)@(4[1-9]|[5-9][0-9]|[1-9][0-9]+([0-9]))\ +([0-9])/+([0-9])/+([0-9])?(\ *) ]]; then
	print -u2 Uhr requires mksh R41 or newer.
	exit 1
fi
typeset -Z6 tosleep

# stupid GNU idiots breaking everything by default… grml…
bcopt=
bc --help >/dev/null 2>&1 && bcopt=-q

function graceful {
	print -n '\033[;H\033[J'
	exit 0
}
trap graceful INT TERM HUP

trap got_sigwinch=1 WINCH
while :; do
got_sigwinch=0

print "\e[0m\nPregenerating arrays, please wait..."
(( r = LINES * 2 ))
(( r = (r > COLUMNS ? COLUMNS : r) / 2 - 1))
(( n = 2 * r + 1 ))
set -A fb
integer fb

integer F_NO=0x00 M_NO=0x1F
integer F_BG=0x01 M_BG=0x1E
integer F_CC=0x02 M_CC=0x1D
integer F_HP=0x04 M_HP=0x1B
integer F_MP=0x08 M_MP=0x17
integer F_SP=0x10 M_SP=0x0F
integer B_BG=0x01 B_BLK=0x02 B_NB=0x0C B_DOT=0x10

set -U
#	-	BLK	BG	NB	DOT	NB|DOT
set -A m2c \
	0x20	1#▀	1#*	1#▀	1#·	1#░	\
	1#▄	1#█	1#█	1#█	1#▆	1#█	\
	1#*	1#█	1##	1#◘	1#⁂	1#◙	\
	1#▄	1#█	1#▆	1#█	1#▒	1#▓	\
	1#.	1#▛	1#☿	1#▛	1#:	1#▒	\
	1#▄	1#█	1#◙	1#█	1#▆	1#▓
typeset -i1 m2c[*]

set -A m2m
integer m2m

integer i=-1 j
while (( ++i <= 0x1F )); do
	(( m2m[i] = !i ? 0 : (i & B_BLK) ? 1 :
	    (i & B_NB) ? ((i & B_DOT) ? 5 : 3) : (i & B_DOT) ? 4 : 2 ))
done

function refresh {
	local -i10 i j z s c
	local t

	for i in "$@"; do
		(( z = (i / n) & 0xFFFE ))
		(( s = i % n ))
		(( i = m2m[fb[z * n + s]] ))
		(( j = m2m[fb[(z + 1) * n + s]] ))
		print -n "\e[$((z / 2 + 1));$((s + 1))H${m2c[j * 6 + i]#1#}"
	done
	print -n "\e[$((r / 2 + 1));$((r + 1))H\e[7mⓄ\e[0m"
}

# put arrayname x y
function put {
	local _x=$(($2)) _y=$(($3)) _i
	nameref _c=$4 _px=$1

	(( _i = (r - _y) * n + _x + r ))
	_px+=($_i)
}

# retrace arrayname maskname colourname
set -A px
function retrace {
	nameref _px=$1 _m=$2 _c=$3
	local _i

	for _i in "${_px[@]}"; do
		(( fb[_i] = (fb[_i] & _m) | _c ))
	done
	px+=("${_px[@]}")
}

# precalculate all lines’ endpoints with bc and paths with Bresenham
integer x y dx sx dy sy e f
bc -l $bcopt |&
print -p scale=20
print -p r=$r
print -p o=r
print -p 'define p(t) {
	auto d
	d = 90 - t
	if (d < 0) d = 360 + d
	return (d * 3.1415926535897932 / 180)
}'
# minutes and seconds – full length, 60 items
i=-1
while (( ++i < 60 )); do
	eval set -A lms$i
	print -p "r * c(p($i * 6))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	x=${S%%.*}
	print -p "r * s(p($i * 6))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	y=${S%%.*}
	(( dx = x < 0 ? -x : x ))
	(( sx = x < 0 ? 1 : -1 ))
	(( dy = y < 0 ? y : -y ))
	(( sy = y < 0 ? 1 : -1 ))
	(( e = dx + dy ))
	while :; do
		put lms$i x y
		(( !x && !y )) && break
		(( f = 2 * e ))
		if (( f > dy )); then
			(( e += dy ))
			(( x += sx ))
		fi
		if (( f < dx )); then
			(( e += dx ))
			(( y += sy ))
		fi
	done
done
# hours – 2/3 length, 60 items (5 per hour)
print -p 'r = o * 2 / 3'
i=-1
while (( ++i < 60 )); do
	eval set -A lh$i
	print -p "r * c(p($i * 6))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	x=${S%%.*}
	print -p "r * s(p($i * 6))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	y=${S%%.*}
	(( dx = x < 0 ? -x : x ))
	(( sx = x < 0 ? 1 : -1 ))
	(( dy = y < 0 ? y : -y ))
	(( sy = y < 0 ? 1 : -1 ))
	(( e = dx + dy ))
	while :; do
		put lh$i x y
		(( !x && !y )) && break
		(( f = 2 * e ))
		if (( f > dy )); then
			(( e += dy ))
			(( x += sx ))
		fi
		if (( f < dx )); then
			(( e += dx ))
			(( y += sy ))
		fi
	done
done
# hour markers – 80% length, 12 items
print -p 'r = o * 8 / 10'
i=-1
set -A mkx
set -A mky
while (( ++i < 12 )); do
	print -p "r * c(p($i * 30))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	mkx[i]=${S%%.*}
	print -p "r * s(p($i * 30))"
	read -p S; [[ $S = ?(-).* ]] && S=0
	mky[i]=${S%%.*}
done
exec 3>&p; exec 3>&-

(( L = LINES >= (COLUMNS / 2) ? (COLUMNS / 2) : LINES ))
# fine-tuning of roman numeral position via screen size
(( ++mkx[7] ))
(( ++mkx[8] ))
case $L {
(22|23)	(( ++mkx[6] )) ;|
(23)
	(( mky[1] += 2 ))
	(( mky[2] += 2 ))
	(( mky[10] += 2 ))
	(( mky[11] += 2 ))
	;;
(24|25|29|30|31|34)
	(( mky[4] += 2 ))
	(( mky[8] += 2 ))
	;|
(27|28|29)
	(( ++mkx[10] ))
	(( mky[8] += 2 ))
	(( mky[9] += 2 ))
	(( mky[10] += 2 ))
	;|
(27|29|31)
	(( mky[0] -= 2 ))
	;|
(27)
	(( --mkx[4] ))
	(( --mkx[5] ))
	(( ++mkx[6] ))
	(( mkx[7] += 2 ))
	(( ++mkx[8] ))
	(( ++mkx[10] ))
	;;
(29)
	(( mky[5] += 2 ))
	(( mky[7] += 2 ))
	;;
(30)
	(( mky[11] -= 2 ))
	;;
}
(( mky[0] += 2 * (L & 1) ))

# clear framebuffer and screen
set -A fb
integer fb
print -n -- '\e[H\e[J'

# draw hour markers
set -A lb
integer e f=-1 k
(( L > 21 )) && while (( ++f < 12 )); do
	(( i=mkx[f] ))
	(( j = mky[f] & ~1 ))
	case $f {
	(0) e=7 S='# # # # #  # ## # # #' ;;
	(1) e=1 S='###' ;;
	(2) e=3 S='# ## ## #' ;;
	(3) e=5 S='# # ## # ## # #' ;;
	(4) e=5 S='# # ## # ##  # ' ;;
	(5) e=3 S='# ## # # ' ;;
	(6) e=5 S='# # ## # # #  #' ;;
	(7) e=7 S='# # # ## # # # #  # #' ;;
	(8) e=9 S='# # # # ## # # # # #  # # #' ;;
	(9) e=5 S='# # ##  # # # #' ;;
	(10) e=3 S='# # # # #' ;;
	(11) e=5 S='# # # #  ## # #' ;;
	}
	Y='0 1 2'
	if (( L > 26 )); then
		d='###########'
		S="${d::e+2} ${S::e}  ${S: e:e}  ${S:2*e} ${d::e+2}"
		(( e += 2 ))
		Y+=' 3 4'
		(( j += 2 ))
	fi
	(( i -= e / 2 ))
	k=0
	for y in $Y; do
		(( y = j - y * 2 + 1 + (r & 1) ))
		(( dy = y + 1 ))
		(( x = i - 1 ))
		while (( ++x < (i + e) )); do
			[[ ${S: k++:1} = ' ' ]] && continue
			put lb x y
			put lb x dy
		done
	done
done
retrace lb M_BG F_BG

# draw outer circle with Bresenham
set -A lc
integer x=r y=-1 f=r dx dy
while (( y < x )); do
	(( dy = y++ * 2 + 1 ))
	if (( y )); then
		(( f -= dy ))
		if (( f < 0 )); then
			(( dx = 1 - x-- * 2 ))
			(( f -= dx ))
		fi
	fi
	put lc x y
	put lc -x y
	put lc -x -y
	put lc x -y
	put lc y x
	put lc -y x
	put lc -y -x
	put lc y -x
done
retrace lc M_CC F_CC
refresh "${px[@]}"; set -A px

set -A do -- -1 -1 -1
isfirst=1
while (( !got_sigwinch )); do
	if (( isfirst )); then
		isfirst=0
	else
		(( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
		if (( tosleep > 999999 )); then
			sleep 0.2
			(( tosleep = 1000000 - ${EPOCHREALTIME#*.} ))
		fi
		if (( tosleep > 999999 )); then
			# huh… maybe no gettimeofday(2) here
			while :; do
				d=$(date +'%H %M %S,%d %b %Y')
				set -A dt $d
				(( dt[2] == do[2] )) || break
				sleep 0.1
			done
		else
			sleep 0.$tosleep
		fi
	fi

	d=$(date +'%H %M %S,%d %b %Y')
	S=${d#*,}
	d=${d%,*}
	set -A dt $d

	(( dt[0] = (dt[0] % 12) * 5 + (dt[1] / 12) ))
	if (( do[2] != -1 )); then
		retrace lms$((do[2])) M_SP F_NO
		(( do[1] == dt[1] )) || retrace lms$((do[1])) M_MP F_NO
		(( do[0] == dt[0] )) || retrace lh$((do[0])) M_HP F_NO
	fi
	(( do[0] == dt[0] )) || retrace lh$((dt[0])) M_HP F_HP
	(( do[1] == dt[1] )) || retrace lms$((dt[1])) M_MP F_MP
	retrace lms$((dt[2])) M_SP F_SP
	refresh "${px[@]}"; set -A px
	set -A do -- "${dt[@]}"

	print -n "\e[1;$((n - ${%S} + 1))H$S\e[1;1H${d// /:}"
done
done
