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; }
        }

    } 
}
