/** Dig-it-Al
 @version 0.4f
 Copyright (c) 1997,
 @author Bruno Andrighetto
 All Rights Reserved

This applet displays the current date and time.
The time is given in digital format.

*/

// Dig-it-Al clock for Java
// 08-11-97
// Version 0.4f
//		- fixed bug re change to single-digit hour (ie 23->0, 12->1)
//		- minor bug fixes/tweaks
// Version 0.4e
//		- uses hashtable to lookup colours
//		- config params for: pauseable, refreshPeriod, test date/time
//		- 'paused' message should be visible
// Version 0.4d
//		- uses LCDDigit 0.1c, so don't need to individually erase previously displayed digit elements
// Version 0.4c
//		- uses checkbox for 12/24 toggle
//		- minor tweaks to display
//		- moved initialisation of variables to the init() method
//		- configurable via PARAM tag: colour, format, interactive, showdate, time pos, date pos
// Version 0.4b
//		- minor tweaks to improve appearance
// Version 0.4a
//		- colour option
// Version 0.4
//		- 12/24 hour toggle
// Version 0.3b,c
//		- some tweaks
// Version 0.3a
//		- uses digit class
//		- displays 'digital' time
// Version 0.2b
//		- includes test data and code for Y2K
//		- only updates screen where necessary *
// Version 0.2a
//		- uses refreshment thread
// Version 0.1a
//		- gets system date/time object
//		- displays Date separately, as: 'dayname, monthname date, fullyear'
//		- displays Time separately, as: 'HH : MM : SS'

import java.awt.*;
import java.applet.Applet;
import java.util.Date;
import java.util.Hashtable;

public class DigitalClock extends Applet implements Runnable
{
	Date system;
	int year, month, date, day;
	int prevDate;
	int hours, minutes, seconds;
	int prevHours, prevMinutes, prevSeconds;
	int testyear, testmonth, testdate, testday;
	int testhours, testminutes, testseconds;

