/* a clock. JosBoersema Oct-2000 (C)
  
 Copyright (C) 2001-2006  J.H. Boersema

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; version 2 of the License, or any later
    at your option.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software:
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
		02110-1301  USA

*/
/*                             Oct2001 added options */
/*                             Sep2006 added options */
/* Compile on 2.2.17 Debian/Linux: gcc -lm -lncurses clockalrm.c */
/* Works with ncurses 5.x */
/* This code has not been optimised or anything like that. */
/* And it doesn't have a lot of comments :-(, sorry */

/* Testing: On a Debian system (at least), /usr/share/zoneinfo contains
 * files with local-time definitions; 
 * % ln -s /usr/share/zoneinfo/ZONE /etc/localtime
 * changes the timezone to ZONE */

#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include <unistd.h>
#include <time.h>
#include <math.h>
#include <string.h>
#include <signal.h>

#define VERSION "1.05"
#define SLEEP_SYSTIME_ERROR 2
#define REVERSE_VIDEO 0
#define SLEEP_SYSTIME_ERROR_STR "2"
#define U_COLOR_HOUR 1
#define COLOR_HOUR COLOR_WHITE
#define COLOR_HOUR_BG COLOR_BLACK
#define U_COLOR_MIN 2
#define COLOR_MIN COLOR_WHITE
#define COLOR_MIN_BG COLOR_BLACK
#define U_COLOR_SEC 3
#define COLOR_SEC COLOR_RED
#define COLOR_SEC_BG COLOR_BLACK
#define U_COLOR_DEL 4
#define COLOR_DEL COLOR_BLACK
#define COLOR_DEL_BG COLOR_BLACK
#define COLOR_ALARM COLOR_GREEN
#define COLOR_ALARM_BG COLOR_BLACK
#define ERASE_VISIBLE '.'
#define ERASE_INVISIBLE ' '
#define SEC_LENTH 120
#define MIN_LENGTH 120
#define HOUR_LENTH 60
#define PROG "clockalrm"
#define USAGE_EXIT 1
#define SYSTIME_EXIT 2
#define PAST_EXIT 3
#define ERR_MKTIME_EXIT 4
#define ALT_SECOND 's'
#define ALT_MINUTE 'm'
#define ALT_HOUR 'H'
#define SECOND_ARM 1
#define MINUTE_ARM 2
#define HOUR_ARM 3
#define PRINT_MODE_GRAPHIC 1 /* The character used for an arm changes during rotation */
#define PRINT_MODE_FIXED 2 /* The character used for an arm is fixed */
#define SEC_YEAR 31558118 /* Average number of seconds per year */

/* Alert: Globals ! (seems simpler right now then passing additional arguments) */
struct
{
	time_t alrm ;
	time_t cntdwn ;
	char *txt ;
	short unlim ; /* Running without end (=1), or exit-time (=0) */
	int off_y ;
	int off_x ;
	int max_y ;
	int max_x ;
	int min_y ;
	int min_x ;
	double width ;
	double hight ;
	signed short beat_terminal ;
} glob_d = { 0 , 0 , "" , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 } ;

struct {
	char character_second ; /* character printed for second-arm */
	char character_minute ;  /* character printed for minute-arm */
	char character_hour ; /* character printed for hour-arm */
	char character_erase_sec ; /* character that erases second-arm */
	short force_large ; /* always print clock big (1=always-big) */
	short verbose ; /* Printing time in words, 0 is sometimes, 1 is never, 2 is always, 3 is short-format */
	short reverse ; /* display count-back clock, bitmask
	                * 0 0b00 nothing
									* 1 0b01  analogue
									* 2 0b10 digital
									* 3 0b11 both 
									*/
	short second_arm_color ;  /* colors for arms second arm */
	short second_arm_color_bg ;
	short minute_arm_color ;
	short minute_arm_color_bg ;
	short hour_arm_color ;
	short hour_arm_color_bg ;
	short delete_arm_color ;
	short delete_arm_color_bg ;
} appearance = { ALT_SECOND , ALT_MINUTE , ALT_HOUR , ERASE_INVISIBLE , 1 , 0 , 0 , COLOR_SEC , COLOR_SEC_BG , COLOR_MIN , COLOR_MIN_BG , COLOR_HOUR , COLOR_HOUR_BG , COLOR_DEL , COLOR_DEL_BG } ;

short global_relative_gmt = 0 ; /* Times relative to UTC/GMT (=1), local-time (=0) */

void error_handler ( char * message , char * message2 , int value ) ;
void usage_handler ( void ) ;
void output_sec ( time_t sec , int print_mode ) ;
void output_min ( time_t min , time_t sec , int print_mode ) ;
void output_hour ( time_t hour , time_t min , int print_mode) ;
int * get_screen_yx ( double fract , int base , int len) ;
void sigwinch_handler ( int sig) ;
void set_basic_screen ( void) ;
void mvaddstr_draw ( int y , int x , int octant , int color , int type_arm , int mode) ; /* draws 1 character to the screen */
void sound_alarm ( short sec_arm_alarm_color , short sec_arm_alarm_color_bg , short sound ) ;
char * sec1970_to_string ( time_t * sec1970) ;
void output_medal ( void ) ;

