package org.treesched.schedules;

import java.io.Serializable;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;

import org.treesched.ConstrainedSchedule;
import org.treesched.DateUtils;

/**
 * Implement a schedule based on days of the year. This will most frequently be
 * used to define anual holidays. 
 * <p>
 * Typical configuration would be:
 * <code><pre>
 * &lt;dayofyear from="25-dec" to="26-dec"/&gt;
 * &lt;dayofyear on="01-jan"/&gt;
 * </code></pre>
 * 
 * @author Rob Gordon
 */


final public class DayOfYearSchedule extends ConstrainedSchedule implements Serializable {

    private static final long serialVersionUID = 20050226;
    
	public static final String DEFAULT_DATE_FORMAT = "dd-MMM";
	private String format = DEFAULT_DATE_FORMAT;

	/** The from day of year. */
	private Integer fromDay;
	
	/** The from day int as string. */
	private String stringFrom;
	
	/** The to day of year. */
	private Integer toDay;
	
	/** The to day int as a string. */
	private String stringTo;

	/**
	 * Set the date format to use for translating string attributes.
	 * 
	 * @param dateFormat The date format.
	 */		
	public void setFormat(String dateFormat) {	
		this.format = dateFormat;
	}

	/**
	 * Get the date format used for translating string attributes.
	 * 
	 * @return The date format being used.
	 */
	public String getFormat() {		
		return this.format;	
	}

	/**
	 * Calendar for the day of year.
	 * 
	 * @param wholeDate The date to take year from.
	 * @param dayOfYear The day of the year.
	 * @return The calendar.
	 */
	private Calendar calendarFor(Date wholeDate, int dayOfYear) {
		GregorianCalendar calendar1 = new GregorianCalendar();
		calendar1.setTime(wholeDate);
	    // copy calendar to remove any hours.
	    Calendar calendar2 = new GregorianCalendar();
	    calendar2.clear();
	    calendar2.set(Calendar.YEAR, calendar1.get(Calendar.YEAR));
		calendar2.set(Calendar.DAY_OF_YEAR, dayOfYear);
		return calendar2;
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#setFrom(java.lang.String)
	 */
	public void setFrom(String from) throws ParseException {
		stringFrom = from;
	    if (from == null) {
	        fromDay = null;
	    }
	    else {
	        SimpleDateFormat format = new SimpleDateFormat(getFormat());
	        fromDay = new Integer(DateUtils.dayOfYear(format.parse(from)));
	    }
	}

	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getFrom()
	 */
	public String getFrom() {
	    return stringFrom;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#setTo(java.lang.String)
	 */
	public void setTo(String to) throws ParseException {
		stringTo = to;
		if (to == null) {
		    toDay = null;
		}
		else {
		    SimpleDateFormat format = new SimpleDateFormat(getFormat());
		    toDay = new Integer(DateUtils.dayOfYear(format.parse(to)));
		}
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getTo()
	 */
	public String getTo() {
	    return stringTo;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getStartDate(java.util.Date)
	 */
	public Date getStartDate(Date now) {
	    if (fromDay == null) {
	        return new Date(0);
	    }
	    int iFromDay = fromDay.intValue();
	    Calendar fromCal = calendarFor(now, iFromDay);
	    if (toDay== null) {
	        return fromCal.getTime();
	    }
	    int iToDay = toDay.intValue();
	    Calendar toCal = calendarFor(now, iToDay);
	    int nowDay = DateUtils.dayOfYear(now);
	    if (iFromDay > iToDay) {
	        if (nowDay > iToDay) {
	            return fromCal.getTime();
	        }
	        else {
	            fromCal.set(Calendar.YEAR, fromCal.get(Calendar.YEAR) - 1);
	            return fromCal.getTime();	            	            
	        }
	    } 
	    else {
	        if (nowDay > iToDay) {
	            fromCal.set(Calendar.YEAR, fromCal.get(Calendar.YEAR) + 1);
	            return fromCal.getTime();
	        }
	        else {
	            return fromCal.getTime();
	        }
	    }
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getEndDate(java.util.Date)
	 */
	public Date getEndDate(Date now) {
	    if (toDay == null) {
	        return new Date(Long.MAX_VALUE);
	    }
	    int iToDay = toDay.intValue();
	    Calendar toCal = calendarFor(now, iToDay);
	    if (fromDay == null) {
	        return toCal.getTime();
	    }
	    int iFromDay = fromDay.intValue();
	    Calendar fromCal = calendarFor(now, iFromDay);
	    int nowDay = DateUtils.dayOfYear(now);
	    Date startOfEndDay;
        if (nowDay > iToDay) {
            toCal.set(Calendar.YEAR, toCal.get(Calendar.YEAR) + 1);
            startOfEndDay = toCal.getTime();
        }
        else {
            startOfEndDay = toCal.getTime();
        }
	    return DateUtils.endOfDay(startOfEndDay);
	}
}