	String monthTable[] = {"January", "February", "March", "April", "May", "June",
							"July", "August", "September", "October", "November", "December"};
	String monthShortTable[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
								"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
	String dayTable[] = {"Sun", "Mon", "Tues", "Wednes", "Thurs", "Fri", "Satur"};


	Hashtable colourTable;		// a bit faster

	private int sleepTime;		// milliseconds to sleep
	private Thread refresh;		// thread to update the time
	private boolean suspended;	// toggle on/off

	LCDDigit digitsTable[];
	int numDigits;

	int posX, posY;			// top left coords of time display
	int size;				// only works if size == 1
	Color color;
	Color bgcolor;
	boolean showDate;
	int datePosX, datePosY;	// top left coords of date display
	String datePrefixStr;
	boolean showFrame;
	int framePosX, framePosY;	// top left coords of display frame

	boolean isInteractive, pauseable;
	Choice chColour;
	Checkbox ch1224;
	Label colourLabel, twelve24Label;

	boolean twelveHourFormat;
	boolean fixAMPM;
	boolean isAM;

	Graphics graphicsOb;

	boolean testing;


	public void init()
	{
		prevDate = date;
		sleepTime = 500;
		numDigits = 10;
		posX = 100;
		posY = 80;
		size = 1;
		color = Color.blue;
		bgcolor = Color.white;
		showDate = true;
		datePosX = 75;
		datePosY = 60;
		datePrefixStr = "";
		showFrame = true;
		framePosX = 15;
		framePosY = 15;
		twelveHourFormat = true;
		fixAMPM = false;
		isInteractive = true;
		pauseable = true;

		testing = false;

		initColourTable();
		processHTMLParams();
		if (isInteractive)
			framePosY += 21;

		setBackground(bgcolor);
		graphicsOb = this.getGraphics();
		graphicsOb.setColor(color);
		LCDDigit.setupElementsTable();
		setupDigitsTable();

		if (isInteractive) {
			// create top panel:
			Panel Panel1 = new Panel();

			// use border layout
			setLayout(new BorderLayout());
			add("North", Panel1);

			// labels
			colourLabel = new Label("Colour:");
			twelve24Label = new Label("Format:");

			// add some choice options:
			chColour = new Choice();
			chColour.addItem("Blue");
			chColour.addItem("Red");
			chColour.addItem("Green");
			chColour.addItem("Orange");
			chColour.addItem("Yellow");
			chColour.addItem("Dark Gray");
			chColour.addItem("Black");

			ch1224 = new Checkbox("AM/PM Format", null, true);

			Panel1.add(colourLabel);
			Panel1.add(chColour);
			Panel1.add(ch1224);
		}
	}

	// start the applet
	public void start()
	{
		date = 0;
		seconds = -1;
		minutes = -1;
		hours = -1;
		if (!testing)
			// get the date and time
			getDateAndTime();
		else // for testing purposes:
		{
			year = testyear;
			month = testmonth;
			date = testdate;
			day = testday;
			hours = testhours;
			minutes = testminutes;
			seconds = testseconds;
		}

/*
		prevDate = date - 1;
		prevHours = (hours + 23) % 24;
		prevMinutes = (minutes + 59) % 60;
		prevSeconds = (seconds + 59) % 60;
*/
		// create a new thread to 'refresh' display when user visits page
		if (refresh == null)
		{
			refresh = new Thread( this );
			refresh.start();
		}
	}

	// terminate time refreshment thread when user leaves page
	public void stop()
	{
		if (refresh != null)
		{
			refresh.stop();
			refresh = null;
		}
	}

	// display the date and time in the applet's Graphics context
	public void paint( Graphics g )
	{
		g.setColor(color);
		if (showFrame)
			g.drawRoundRect(framePosX, framePosY,
							size().width-(2*framePosX), size().height-(2*framePosY), 10, 10);

		getDateAndTime();
		// Ssytem.out.println( "Full system date and time: " + system.toString() );

		if (showDate) {
			// only refresh date display if date has changed
			if (date != prevDate)
			{
				g.clearRect(datePosX, datePosY-10, size().width-datePosX, 20);
				prevDate = date;
			}
			displayDate( g, datePosX, datePosY );
		}

		if (hours != prevHours)
		{
			if ((prevHours == 11) || (prevHours == 23))
				fixAMPM = true;
			prevHours = hours;
		}

		// the 'digital' time
		displayDigitalTime( g, posX, posY );
	}

	// override update to eliminate flicker
	public void update( Graphics g )
	{
		paint( g );
	}

	// get html parameters
	public void processHTMLParams()
	{
		String parameter;

		parameter = getParameter("timex");
		posX = (parameter == null ? 100 : Integer.parseInt(parameter));
		parameter = getParameter("timey");
		posY = (parameter == null ? 80 : Integer.parseInt(parameter));
		parameter = getParameter("colour");
		color = (parameter == null ? Color.blue : parseColour(parameter));
		parameter = getParameter("format");  // ie AM/PM (12) or 24 hour -- for backward compat.
		twelveHourFormat = (parameter == null ? true : !(parameter.equals("24")));
		parameter = getParameter("ampmformat");  // AM/PM format?
		twelveHourFormat = (parameter == null ? twelveHourFormat : !(parameter.equals("false")));
		parameter = getParameter("interactive");
		isInteractive = (parameter == null ? true : !(parameter.equals("false")));
		parameter = getParameter("showdate");
		showDate = (parameter == null ? true : !(parameter.equals("false")));
		parameter = getParameter("datex");
		datePosX = (parameter == null ? 75 : Integer.parseInt(parameter));
		parameter = getParameter("datey");
		datePosY = (parameter == null ? 60 : Integer.parseInt(parameter));
		parameter = getParameter("dateprefix");
		datePrefixStr = (parameter == null ? "" : parameter);
		parameter = getParameter("showframe");
		showFrame = (parameter == null ? true : !(parameter.equals("false")));
		parameter = getParameter("framex");
		framePosX = (parameter == null ? 15 : Integer.parseInt(parameter));
		parameter = getParameter("framey");
		framePosY = (parameter == null ? 15 : Integer.parseInt(parameter));
		parameter = getParameter("testing");
		testing = (parameter == null ? false : parameter.equals("true"));
		parameter = getParameter("testyr");
		testyear = (parameter == null ? 99 : Integer.parseInt(parameter));
		parameter = getParameter("testmon");
		testmonth = (parameter == null ? 11 : Integer.parseInt(parameter));
		parameter = getParameter("testdate");
		testdate = (parameter == null ? 31 : Integer.parseInt(parameter));
		parameter = getParameter("testday");
		testday = (parameter == null ? 5 : Integer.parseInt(parameter));
		parameter = getParameter("testhr");
		testhours = (parameter == null ? 23 : Integer.parseInt(parameter));
		parameter = getParameter("testmin");
		testminutes = (parameter == null ? 59 : Integer.parseInt(parameter));
		parameter = getParameter("testsec");
		testseconds = (parameter == null ? 30 : Integer.parseInt(parameter));
		parameter = getParameter("refreshperiod"); // update every ... milliseconds
		sleepTime = (parameter == null ? 500 : Integer.parseInt(parameter));
		parameter = getParameter("pauseable");
		pauseable = (parameter == null ? true : !(parameter.equals("false")));
	}

	private void initColourTable() {
		try {
			colourTable = new Hashtable();
			colourTable.put("blue", Color.blue);
			colourTable.put("red", Color.red);
			colourTable.put("green", Color.green);
			colourTable.put("orange", Color.orange);
			colourTable.put("yellow", Color.yellow);
			colourTable.put("dark gray", Color.darkGray);
			colourTable.put("black", Color.black);
		} catch (NullPointerException e) {
			System.out.println(" !! Exception caught in initColourTable !!"); // jic
			System.out.println(e.getMessage());
		}
	}

	private Color parseColour(String colourString)
	{
		try {
			Color colourValue = (Color) colourTable.get(colourString.toLowerCase());
			if (colourValue != null)
				return colourValue;
			else
				return Color.blue;
		} catch (NullPointerException e) {
			System.out.println(" !! Exception caught in parseColour !!"); // jic
			System.out.println(e.getMessage());
			return Color.blue;
		}
	}

	public void setupDigitsTable()
	{
		digitsTable = new LCDDigit[numDigits];
		for (int i=0; i<numDigits; i++)
			digitsTable[i] = new LCDDigit(i);
	}

	synchronized public void getDateAndTime()
	{
		prevDate = date;
		prevSeconds = seconds;
		prevMinutes = minutes;
		prevHours = hours;
		if (!testing)
		{
			system = new Date();
			year = system.getYear();	// note: from 1900, so 100 => year 2000
			month = system.getMonth();	// 0 .. 11, note: 0 => January!
			date = system.getDate();	// 1 .. 31 
			day = system.getDay();		// 0 .. 6, note: 0 => Sunday
			hours = system.getHours();		// 0 .. 23
			minutes = system.getMinutes();	// 0 .. 59
			seconds = system.getSeconds();	// 0 .. 59
		}
		else // for testing purposes:
		{
			seconds = (seconds + 1) % 60;
			if (seconds == 0) {
				minutes = (minutes + 1) % 60;
				if (minutes == 0) {
					hours = (hours + 1) % 24;
					if (hours == 0) {
						date = ((date + 1) % 32);
						day = (day + 1) % 7;
						if (date == 0) {
							date = 1;
							month = (month + 1) % 12;
							if (month == 0)
								year++;
						}
					}
				}
			}
		}
	}

	synchronized public void displayDate( Graphics g , int x, int y )
	{
		g.drawString( datePrefixStr + dayTable[day] + "day, " + monthTable[month] + " " +
						Integer.toString(date) + ", " + Integer.toString(year+1900), x, y );
	}

	synchronized public void displayDigitalTime( Graphics g , int x, int y )
	{
		int savedHours = hours;

		if (twelveHourFormat)
		{
			if (hours < 12)	// am
			{
				isAM = true;
				if (hours == 0)
					hours = 12;
			}
			else			// pm
			{
				isAM = false;
				if (hours != 12)
					hours = hours - 12;
			}
		}

		// draw the ':'s
		g.setColor(color);
		g.fillOval(x+38, y+5, 2, 2);
		g.fillOval(x+38, y+15, 2, 2);
		g.fillOval(x+86, y+5, 2, 2);
		g.fillOval(x+86, y+15, 2, 2);

		// display the time
		if (hours >= 10)
			digitsTable[hours / 10].draw(g, x, y, size, color, bgcolor);
		else
			digitsTable[hours / 10].draw(g, x, y, size, bgcolor, bgcolor);
		digitsTable[hours % 10].draw(g, x+18, y, size, color, bgcolor);
		digitsTable[minutes / 10].draw(g, x+48, y, size, color, bgcolor);
		digitsTable[minutes % 10].draw(g, x+66, y, size, color, bgcolor);
		digitsTable[seconds / 10].draw(g, x+96, y, size, color, bgcolor);
		digitsTable[seconds % 10].draw(g, x+114, y, size, color, bgcolor);
		g.setColor(color);
		if (twelveHourFormat)
		{
			if (fixAMPM)
			{
				g.clearRect(x+140, y+10, 30, 20);
				fixAMPM = false;
			}
			if (isAM)
				g.drawString( "AM", x+140, y+20 );
			else
				g.drawString( "PM", x+140, y+20 );
		}
		hours = savedHours;
	}

	public void run()
	{
		while ( true )
		{
			// browser fix
			postEvent( new Event( this, Event.MOUSE_ENTER, "" ) );

			try {
				Thread.sleep( sleepTime );
			}
			catch ( InterruptedException e ) {
				showStatus( e.toString() );
			}

			repaint();
		}
	}

	public boolean mouseDown( Event e, int x, int y )
	{
		if (suspended)
		{
			refresh.resume();
			suspended = false;
			graphicsOb.clearRect(size().width/2-40, size().height/2-10, 80, 20);
		}
		else
		{
			if (pauseable) {
				refresh.suspend();
				suspended = true;
				graphicsOb.setColor(Color.red);
				graphicsOb.drawString( "** Paused **", size().width/2-40, size().height/2);
				graphicsOb.setColor(color);
			}
		}
		return true;
	}

	public boolean action(Event evt, Object arg)
	{
		String label = (String) arg;
		boolean savedTwelveHourFormat = twelveHourFormat;
		Color savedColor = color;

		// check colour
		color = parseColour(chColour.getSelectedItem());

		// check 12/24 hour toggle
		twelveHourFormat = ch1224.getState();

		// check if need to redisplay clock etc
		if ((twelveHourFormat != savedTwelveHourFormat) || (color != savedColor))
		{
			graphicsOb.clearRect(0, 0, size().width, size().height);

			if (showDate)
				// the date
				displayDate( graphicsOb, datePosX, datePosY );
		
			// the 'digital' time
			displayDigitalTime( graphicsOb, posX, posY );
		}

		return true;
	}
}
