package simulator;

/*
 * Time.java
 * Created on Jan 18, 2005
 *
 */

import java.util.Calendar;
import java.util.Formatter;
import java.util.GregorianCalendar;


/**
 *  Time - This class defines time in the amusement park simulation.
 *  For this current implementation, only hours and minutes are considered
 *  and date is ignored.
 *  @author Ping
 *
 */
public class Time implements ClockedThing, Cloneable, Comparable<Time>
{
    private Calendar time;
    
    /**
     * Creates a Time instance based on the hour and minute given
     * 
     * @param hour - Represents the hour.  Note this should be according to
     * 24 hour clock (or military time).
     * @param minute - Represents the minute within the hour specified.
     */
    public Time(int hour, int minutes)
    {
        // we don't care about the date for now.
        time = new GregorianCalendar(0, 0, 0, hour, minutes);
    }
    
    /**
     * This function sets the clock to the time specified by hour and minute
     * @param hour - Represents the hour.  Note this should be according to
     * 24 hour clock (or military time).
     * @param minute - Represents the minute within the hour specified.
     */
    public void setTime(int hour, int minute)
    {
        time.set(Calendar.HOUR_OF_DAY, hour);
        time.set(Calendar.MINUTE, minute);
    }
    
    /**
     * This function returns the time as a Calendar
     * @return Calendar representing the time
     */
    public Calendar getTime()
    {
        return time;
    }
    
    
    /**
     * This function adds the value specified to the hours.  It will
     * handle roll over appropriately 
     * @param value - The number of hours to add
     */
    public void addHour(int value)
    {
        time.add(Calendar.HOUR_OF_DAY, value);
    }
    
    /**
     * This function adds the value specified to the minutes.  It will
     * handle roll over appropriately (by incrementing the hour as appropriate)
     * @param value - The number of minutes to add
     */
    public void addMinute(int value)
    {
        time.add(Calendar.MINUTE, value);
    }
    
    /**
     * This function defines how the Time should be represented as a
     * string
     * @return
     */
    public String toString()
    {
        Formatter f = new Formatter();
        
        // See the Formatter class' Java doc for exact definition of 
        // each flag.  But basically its saying format the string such that
        // we want the hour from the first argument, the minute from the first
        // argument, and them the AM or PM flag from the first argument.
        
        Time onePM = new Time(13, 0);
        Time oneAM = new Time(1, 0);
        // The reason we use two different format is due to a "feature"
        // of the Calendar's time representation.  If we use simply hours 
        // (1-12), then 12 noon will be represented as 0:00 PM.  
        // But if we use hours of the day (24 hours) then 12 noon will
        // be correctly represented as 12:00 PM.  So anytime before 1 PM
        // we use 24 hour clock, and then switch to a simple 12 hour
        // clock for any time between 1 PM and 1 AM.
        if(before(onePM) && after(oneAM))
        {
            f.format("%1$tk:%1$TM %1$Tp", time);
        }
        else
        {
            f.format("%1$Tl:%1$TM %1$Tp", time);
        }
        return f.toString();
    }
    
    /**
     * This function returns true if the two Time objects represent the
     * same moment in time (hours and minutes)
     * @param t The other time object to compare with
     * @return true if both Time are at the same hour and minute
     */
    public boolean equals(Time t)
    {
        return 
            time.get(Calendar.HOUR_OF_DAY) == t.time.get(Calendar.HOUR_OF_DAY) &&
            time.get(Calendar.MINUTE) == t.time.get(Calendar.MINUTE);
    }
    
    /**
     * This function returns true if the time is before some other time t.
     * @param t  The other time to compare it with
     * @return This function returns true if this time instance is before 
     * time t.  False otherwise.
     */
    public boolean before(Time t)
    {
        if(time.get(Calendar.HOUR_OF_DAY) > t.time.get(Calendar.HOUR_OF_DAY))
        {
            // the hour is already after the time passed in
            return false;
        }
        else if(time.get(Calendar.HOUR_OF_DAY) < t.time.get(Calendar.HOUR_OF_DAY))
        {
            // the hour has not passed
            return true;
        }
        else
        {
            // the hour is equal
            return time.get(Calendar.MINUTE) < t.time.get(Calendar.MINUTE);
        }        
    }
    
    /**
     * This function returns true if the time is after some other time t.
     * @param t  The other time to compare it with
     * @return This function returns true if this time instance is after 
     * time t.  False otherwise.
     */
    public boolean after(Time t)
    {
        if(time.get(Calendar.HOUR_OF_DAY) > t.time.get(Calendar.HOUR_OF_DAY))
        {
            // the hour is already after the time passed in
            return true;
        }
        else if(time.get(Calendar.HOUR_OF_DAY) < t.time.get(Calendar.HOUR_OF_DAY))
        {
            // the hour has not passed
            return false;
        }
        else
        {
            // the hour is equal
            return time.get(Calendar.MINUTE) > t.time.get(Calendar.MINUTE);
        }        
    }
    
    /**
     * This function is called to signify that 1 clock tick has passed
     */
    public boolean onClockTick()
    {
        time.add(Calendar.MINUTE, 1);
        return true;
    }
    
    public Object clone() throws CloneNotSupportedException
    {
        Time clone = (Time) super.clone();
        clone.time = (Calendar) this.time.clone();
        return clone;
    }
    
    /**
     * This compares two different time objects.  It returns
     * negative if this instance is less than the one passed in, 0
     * if they are equal, and positive if this instance is greater
     * @see java.lang.Comparable#compareTo(java.lang.Object)
     */
    public int compareTo(Time arg0)
    {
        if(this.equals(arg0))
        {
            return 0;
        }
        else if(this.before(arg0))
        {
            return -1;
        }
        else
        {
            return 1;
        }
    }
    
    public static void main(String args[]) throws CloneNotSupportedException
    {
        /*
        Time time1 = new Time(12, 00);
        Time time2 = (Time)time1.clone();
        Time time3 = new Time(24, 15);
        
        time2.addHour(13);
        time2.addMinute(35);
        
        System.out.println(time1.toString());
        System.out.println(time2.toString());
        
        System.out.println(time3.toString());
        System.out.println(time1.compareTo(time3));
        System.out.println(time3.compareTo(time1));
        System.out.println(time3.compareTo(time3));
        */
    }   
}
