import java.util.*;

/**
  * TimeCalc populates a TimeGrid object, passes it back to the observer,
  * and then sleeps for the specified time before repeating the process.
  */
public class TimeCalc implements Runnable {
	private Thread	calcThread;		// thread for this object
	private long 	startTime; 		// ms offset from 1/1/1970
	private long 	delay;			// delay between recalcs in ms
	private DialClockFrame 	callbackObj; // Object who's update() method should
									     // be called when new data is avail

	/**
	  * Contructor that takes a starting time in milliseconds offset from
	  * Jan 1, 1970, a delay in millis to wait before recalculating and 
	  * a DialClockFrame object to notify when new data is available.  A
	  * thread is started in this object.
	  */
	public TimeCalc(long startTime, long delay, DialClockFrame callbackObj) {
		this.startTime = startTime;
		this.delay = delay;
		this.callbackObj = callbackObj;

		// start a thread of execution for this object (use getThread()) to
		// stop it
		calcThread = new Thread(this);
		calcThread.start();
	}

	/** 
	  * Used to get the thread in TimeCalc object (usually to stop() it).
	  */
	public Thread getThread() {
		return calcThread;
	}

	/**
	  *  Main loop of thread running for this object, called indirectly
	  *  by the constructors call to start().  Should not be called directly.
	  */
	public void run() {
		long initTime   = System.currentTimeMillis(); // note when first run
		long offsetTime, nowWithMilli, nowNoMilli;
		Date lastMinD, lastHrD, lastDayD;
		TimeGrid tg;

		while ( true ) {
			// We don't have to show the current system time, but we are
			// running from a starting time specified in the constructor
			// requiring the need to calculate the current time offset since
			// this was first run.

			// calc how many millis have elapsed since run was first called
			offsetTime = System.currentTimeMillis() - initTime;
			nowWithMilli = startTime + offsetTime;

			// truncate all the milliseconds
			nowNoMilli= (long)Math.floor(nowWithMilli / 1000.0) * 1000;

			lastMinD 	= new Date(nowNoMilli);
			lastHrD 	= new Date(nowNoMilli);
			lastDayD 	= new Date(nowNoMilli);

			// find the start of this minute
			lastMinD.setSeconds(0);  

			// find the start of this hour
			lastHrD.setSeconds(0);
			lastHrD.setMinutes(0);

			// find the start of this day
			lastDayD.setSeconds(0);
			lastDayD.setMinutes(0);
			lastDayD.setHours(0);

			tg			= new TimeGrid();

			// calc seconds and minutes in this minute
			tg.cSecInMin = ( nowWithMilli - lastMinD.getTime() ) / 1000.0;
			tg.tSecInMin = 60.0;
			tg.cMinInMin = tg.cSecInMin / tg.tSecInMin;
			tg.tMinInMin = 1.0;

			// calc seconds, minutes, and hours in this hour
			tg.cSecInHr = ( nowWithMilli - lastHrD.getTime() ) / 1000.0;
			tg.tSecInHr = 60.0 * tg.tSecInMin;
			tg.cMinInHr = tg.cSecInHr / tg.tSecInMin;
			tg.tMinInHr = tg.tSecInHr / tg.tSecInMin;
			tg.cHrInHr 	= tg.cSecInHr / tg.tSecInHr;
			tg.tHrInHr 	= 1.0;

			// calc seconds, minutes, hours, and days in this day
			tg.cSecInDay = ( nowWithMilli - lastDayD.getTime() ) / 1000.0;
			tg.tSecInDay = 24.0 * tg.tSecInHr;
			tg.cMinInDay = tg.cSecInDay / tg.tSecInMin;
			tg.tMinInDay = tg.tSecInDay / tg.tSecInMin;
			tg.cHrInDay	 = tg.cSecInDay / tg.tSecInHr;
			tg.tHrInDay	 = tg.tSecInDay / tg.tSecInHr;
			tg.cDayInDay = tg.cSecInDay / tg.tSecInDay;
			tg.tDayInDay = 1.0;

			// pass back this TimeGrid object to the DialClockFrame that
			// wanted to know when we had new info
			callbackObj.update(tg);

			// take a nap for the specified delay --don't want to 
			// suck up all the CPU
			try { Thread.sleep(delay); } 
			catch (InterruptedException ignored) { break; }
		}

	} 
}