int main ( int argc , char * * argv )
{
	long signed zone_offs = 0 ; /* user defined zone-offset */
	char * args_tmpp ;
	short sec_arm = 1 ; /* 1=second-arm, 0=no-second-arm */
	int was_systime_error = 0 ;
	time_t sec1970 ;
	time_t secdiff ;
	struct tm * utsp ;
	int turns , i ;
	short init = 0 ;
	int delay = 1 ;
	long int time_tmp = 0 ;
	char print_mode = PRINT_MODE_FIXED ; /* default print-character-mode */
	short print_am_pm = 0 ; /* Default for printing AM/PM */
	short sec_arm_alarm_color = COLOR_ALARM ; /* Default color for second arm during alarm */
	short sec_arm_alarm_color_bg = COLOR_ALARM_BG ; /* Default color for second arm during alarm */
	short make_sound = 1 ; /* Default for making sound when alarm yes/no 1/0 */
	char temp_string [ 31 ] ; /* Temporary space for writing misc strings */
	short do_internal = 0 ; /* bitmask for internal/debugging things */
	long signed cntdwn_offs = 0 , alrm_offs = 0 ;
	short watch_sim = 0 ; /* Print day name/number in wrist-watch style */

	if ( -1== ( sec1970 = time ( 0 ) ) )
	{
		error_handler ( "systime error" , "\n" , SYSTIME_EXIT ) ;
	}

	tzset ( ) ;/* initialize "timezone" variable */

	signal ( SIGWINCH , sigwinch_handler ) ;

	if ( 1 < argc ) 
	{
		char * temp_arg_p ;
		for ( i = 1 ; i < argc ; i ++ ) 
		{
		/* pre-Process arguments: check for arguments that cause all times
		 * to become interpreted relative to UTC/GMT rather then local time.*/
			if ( 0 == strncmp ( "-u" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--utc" , * ( argv+i )  , 5 ) || 0 == strncmp ( "--gmt" , * ( argv+i ) , 5 ) ) 
			{
				global_relative_gmt = 1 ;
			}
		}
		/* Process arguments */
		for ( i = 1 ; i < argc ; i ++ ) 
		{
			short do_usage_error = 0 ;

			if ( 0 == strncmp ( "-d" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--date" , * ( argv + i ) , 6 ) ) 
			{
				struct tm tmp ;
				glob_d . unlim = 0 ;
				if ( 0 == strncmp ( "-d" , * ( argv + i ) , 2 ) ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else 
				{
					temp_arg_p = * ( argv + i ) + 7 ;
				}
				if ( 0 == global_relative_gmt ) 
				{
					utsp = localtime ( & sec1970 ) ;
				}
				else 
				{
					utsp = gmtime ( & sec1970 )  ;
				}
				/* Read user-given time into understandable struct tm */
				if ( 0 == do_usage_error && 19 == strlen ( temp_arg_p ) ) 
				{
					if ( '/' == * ( temp_arg_p + 2 ) ) 
					{ /* MONTH/DAY/YEAR HOUR:MIN:SEC */
						tmp . tm_mon  = strtol ( temp_arg_p      , 0 , 10 ) - 1 ;
						tmp . tm_mday = strtol ( temp_arg_p +  3 , 0 , 10 ) ;
						tmp . tm_year = strtol ( temp_arg_p +  6 , 0 , 10 ) - 1900 ;
						tmp . tm_hour = strtol ( temp_arg_p + 11 , 0 , 10 ) ;
						tmp . tm_min  = strtol ( temp_arg_p + 14 , 0 , 10 ) ;
						tmp . tm_sec  = strtol ( temp_arg_p + 17 , 0 , 10 ) ;
					}
					else if ( '-' == * ( temp_arg_p + 2 ) ) 
					{ /* DAY-MONTH-YEAR HOUR:MIN:SEC */
						tmp . tm_mday = strtol ( temp_arg_p      , 0 , 10 ) ;
						tmp . tm_mon  = strtol ( temp_arg_p +  3 , 0 , 10 ) - 1 ;
						tmp . tm_year = strtol ( temp_arg_p +  6 , 0 , 10 ) - 1900 ;
						tmp . tm_hour = strtol ( temp_arg_p + 11 , 0 , 10 ) ;
						tmp . tm_min  = strtol ( temp_arg_p + 14 , 0 , 10 ) ;
						tmp . tm_sec  = strtol ( temp_arg_p + 17 , 0 , 10 ) ;
					}
					else
					{ /* YEAR.MONTH.DAY HOUR:MIN:SEC (default because most coherent format) */
						tmp . tm_year = strtol ( temp_arg_p      , 0 , 10 ) - 1900 ;
						tmp . tm_mon  = strtol ( temp_arg_p +  5 , 0 , 10 ) - 1 ;
						tmp . tm_mday = strtol ( temp_arg_p +  8 , 0 , 10 ) ;
						tmp . tm_hour = strtol ( temp_arg_p + 11 , 0 , 10 ) ;
						tmp . tm_min  = strtol ( temp_arg_p + 14 , 0 , 10 ) ;
						tmp . tm_sec  = strtol ( temp_arg_p + 17 , 0 , 10 ) ;
					}
				}
				else if ( 0 == do_usage_error && 8 == strlen ( temp_arg_p ) ) 
				{
					tmp . tm_year = ( * utsp ) . tm_year ;
					tmp . tm_mon  = ( * utsp ) . tm_mon ;
					tmp . tm_mday = ( * utsp ) . tm_mday ;
					tmp . tm_hour = strtol ( temp_arg_p + 0 , 0 , 10 ) ;
					tmp . tm_min  = strtol ( temp_arg_p + 3 , 0 , 10 ) ;
					tmp . tm_sec  = strtol ( temp_arg_p + 6 , 0 , 10 ) ;
				}
				if ( 1 == ( do_internal & 0x1 ) )
				{
					printf ( "%-10s: arg according to asctime(3) -> : %s" , PROG , asctime ( & tmp ) ) ;
				}
				if ( 1 == do_usage_error )
				{
					error_handler ( "-d | --date requires date and time in one argument  ( full year ) " , ", see -h\n" , USAGE_EXIT ) ;
				}

				if ( - 1 == ( glob_d . cntdwn = mktime ( & tmp ) ) ) 
				{ /* Where is the function which expects "UTC" ? */
					error_handler ( "mktime(3)  error" , ", could not make a time from arguments  ( -h for usage ) \n" , ERR_MKTIME_EXIT ) ;
				}
				if ( 1 == ( do_internal & 0x1 ) )
				{
					printf ( "%-10s: arg after mktime(3) call    -> : %s" , PROG , asctime ( & tmp ) ) ;
					printf ( "                   -*-\n" ) ;
				}
				/* Trying to guess what UTC time belongs to the local-time
				 * obtained from mktime(3), although mktime gives UTC time.
				 */
				if ( 1 == global_relative_gmt )
				{
					/* Hoping this will undo the time-zone, which mktime(3) has
					 * erroneously (from program standpoint) factored in, while
					 * argument was UTC time. */
					glob_d . cntdwn -= ( time_t ) timezone ;
					/* Trying to guess what mktime(3) has guessed about the
					 * DST setting of its erroneously as local-time interpreted
					 * UTC time argument */
					utsp = localtime ( & glob_d.cntdwn ) ; /* Get DST setting of exit time */
					if ( 0 < ( * utsp ) . tm_isdst )  
					{/* If DST-bug was active, remove */
						glob_d . cntdwn += ( time_t ) 3600 ;
					}
				}
			}
			else if ( 0 == strncmp ( "-a" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--alarm" , * ( argv + i ) , 7 ) )
			{
				if ( 0 == strncmp ( "-a" , * ( argv + i ) , 2 ) )
				{
					if ( i + 1 < argc )
					{
						i++;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 8 ;
				}
				if ( 1 == do_usage_error ) 
				{
					error_handler ( "-a | --alarm requires an argument M:S" , ", see -h\n" , USAGE_EXIT ) ;
				}
				alrm_offs = strtol ( temp_arg_p , & args_tmpp , 10 ) * 60 ;
				alrm_offs += strtol ( args_tmpp + 1 , 0 , 10 ) ;
				glob_d . unlim = 0 ;
			}
			else if ( 0 == strncmp ( "-t" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--text" , * ( argv + i ) , 6 ) )
			{
				if ( 0 == strncmp ( "-t" , * ( argv + i ) , 2 ) ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 7 ;
				}
				if ( 1 == do_usage_error )
				{
					error_handler ( "-t | --text requires an argument STRING",", see -h\n" , USAGE_EXIT ) ;
				}
				glob_d . txt = temp_arg_p ;
			}
			else if ( 0 == strncmp ( "-o" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--offset" , * ( argv + i ) , 8 ) )
			{
				if ( 0 == strncmp ( "-o" , * ( argv+i )  , 2 )  ) 
				{
					if ( i + 1 < argc ) 
					{
						i++;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 9 ;
				}
				if ( 1 == do_usage_error )
				{
					error_handler ( "-o | --offset requires an argument M:S" , ", see -h\n" , USAGE_EXIT ) ;
				}
				glob_d . unlim = 0 ;
				cntdwn_offs = abs ( strtol ( temp_arg_p , & args_tmpp , 10 ) * 60 ) ;
				cntdwn_offs += strtol ( args_tmpp + 1 , 0 , 10 ) ;
				if ( '-' == * temp_arg_p ) 
				{
					cntdwn_offs = - cntdwn_offs ;
				}
			}
			else if ( 0 == strncmp ( "-u" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--utc" , * ( argv + i ) , 5 ) || 0 == strncmp ( "--gmt" , * ( argv + i ) , 5 )  ) 
			{
				global_relative_gmt = 1 ; /* Times relative to UTC/GMT  ( =1 )  ,  local-time  ( =0 )  */
			}
			else if ( 0 == strncmp ( "-Z" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--zero" , * ( argv + i ) , 5 ) || 0 == strncmp ( "--gmt" , * ( argv + i ) , 5 ) ) 
			{
				/* A convenient way to get an upcounting clock. */
				zone_offs = - sec1970 ; /* Clear offset to 0 seconds-since-Epoch `now'. */
				global_relative_gmt = 1 ; /* Times relative to UTC/GMT  ( =1 )  ,  local-time  ( =0 )  */
			}
			else if ( 0 == strncmp ( "-z" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--zone" , * ( argv + i ) , 6 ) ) 
			{
				if ( 0 == strncmp ( "-z" , * ( argv+i ) , 2 ) ) {
					if ( i + 1 < argc )
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 7 ;
				}
				if ( 1 == do_usage_error )
				{
					error_handler ( "-z | --zone requires an argument H:M:S" , ", see -h\n" , USAGE_EXIT ) ;
				}
				zone_offs = abs ( strtol ( temp_arg_p , & args_tmpp , 10 )  * 3600 ) ;
				zone_offs += strtol ( args_tmpp + 1 , & args_tmpp , 10 )  * 60;
				zone_offs += strtol ( args_tmpp + 1 , 0 , 10 ) ;
				if ( '-' == * temp_arg_p ) 
				{
					zone_offs = - zone_offs ;
				}
			}
			else if ( 0 == strncmp ( "-C" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--countdown-digital" , * ( argv + i ) , 19 ) )
			{
				glob_d . unlim = 0 ;
				appearance . reverse |= 0x2 ;  /* Only digital reverse clock */
			   /* Use the space 
				  * `appearance.reverse > 0' as verbosity setting,
				  * while ==0 is the `count-down appearance of
				  * clock' boolean. Can not load it on
				  * appearance.verbose, because this setting 
				  * is independent of that (may want to mix). */
			}
			else if ( 0 == strncmp ( "-c" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--countdown" , * ( argv + i ) , 11 ) ) 
			{
				glob_d . unlim = 0 ;
				appearance . reverse |= 0x1 ; /* Only analogue reverse clock */
			}
			else if ( 0 == strncmp ( "-s" , * ( argv + i )  , 2 ) || 0 == strncmp ( "--sleep" , * ( argv + i ) , 7 ) ) 
			{
				if ( 0 == strncmp ( "-s" , * ( argv + i ) , 2 ) ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 8 ;
				}
				if  ( 1 == do_usage_error ) {
					error_handler ( "-s | --sleep requires an argument SECONDS" , ", see '-h\n'" , USAGE_EXIT ) ;
				}
				delay = ( int ) strtol ( temp_arg_p , 0 , 10 ) ;
			}
			else if ( 0 == strncmp ( "-n" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--no-sec-arm" , * ( argv + i )  , 12 )  )
			{
				sec_arm = 0 ;
			}
			else if ( 0 == strncmp ( "-q" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--quotidian" , * ( argv + i )  , 11 )  )
			{ /* "quotidian" a) daily, b) of all days acc. lex. */
				watch_sim = 1 ;
			}
			else if ( 0 == strncmp ( "-h" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--help" , * ( argv + i ) , 6 ) ) 
			{
				usage_handler ( ) ;
			}
			/* ASCII-art mode for printing arms characters */
			else if ( 0 == strncmp ( "-g" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--graphic" , * ( argv + i ) , 9 ) ) 
			{
				print_mode = PRINT_MODE_GRAPHIC ;
			}
			/* ASCII-art mode for printing arms characters */
			else if ( 0 == strncmp ( "-f" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--fixed" , * ( argv + i ) , 7 ) ) 
			{
				print_mode = PRINT_MODE_FIXED ;
			}
			/* Foot steps left by second arm */
			else if ( 0 == strncmp ( "-m" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--medal" , * ( argv + i ) , 6 ) ) 
			{
				appearance . character_erase_sec = ERASE_VISIBLE ;
				appearance . delete_arm_color = COLOR_BLUE ;
			}
			/* Version */
			else if ( 0 == strncmp ( "-v" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--version" , * ( argv + i ) , 9 ) ) 
			{
				fprintf ( stdout , "%s %s\n" , PROG , VERSION ) ;
				exit ( 0 ) ;
			}
			/* Print AM|PM */
			else if ( 0 == strncmp ( "-b" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--bell-no" , * ( argv + i ) , 9 ) ) 
			{
				make_sound = 0 ; /* do not make sound */
			}
			/* Print AM|PM */
			else if ( 0 == strncmp ( "-x" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--am-pm" , * ( argv + i ) , 7 ) ) 
			{
				print_am_pm = 1 ;
			}
			/* Set characters used for arms ... */
			else if ( 0 == strncmp ( "-p" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--print" , * ( argv + i ) , 7 ) )
			{
				if ( 0 == strncmp ( "-p" , * ( argv + i ) , 2 ) )
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 8 ;
				}
				if  ( 1 == do_usage_error ) 
				{
					error_handler ( "-p | --print requires an argument HMS" , ", see -h\n" , USAGE_EXIT ) ;
				}
				/* Below are some fun options shortcuts */
				if ( 0 == strncmp ( "empty" , temp_arg_p , 5 ) )
				{ /* shorthand for use with colored arms */
					/* Kind of usage error: trailing garbage `z' */
					appearance . character_hour = ' ' ;
					appearance . character_minute = ' ' ;
					appearance . character_second = ' ' ; 
					appearance . character_erase_sec = ' ' ;
				}
				else if ( 0 == strncmp ( "fizzz" , temp_arg_p , 5 ) )
				{ /* Wild looking, backdrop has some texture */
					/* Kind of usage error: trailing garbage `z' */
					appearance . character_hour = '@' ;
					appearance . character_minute = '^' ;
					appearance . character_second = ' ' ;
					appearance . character_erase_sec = 0x1F ;
					appearance . delete_arm_color = COLOR_GREEN ;
				}
				else if ( 0 == strncmp ( "knitted" , temp_arg_p , 7 ) )
				{ /* knitted sweater look */
					appearance . character_hour = 'X' ;
					appearance . character_minute = 'X' ;
					appearance . character_second = 'X' ; 
					appearance . character_erase_sec = 'X' ;
					appearance . second_arm_color = COLOR_YELLOW ;
					sec_arm_alarm_color = COLOR_YELLOW ;
					appearance . delete_arm_color = COLOR_RED ;
					if ( 0 == strncmp ( "knitted2" , temp_arg_p , 8 ) )
					{ /* different colors */
						appearance . second_arm_color = COLOR_CYAN ;
						sec_arm_alarm_color = COLOR_CYAN ;
						appearance . delete_arm_color = COLOR_BLUE ;
					}
				}
				else if ( 0 == strncmp ( "nomedal" , temp_arg_p , 7 ) )
				{ /* shorthand for use with colored arms */
					/* Kind of usage error: trailing garbage `z' */
					appearance . delete_arm_color = COLOR_BLACK ;
					appearance . delete_arm_color_bg = COLOR_BLACK ;
				}
				else if ( 0 == strncmp ( "normal" , temp_arg_p , 6 ) )
				{ /* Reset to defaults, in case combinations of --print
					 * cheatcodes change this in by user undesired ways.
					 */
					appearance . character_hour = 'H' ;
					appearance . character_minute = 'm' ;
					appearance . character_second = 's' ; 
					appearance . character_erase_sec = ' ' ;
				}
				else if ( 0 == strncmp ( "shade" , temp_arg_p , 5 ) )
				{ /* Another quiet appearance, easier to see hour arm */
					/* Kind of usage error: trailing garbage `z' */
					appearance . character_hour = '\\' ;
					appearance . character_minute = '/' ;
					appearance . character_second = ' ' ;
					appearance . character_erase_sec = '/' ;
					appearance . delete_arm_color = COLOR_MAGENTA ;
				}
				else if ( 0 == strncmp ( "simple" , temp_arg_p , 6 ) )
				{ /* shorthand for use with colored arms */
					/* Kind of usage error: trailing garbage `z' */
					appearance . character_hour = ' ' ;
					appearance . character_minute = ' ' ;
					appearance . character_second = ' ' ; 
					appearance . character_erase_sec = ' ' ;
					appearance . hour_arm_color = COLOR_BLUE ;
					appearance . hour_arm_color_bg = COLOR_GREEN ;
					appearance . minute_arm_color = COLOR_BLUE ;
					appearance . minute_arm_color_bg = COLOR_RED ;
					appearance . second_arm_color = COLOR_BLUE ;
					appearance . second_arm_color_bg = COLOR_MAGENTA ;
					sec_arm_alarm_color = COLOR_RED ;
					sec_arm_alarm_color_bg = COLOR_BLACK ;
					appearance . delete_arm_color = COLOR_BLACK ;
					appearance . delete_arm_color_bg = COLOR_BLUE ;
				}
				else if ( 0 == strncmp ( "terse" , temp_arg_p , 5 ) )
				{ /* Quiet clock */
					/* Kind of usage error: trailing garbage `z' */
					appearance . character_hour = '.' ;
					appearance . character_minute = '.' ;
					appearance . character_second = '.' ; 
					appearance . character_erase_sec = ' ' ;
					appearance . second_arm_color = COLOR_BLUE ;
				}
				else if ( 0 == strncmp ( "wakeup" , temp_arg_p , 6 ) )
				{ /* This suddenly wakes up a dark terminal, flashing accross
					 * a dark room. Keeps the second arm visible for orientation.
					 */
					appearance . character_hour = ' ' ;
					appearance . character_minute = ' ' ;
					appearance . character_second = ' ' ; 
					appearance . character_erase_sec = ' ' ;
					appearance . hour_arm_color = COLOR_RED ;
					appearance . hour_arm_color_bg = COLOR_BLACK ;
					appearance . minute_arm_color = COLOR_RED ;
					appearance . minute_arm_color_bg = COLOR_BLACK ;
					appearance . second_arm_color = COLOR_BLACK ;
					appearance . second_arm_color_bg = COLOR_BLACK ;
					sec_arm_alarm_color = COLOR_BLACK ;
					sec_arm_alarm_color_bg = COLOR_MAGENTA ;
					appearance . delete_arm_color = COLOR_WHITE ;
					appearance . delete_arm_color_bg = COLOR_BLACK ;
					/* Keep terminal dark by default */
					appearance . verbose = 1 ; /* never print time */
				}
				else
				{ 
					appearance . character_hour = * ( temp_arg_p + 0 ) ;
					appearance . character_minute = * ( temp_arg_p + 1 ) ;
					appearance . character_second = * ( temp_arg_p + 2 ) ;
					if ( 0 != * ( temp_arg_p + 3 ) )  
					{ /* not NULL end of string */
						appearance . character_erase_sec = * ( temp_arg_p + 3 ) ;
						/* Reverse second arm: --print="O+ @" */
						/* Same, with shade effect: --print="O+ ^X"   ( Control-X  )  */
					}
				}
			}
			/* Force large printing of clock ... */
			else if ( 0 == strncmp ( "-l",* ( argv + i ) , 2 ) || 0 == strncmp ( "--little" , * ( argv + i ) , 7 ) ) 
			{
				appearance . force_large = 0 ; /* 1 = large ,  0 = only large if no alarm */
			}
			else if ( 0 == strncmp ( "-w" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--when-short" , * ( argv + i ) , 9 ) ) 
			{
				appearance . verbose = 3 ; /* always print date/time ,  short format */
				/* Printing alarm/exit time/date is also suppressed */
			}
			else if ( 0 == strncmp ( "-W" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--when-full" , * ( argv + i ) , 10 ) ) 
			{
				appearance . verbose = 2 ; /* always print date/time */
			}
			else if ( 0 == strncmp ( "-0" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--when-no" , * ( argv + i ) , 9 ) ) 
			{
				appearance . verbose = 1 ; /* never print time */
			}
			/* Set seconds-arm color during alarm mode */
			/* Keep this above --sec-color" */
			else if ( 0 == strncmp ( "-K" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--sec-color-alarm" , * ( argv + i ) , 17 ) ) 
			{
				if ( 0 == strncmp ( "-K" , * ( argv + i ) , 2 )  ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv+i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 18 ;
				}
				/* Find color */
				if ( 0 == strncmp ( "red" , temp_arg_p , 3 ) ) 
				{
					sec_arm_alarm_color = COLOR_RED ;
				}
				else if ( 0 == strncmp ( "blue" , temp_arg_p , 4 ) ) 
				{
					sec_arm_alarm_color = COLOR_BLUE ;
				}
				else if ( 0 == strncmp ( "green" , temp_arg_p , 5 ) )
				{
					sec_arm_alarm_color = COLOR_GREEN ;
				}
				else if ( 0 == strncmp ( "yellow" , temp_arg_p , 6 ) )
				{
					sec_arm_alarm_color = COLOR_YELLOW ;
				}
				else if ( 0 == strncmp ( "white" , temp_arg_p , 5 ) ) 
				{
					sec_arm_alarm_color = COLOR_WHITE ;
				}
				else if ( 0 == strncmp ( "magenta" , temp_arg_p , 7 ) ) 
				{
					sec_arm_alarm_color = COLOR_MAGENTA;
				}
				else if ( 0 == strncmp ( "cyan" , temp_arg_p , 4 ) ) 
				{
					sec_arm_alarm_color = COLOR_CYAN ;
				}
				else if ( 0 == strncmp ( "black" , temp_arg_p , 5 ) ) 
				{
					sec_arm_alarm_color = COLOR_BLACK ;
				}
				else
				{
					do_usage_error = 1 ;
				}
				if  ( 1 == do_usage_error ) 
				{
					error_handler ( "-K | --sec-color-alarm requires an argument COLOR" , ", see -h\n" , USAGE_EXIT ) ;
				}
			}
			/* Set seconds-arm color */
			else if ( 0 == strncmp ( "-k" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--sec-color" , * ( argv + i ) , 11 ) ) 
			{
				if ( 0 == strncmp ( "-k" , * ( argv + i ) , 2 ) ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 12 ;
				}
				/* Find color */
				if ( 0 == strncmp ( "red" , temp_arg_p , 3 ) ) 
				{
					appearance . second_arm_color = COLOR_RED ;
				}
				else if ( 0 == strncmp ( "blue" , temp_arg_p , 4 ) ) 
				{
					appearance . second_arm_color = COLOR_BLUE ;
				}
				else if ( 0 == strncmp ( "green" , temp_arg_p , 5 ) ) 
				{
					appearance . second_arm_color = COLOR_GREEN ;
				}
				else if ( 0 == strncmp ( "yellow" , temp_arg_p , 6 ) ) 
				{
					appearance . second_arm_color = COLOR_YELLOW ;
				}
				else if ( 0 == strncmp ( "white" , temp_arg_p , 5 ) ) 
				{
					appearance . second_arm_color = COLOR_WHITE ;
				}
				else if ( 0 == strncmp ( "magenta" , temp_arg_p , 7 ) ) 
				{
					appearance . second_arm_color = COLOR_MAGENTA ;
				}
				else if ( 0 == strncmp ( "cyan" , temp_arg_p , 4 ) ) 
				{
					appearance . second_arm_color = COLOR_CYAN ;
				}
				else if ( 0 == strncmp ( "black" , temp_arg_p , 5 ) ) 
				{
					appearance . second_arm_color = COLOR_BLACK ;
				}
				else 
				{
					do_usage_error = 1 ;
				}
				if ( 1 == do_usage_error ) 
				{
					error_handler ( "-k | --sec-color requires an argument COLOR" , ", see -h\n" , USAGE_EXIT ) ;
				}
			}
			/* Set all colors */
			else if ( 0 == strncmp ( "-j" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--set-colors" , * ( argv + i ) , 12 ) ) 
			{
				short break_out = 0 ;
				short * arg_color = NULL ;
				int arg_step = 1 ;

				if ( 0 == strncmp ( "-j" , * ( argv + i ) , 2 ) ) 
				{
					if ( i + 1 < argc ) 
					{
						i ++ ;
					}
					else
					{
						do_usage_error = 1 ;
					}
					temp_arg_p = * ( argv + i ) ;
				}
				else
				{
					temp_arg_p = * ( argv + i ) + 13 ;
				}
				/* Find color */
				while ( 0 == break_out ) 
				{
					/* Advance through all color definitions, create pointer to
					 * the variable to change from the user argument */
					switch ( arg_step )
					{
						case 1 :
							arg_color = & appearance . hour_arm_color ;
							break ;
						case 2 :
							arg_color = & appearance . hour_arm_color_bg ;
							break ;
						case 3 :
							arg_color = & appearance . minute_arm_color ;
							break ;
						case 4 :
							arg_color = & appearance . minute_arm_color_bg ;
							break ;
						case 5 :
							arg_color = & appearance . second_arm_color ;
							break ;
						case 6 :
							arg_color = & appearance . second_arm_color_bg ;
							break ;
						case 7 :
							arg_color = & sec_arm_alarm_color ;
							break ;
						case 8 :
							arg_color = & sec_arm_alarm_color_bg ;
							break ;
						case 9 :
							arg_color = & appearance . delete_arm_color ;
							break ;
						case 10 :
							arg_color = & appearance . delete_arm_color_bg ;
							break ;
					}
					arg_step ++ ;

					if ( 0 == strncmp ( "nop" , temp_arg_p , 3 ) )
					{
						; /* do not change */
					}
					else if ( 0 == strncmp ( "red" , temp_arg_p , 3 ) ) 
					{
						* arg_color = COLOR_RED ;
					}
					else if ( 0 == strncmp ( "blue" , temp_arg_p , 4 ) ) 
					{
						* arg_color = COLOR_BLUE ;
					}
					else if ( 0 == strncmp ( "green" , temp_arg_p , 5 ) ) 
					{
						* arg_color = COLOR_GREEN ;
					}
					else if ( 0 == strncmp ( "yellow" , temp_arg_p , 6 ) ) 
					{
						* arg_color = COLOR_YELLOW ;
					}
					else if ( 0 == strncmp ( "white" , temp_arg_p , 5 ) ) 
					{
						* arg_color = COLOR_WHITE ;
					}
					else if ( 0 == strncmp ( "magenta" , temp_arg_p , 7 ) ) 
					{
						* arg_color = COLOR_MAGENTA ;
					}
					else if ( 0 == strncmp ( "cyan" , temp_arg_p , 4 ) ) 
					{
						* arg_color = COLOR_CYAN ;
					}
					else if ( 0 == strncmp ( "black" , temp_arg_p , 5 ) ) 
					{
						* arg_color = COLOR_BLACK ;
					}
					else 
					{
						do_usage_error = 1 ;
						break ;
					}
					/* Advance pointer to next argument */
					while ( ',' != * temp_arg_p )
					{
						if ( '\0' == * temp_arg_p ) 
						{
							break_out = 1 ;
							break ;
						}
						else
						{
							temp_arg_p ++ ;
						}
					}
					temp_arg_p ++ ;
				}
				if ( 1 == do_usage_error ) 
				{
					error_handler ( "-j | --set-colors requires an argument COLOR[,COLOR[,COLOR[...]]" , ", see -h\n" , USAGE_EXIT ) ;
				}
			}
			//
			else if ( 0 == strncmp ( "-e" , * ( argv + i ) , 2 ) || 0 == strncmp ( "--exit" , * ( argv + i ) , 6 ) ) 
			{
				do_internal |= 0x1 ; /* Set last bit */
			}
			else
			{
				error_handler ( * ( argv + i ) , " :unrecognized option ,  see -h\n" , USAGE_EXIT ) ;
			}
		}
	}

	/* Compute exit offset */
	if ( 0 == glob_d . unlim ) 
	{
		if ( - 1 == ( time_tmp = time ( 0 ) ) )
		{
			error_handler ( "systime error" , "\n" , SYSTIME_EXIT ) ;
		}
		if ( 0 == glob_d . cntdwn )
		{ /* Set count-down to present moment */
			glob_d . cntdwn = time_tmp + zone_offs ;
		}
		glob_d . cntdwn += cntdwn_offs ;
		glob_d . alrm = glob_d . cntdwn - alrm_offs ;
		if ( sec1970 + zone_offs > glob_d . cntdwn ) 
		{
			time_t current_time ; /* Better error message */
			current_time = sec1970 + zone_offs ;
			fprintf ( stderr , PROG ": current time:      %s" , sec1970_to_string ( & current_time ) ) ;
			error_handler ( "value in the past: " , sec1970_to_string ( & glob_d . cntdwn ) , PAST_EXIT ) ;
		}
	}

	if ( 1 == ( do_internal & 0x1 ) )
	{
		printf ( "%-10s: current time in seconds    -> : %ld\n" , PROG , ( long ) sec1970 ) ;
		printf ( "%-10s: printed as                 -> : %s" , PROG , sec1970_to_string ( & sec1970 ) ) ;
		printf ( "%-10s: time according to localtime(3): %s" , "..." , asctime ( localtime ( & sec1970 ) ) ) ;
		printf ( "%-10s: time according to gmtime(3)   : %s" , "..." , asctime ( gmtime ( & sec1970 ) ) ) ;
		printf ( "                   -*-\n" ) ;
		printf ( "%-10s: end on second             -> : %ld\n" , PROG , ( long ) glob_d . cntdwn ) ;
		printf ( "%-10s: printed as                -> : %s" , PROG , sec1970_to_string ( & glob_d . cntdwn ) ) ;
		printf ( "%-10s: end according to localtime(3): %s" , "..." , asctime ( localtime ( & glob_d . cntdwn ) ) ) ;
		printf ( "%-10s: end according to gmtime(3)   : %s" , "..." , asctime ( gmtime ( & glob_d . cntdwn ) ) ) ;
		return 0 ;
	}

	set_basic_screen ( ) ;
	do
	{
		if ( 1 == glob_d . beat_terminal ) 
		{
			sigwinch_handler ( 0 ) ;
			output_medal ( ) ;
			glob_d . beat_terminal = 0 ;
			init = 0 ;
		}
		if ( 1 < init )
		{
			sleep ( delay ) ;
		}
		else
		{
			init ++ ;
		}
		while ( - 1 == ( sec1970 = time ( 0 ) ) ) 
		{
			/* Error handling ,  but stay alive */
			mvaddstr ( glob_d . off_y - 1 , glob_d . off_x - 8 , "!GET TIME ERROR!" ) ;
			mvaddstr ( glob_d . off_y , glob_d . off_x - 12 , " ( retry every " SLEEP_SYSTIME_ERROR_STR " seconds ) " ) ;
			sleep ( SLEEP_SYSTIME_ERROR ) ;
			was_systime_error = 1 ;
		}
		if ( 1 == was_systime_error ) 
		{
			if ( 0 == glob_d . unlim && sec1970 > glob_d . cntdwn ) 
			{
				error_handler ( glob_d . txt , "\nCouldn't get systime ,  countdown elapsed.\n" , SYSTIME_EXIT ) ;
			}
			was_systime_error = 0 ;
			sigwinch_handler ( 0 ) ;
		}
		sec1970 += zone_offs ; /* User defined warp */
		if ( 0 == global_relative_gmt ) 
		{
			utsp = localtime ( & sec1970 ) ;
		}
		else
		{
			utsp = gmtime ( & sec1970 ) ; 
		}
		secdiff = glob_d . cntdwn - sec1970 ; /* Time to go */
		if ( 0 != ( appearance . reverse & 0x1 ) )
		{	/* print analogue count-down */
			turns = ( int )  ( secdiff / 43200 ) ; /* 12 * 60^2 ,  12 hour intervals */
			utsp = gmtime ( & secdiff ) ; 

			/* Print visual representation of multiple turns of 12 hour clock
			 * to count-down */
			move ( 0 , glob_d . off_x ) ;
			for ( i = 0 ; i < turns ; i ++ ) { 
				mvaddch ( glob_d . min_y , glob_d . off_x + i , '<' ) ;
			}
		}
		if ( 1 == sec_arm ) 
		{
			output_sec ( ( * utsp ) . tm_sec , print_mode ) ;
		}
		output_min ( ( * utsp ) . tm_min , ( * utsp ) . tm_sec , print_mode ) ;
		output_hour ( ( * utsp ) . tm_hour , ( * utsp ) . tm_min , print_mode ) ;
		/* Reset to "present time" ,  not `arm moving dummy time' */
		if ( 0 != ( appearance . reverse & 0x1 ) ) 
		{
			if ( 0 == global_relative_gmt ) 
			{
				utsp = localtime ( & sec1970 ) ;
			}
			else
			{
				utsp = gmtime ( & sec1970 ) ;
			}
		}
		if ( 1 == print_am_pm ) 
		{
			if (  ( * utsp ) . tm_hour < 12 ) 
			{
				mvaddstr ( glob_d . min_y + 1 , glob_d . max_x - 4 , "AM" ) ;
				mvaddstr ( glob_d . max_y - 1 , glob_d . max_x - 4 , "  " ) ;
			}
			else
			{
				mvaddstr ( glob_d . min_y + 1 , glob_d . max_x - 4 , "  " ) ;
				mvaddstr ( glob_d . max_y - 1 , glob_d . max_x - 4 , "PM" ) ;
			}
		}
		if ( 2 == appearance . verbose || 3 == appearance . verbose || ( 0 == appearance . verbose && 0 == glob_d . unlim ) ) 
		{
			/* This prints the current time in words */
			if ( 1 == glob_d . unlim || 3 == appearance . verbose ) 
			{ /* unlimited run: print at bottom. When small printing time ,  also suppress alarm time/date. */
				if ( 3 == appearance . verbose ) 
				{ /* Special format: small */
					mvaddnstr ( glob_d . max_y , glob_d . min_x , sec1970_to_string ( & sec1970 ) + 11 , 8 ) ;
				}
				else
				{ /* Normal wide format */
					mvaddnstr ( glob_d . max_y , glob_d . min_x , sec1970_to_string ( & sec1970 ) , 24 ) ;
				}
			}
			else
			{ /* Limited run: print higher ,  above alarm/exit times */
				mvaddnstr ( glob_d . max_y - 2 , glob_d . min_x , sec1970_to_string ( & sec1970 ) , 24 ) ;
			}

		}
		if ( 0 != ( appearance . reverse & 0x2 ) )
		{/* If digital countdown */
			if ( 60 * 60 * 24 * 365 < secdiff ) 
			{ // larger then one year 
				// This is a crude approximation of calender time.
				// But an exact measure of identical intervals...
				// The Earth' rotation isn't the best of time-keepers.
				// Exactly reproducing the year-difference according to calender
				// time is more difficult. The countdown is an exact representation
				// of seconds to count-down, where calender-time is an artistic
				// impression of the same.
				snprintf ( temp_string , 30 , " % 3li:%03i:%02i:%02i:%02i     "
				 , (long int ) (   secdiff                             / ( 60 * 60 * 24 * 365 ) )
				 ,     ( int ) ( ( secdiff % ( 60 * 60 * 24 * 365 ) )  / ( 60 * 60 * 24       ) )
				 ,     ( int ) ( ( secdiff % ( 60 * 60 * 24       ) )  / ( 60 * 60            ) )
				 ,     ( int ) ( ( secdiff % ( 60 * 60            ) )  /   60                   )
				 ,     ( int ) (   secdiff %   60                                               )
				 ); 
			}
			else if ( 60 * 60 * 24 < secdiff )
			{ /* larger then one day */
				/* Copy from right above, excl. years argument */
				snprintf ( temp_string , 30 , "     %03i:%02i:%02i:%02i     "
				 , ( int ) (   secdiff                            / ( 60 * 60 * 24 ) )
				 , ( int ) ( ( secdiff % ( 60 * 60 * 24      ) )  / ( 60 * 60      ) )
				 , ( int ) ( ( secdiff % ( 60 * 60           ) )  /   60             )
				 , ( int ) (   secdiff %   60                                        )
				 ); 
			}
			else
			{
				/* Copy from right above, excl. days argument */
				snprintf ( temp_string , 30 , "         %02i:%02i:%02i     "
				 , ( int ) (   secdiff                            / ( 60 * 60    ) )
				 , ( int ) ( ( secdiff % ( 60 * 60           ) )  /   60           )
				 , ( int ) (   secdiff %   60                                      )
				 ); 
			}
			mvaddnstr ( glob_d . max_y , glob_d . max_x - 30 , temp_string , 30 ) ; /* Exception: for both --utc and local time modes: call gmtime() */
			clrtoeol ( ) ; /* Remove garbage if any */
		}
		/* Print alarm/exit date/time. This is printed continuously, to
		 * prevent overwriting by clock in very small window */
		if ( 0 == glob_d . unlim && 1 != appearance . verbose && 3 != appearance . verbose ) 
		{ /* if limited run ,  and not verbose supressed */
			/* This prints the date of alarm and exit */
			mvaddnstr ( glob_d . max_y - 1 , glob_d . min_x , 
					sec1970_to_string ( & glob_d . alrm ) , 24 )  ;
			mvaddnstr ( glob_d . max_y , glob_d . min_x , 
					sec1970_to_string ( & glob_d . cntdwn ) , 24 )  ;
		}
		if ( 1 == watch_sim ) 
		{ /* Print day-of-week-name and day-of-month number, wrist-watch style */
			/* Get current date/time in string */
			snprintf ( temp_string , 30 , "%s" , sec1970_to_string ( & sec1970 ) ) ;
			/* ... and print its needed sections ... the day of the week: */
			mvaddnstr ( glob_d . off_y , glob_d . off_x + COLS / 5 , 
					temp_string , 3 )  ;
			/* Print always as a single word, looks nicer */
			if ( 9 < ( * utsp ) . tm_mday )
			{ /* 2 digits day-of-month */
				mvaddnstr ( glob_d . off_y , glob_d . off_x + COLS / 5 + 3 , 
						temp_string + 8 , 2 )  ;
			}
			else
			{ /* 1 digit day-of-month */
				mvaddnstr ( glob_d . off_y , glob_d . off_x + COLS / 5 + 3 , 
						temp_string + 9 , 1 )  ;
			}
		}
		if ( 0 == glob_d . unlim && sec1970 >= glob_d . alrm ) 
		{
			sound_alarm ( sec_arm_alarm_color , sec_arm_alarm_color_bg , make_sound ) ;
		}
		move ( glob_d . off_y , glob_d . off_x ) ;
		refresh ( ) ;
	} while ( 1 == glob_d . unlim || sec1970 < glob_d . cntdwn ) ;
	endwin ( ) ;
	return EXIT_SUCCESS ;
}

void sigwinch_handler ( int sig )
{
	clear ( ) ;
	refresh ( ) ;
	endwin ( ) ;
	set_basic_screen ( ) ;
	glob_d . beat_terminal = 1 ;
}

void set_basic_screen ( void  ) 
{
	initscr ( ) ;
	curs_set ( 0 ) ;
	start_color ( ) ;
	init_pair ( U_COLOR_HOUR , appearance . hour_arm_color , appearance . hour_arm_color_bg ) ;
	init_pair ( U_COLOR_MIN , appearance . minute_arm_color , appearance . minute_arm_color_bg ) ;
	init_pair ( U_COLOR_SEC , appearance . second_arm_color , appearance . second_arm_color_bg ) ;
	init_pair ( U_COLOR_DEL , appearance . delete_arm_color , appearance . delete_arm_color_bg ) ;

	if ( 1 == appearance . force_large ) 
	{
		glob_d . width = COLS / ( double ) 266.6 ;
		glob_d . off_x = ( int )  ( COLS / 2 ) ;
	}
	else
	{
		glob_d . width = COLS / ( double ) 400 ;
		glob_d . off_x = ( int )  ( COLS / 2 + 8 ) ;
	}
	glob_d . off_y =  ( int ) ( LINES / 2 ) ;
	glob_d . max_y = LINES - 1 ;
	glob_d . max_x = COLS - 1 ;
	glob_d . min_y = 0 ;
	glob_d . min_x = 0 ;
	glob_d . hight = LINES / ( double ) 250 ;
	/* This prints the user-defined string from the command-line */
	mvaddnstr ( glob_d . min_y , glob_d . min_x , glob_d . txt , strlen ( glob_d . txt ) ) ;
	return ;
}

void error_handler ( char * message , char * message2 , int value )
{
	endwin ( ) ;
	fprintf ( stderr , PROG ": %s%s" , message , message2 ) ;
	exit ( value ) ;
}

void usage_handler ( void )
{
	endwin ( ) ;
	fprintf ( stdout ,
"Clockalrm, usage: clockalrm [OPTION[S]]\n"
"-a ... | --alarm=MIN[.SEC]       alarm goes before termination, default 0:0\n"
"-b     | --bell-no               no beeps\n"
"-c     | --countdown             analogue countdown clock\n"
"-C     | --countdown-digital     digital countdown\n"
"-d ... | --date=DATE             program termination\n"
"   DATE: YYYY.MM.DD.HH.MM.SS     YEAR.MONTH.DAY\n"
"         DD-MM-YYYY.HH:MM:SS     DAY-MONTH-YEAR\n"
"         MM/DD/YYYY.HH:MM:SS     MONTH/DAY/YEAR\n"
"         HH:MM:SS                Assume present day\n"
"-e     | --exit                  Print time perceptions, then exit (debugging)\n"
"-f     | --fixed                 use \"Hms\" to print arms\n"
"-g     | --graphic               print arms ASCII-art style\n"
"-h     | --help                  usage\n"
"-j ... | --set-colors=COLOR[...] Define comma-list of COLORS:\n"
"   HOUR-FG,HOUR-BG,MIN-FG,MIN-BG,SEC-FG,SEC-BG,ALARM-FG,ALARM-BG,BG-FG,BG-BG\n"
"   The COLOR can be: black,blue,cyan,green,magenta,nop,red,white,yellow\n"
"-k ... | --sec-color=COLOR       second arm color to black,blue,cyan,green,\n"
"                                 magenta,red,white,yellow\n"
"-K ... | --sec-color-alarm=COLOR second arm color during alarm to black,blue,\n" 
"                                 cyan,green,magenta,red,white,yellow\n"
"-l     | --little                smaller clock\n"
"-m     | --medal                 clock background\n"
"-n     | --no-sec-arm            don't print seconds arm\n"
"-o ... | --offset=MIN[.SEC]      offset to -d, or from `now' if no -d option\n"
"-p ... | --print=123[4]          characters for --fixed mode, 4 for background\n"
"   Cheats: empty,fizzz,knitted[2],nomedal,normal,shade,simple,terse,wakeup\n"
"-q     | --quotidian             day in wrist-watch style\n"
"-s ... | --sleep=SECONDS         sleep between sampling time, default 1\n"
"-t ... | --text=TEXT             display text\n"
"-u     | --utc | --gmt           display UTC/GMT\n"
"-v     | --version               print version\n"
"-w     | --when-short            print current time in HH:MM:SS format\n"
"-W     | --when-full             always display time in words/numbers\n"
"-0     | --when-no               never display time in words/numbers\n"
"-x     | --am-pm                 display \"AM\"|\"PM\"\n"
"-z ... | --zone=HH[.MM[.SS]]     timezone\n"
"-Z     | --zero                  present is 1 Jan 1970 00:00:00\n"
		) ;
	exit ( EXIT_SUCCESS ) ;
}

void output_sec ( time_t sec , int print_mode )
{
	static int last_sec = -1 ;
	int * yx , i ;
	int octant ;

	if ( -1 == last_sec ) 
	{
		last_sec = sec ;
		return ;
	}

	for ( i = 0 ; SEC_LENTH > i ; i ++ ) 
	{
		yx = get_screen_yx ( last_sec , 60 , i ) ;
		mvaddch ( * yx , * ( yx + 1 )  , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
	}
	octant = ( int ) ( ( ( double ) sec + 3.75 ) / 7.5 ) ;
	/* Write new arm */
	for ( i = 0 ; SEC_LENTH > i ; i ++ ) 
	{
		yx = get_screen_yx ( sec , 60 , i ) ;
		mvaddstr_draw ( * yx , * ( yx + 1 ) , octant , U_COLOR_SEC , SECOND_ARM , print_mode ) ;
	}
	last_sec = sec ;
	return ;
}

void output_min ( time_t min , time_t sec , int print_mode )
{
	static double last_min = -1 ;
	double fract_min ;
	int * yx , i ;
	int octant ;

	if ( -1 == last_min )
	{
		last_min = min + sec / ( double ) 60 ;;
		return ; 
	}

	for ( i = 0 ; MIN_LENGTH > i ; i ++ ) 
	{
		yx = get_screen_yx ( last_min , 60 , i ) ;
		mvaddch ( * yx , * ( yx + 1 ) , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
	}
	fract_min = min + sec / ( double ) 60 ;
	octant = ( int ) ( ( fract_min + 3.75 ) / 7.5 ) ;
	for ( i = 0 ; MIN_LENGTH > i ; i ++ ) 
	{
		yx = get_screen_yx ( fract_min , 60 , i ) ;
		mvaddstr_draw ( * yx , * ( yx + 1 ) , octant , U_COLOR_MIN , MINUTE_ARM , print_mode ) ;
	}
	last_min = fract_min ;
	return ;
}

void output_hour ( time_t hour , time_t min , int print_mode)
{
	static double last_hour = -1 ;
	double fract_hour ;
	int * yx , i ;
	int octant ;

	if ( -1 == last_hour ) 
	{
		last_hour = hour + min / ( double ) 60 ;
		return ;
	}

	for ( i = 0 ; HOUR_LENTH > i ; i ++ )
	{
		yx = get_screen_yx ( last_hour , 12 , i ) ;
		mvaddch ( * yx , * ( yx + 1) , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
	}
	if ( 12 <= hour )
	{
		hour -= 12 ;
	}
	fract_hour = hour + min / ( double ) 60 ;
	octant = ( int ) ( ( fract_hour + .75 ) / 1.5 ) ;
	for ( i = 0 ; HOUR_LENTH > i ; i ++ )
	{
		yx = get_screen_yx ( fract_hour , 12 , i ) ;
		mvaddstr_draw ( * yx , * ( yx + 1 ) , octant , U_COLOR_HOUR , HOUR_ARM , print_mode ) ;
	}
	last_hour = fract_hour ;
	return ;
}

/* Paint background. This prints a lot of "erase arms", enough
 * to cover the whole background */
void output_medal ( void )
{
	int * yx , i , j ;
	int step ;
	
	/* Guessing how many arms to print, proportional to size of screen */
	if ( LINES < COLS ) 
	{
		step = COLS * 4 ; //trial&error
	}
	else
	{
		step = LINES * 4 ;
	}

	for ( j = 0 ; step > j ; j ++ )
	{
		/* This draws an outer circle, furthest point of the arms */
		yx = get_screen_yx ( j , step , MIN_LENGTH ) ;
		mvaddch ( * yx , * ( yx + 1 ) , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
		/* This draws from that point, a straight line to the center of the
		 * clock, insofar possible when only going up/down left/right */
		if ( LINES / 2  > * ( yx ) )
		{
			for ( i = * ( yx ) ; LINES / 2 >= i ; i ++ )
			{
				mvaddch ( i , * ( yx + 1 ) , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
			}
		}
		else
		{
			for ( i = * ( yx ) ; LINES / 2 <= i ; i -- )
			{
				mvaddch ( i , * ( yx + 1 ) , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
			}
		}
		if ( COLS / 2  > * ( yx + 1 ) )
		{
			for ( i = * ( yx + 1 ) ; COLS / 2 >= i ; i ++ )
			{
				mvaddch ( * ( yx ) , i , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
			}
		}
		else
		{
			for ( i = * ( yx + 1 ) ; COLS / 2 <= i ; i -- )
			{
				mvaddch ( * ( yx ) , i , REVERSE_VIDEO | COLOR_PAIR ( U_COLOR_DEL ) | appearance . character_erase_sec ) ;
			}
		}
	}
	return ;
}

/* Give a x-y coordinate, given radial input coordinates */
int * get_screen_yx ( double fract , int base , int len )
{
	static signed int screen_yx [ 2 ] = { 0 } ;
	double tmp ;

	tmp = fract / ( double ) base * 6.2831853 ;
	* screen_yx = ( int) ( -cos ( tmp ) *  len * glob_d . hight + glob_d . off_y ) ;
	* ( screen_yx + 1 ) = ( int ) ( sin ( tmp ) * len * glob_d . width + glob_d . off_x ) ;
	if ( glob_d . max_y < * screen_yx )
	{
		* screen_yx = glob_d . max_y ;
	}
	if ( glob_d . max_x < * ( screen_yx + 1 ) )
	{
		* ( screen_yx + 1 ) = glob_d . max_x ;
	}
	if ( glob_d . min_y > * screen_yx ) 
	{
		* screen_yx = glob_d . min_y ;
	}
	if ( glob_d . min_x > * ( screen_yx + 1 ) )
	{
		* ( screen_yx + 1 ) = glob_d . min_x ;
	}
	/*
	 * This cleared a block for date/time. Because it can produce an ugly
	 * edge, static exit/alarm date/times are now continuously printed.
	 * 
	 *//* //DELETE
	if ( 0 == glob_d . unlim && * ( screen_yx + 1 ) <= glob_d . min_x + 23 && * screen_yx >= glob_d . max_y - 2 ) 
	{
		* ( screen_yx + 1 ) = glob_d . min_x + 23 ;
		* screen_yx = glob_d . min_y - 2 ;
	}
	*/
	return screen_yx ;
}

void mvaddstr_draw ( int y , int x , int octant , int color , int type_arm , int mode )
{
	char print_char ;
	/* Select character to print differently for every part of the circle */
	if ( PRINT_MODE_GRAPHIC == mode )
	{
		switch ( octant )
		{
			/* For every part of the circle ,  another character */
			case 0 :
				print_char = '|' ;
				break ;
			case 1 :
				print_char = '/' ;
				break ;
			case 2 :
				print_char = '=' ;
				break ;
			case 3 : 
				print_char = '\\' ;
				break ;
			case 4 : 
				print_char = '|' ;
				break ;
			case 5 : 
				print_char = '/' ;
				break ;
			case 6 : 
				print_char = '=' ;
				break ;
			case 7 : 
				print_char = '\\' ;
				break ;
			case 8 : 
				print_char = '|' ;
				break ;
			default : 
				print_char = appearance . character_erase_sec ;
				break ;
		}
	}
	else
	{ /* if  (  PRINT_MODE_FIXED == mode ) { */
		/* Select a character to print depending on the arm */
		switch ( type_arm )
		{
			case SECOND_ARM :
				print_char = appearance . character_second ;
				break ;
			case MINUTE_ARM :
				print_char = appearance . character_minute ;
				break ;
			case HOUR_ARM :
				print_char = appearance . character_hour ;
				break ;
			default :
				print_char = appearance . character_erase_sec ;
		}
	}
	mvaddch ( y , x , REVERSE_VIDEO | COLOR_PAIR ( color ) | ( char ) print_char ) ;
	return ;
}

/* Sound the alarm ,  change color second arm */
void sound_alarm ( short sec_arm_alarm_color , short sec_arm_alarm_color_bg , short sound ) 
{//FIXME: these two color args should be put in the global struct...
	static char here_bool = 0 ;
	short swap_color ;
	if ( 0 == here_bool ) 
	{
		appearance . second_arm_color = sec_arm_alarm_color ;
		appearance . second_arm_color_bg = sec_arm_alarm_color_bg ;
		set_basic_screen ( ) ; /* Need to re-initialize for color change.*/
	}
	else
	{
		here_bool = 1 ;
	}
	/* This causes the background to blink: reversing its fore/back-ground
	 * colors every pass */
	swap_color = appearance . delete_arm_color ;
	appearance . delete_arm_color = appearance . delete_arm_color_bg ;
	appearance . delete_arm_color_bg = swap_color ;
	if ( 1 == sound ) 
	{
		beep (  ) ;
	}
}

char * sec1970_to_string ( time_t * sec1970 )
{
	static struct tm time_insulated ; /* Use other memory, repeated calls
                            * otherwise make things even more difficult */
	if ( 0 == global_relative_gmt ) 
	{
		return asctime ( localtime_r ( sec1970 , & time_insulated ) ) ;
	}
	else 
	{
		return asctime ( gmtime_r ( sec1970 , & time_insulated ) ) ;
	}
}

