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 week.
 * <p>
 * Typical configuration is:
 * <code><pre>
 * &lt;dayofweek on="TUE"/&gt;
 * </pre></code>
 * 
 * @author Rob Gordon
 */


final public class DayOfWeekSchedule extends ConstrainedSchedule implements Serializable {

    private static final long serialVersionUID = 20050226;
    
	public final static String DEFAULT_DATE_FORMAT = "EEE";
	private String format = DEFAULT_DATE_FORMAT;

	/** The from day of week. */
	private Integer fromDay;
	
	/** The from day int as a string. */
	private String stringFrom;
	
	/** The to day of week. */
	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;	
	}
	
	/*
	 *  (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 sdf = new SimpleDateFormat(format);
		    fromDay = new Integer(DateUtils.dayOfWeek(sdf.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 sdf = new SimpleDateFormat(format);
		    toDay = new Integer(DateUtils.dayOfWeek(sdf.parse(to)));
		}
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getTo()
	 */
	public String getTo() {
	    return stringTo;
	}

    /**
     * Utility function to get a calendar which 
     * represents the day of the week in which now is.
     * @param now The date to take week from.
     * @param day The day.
     * @return The Calendar.
     */
	private Calendar calendarFor(Date wholeDate, int day) {
		
		GregorianCalendar c1 = new GregorianCalendar();
		c1.setTime(wholeDate);
		GregorianCalendar c2 = new GregorianCalendar(
				c1.get(Calendar.YEAR), c1.get(Calendar.MONTH), 
				c1.get(Calendar.DAY_OF_MONTH));
		c2.add(Calendar.DATE, day - c1.get(Calendar.DAY_OF_WEEK));
		return c2;
	}

	/*
	 *  (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.dayOfWeek(now);
	    if (iFromDay > iToDay) {
	        if (nowDay  > iToDay) {
	            return fromCal.getTime();
	        }
	        else {
	            fromCal.add(Calendar.DATE, -7);
	            return fromCal.getTime();
	        }
	    } 
	    else {
	        if (nowDay > iToDay) {
	            fromCal.add(Calendar.DATE, 7);
	            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.dayOfWeek(now);
	    Date startOfEndDay;
        if (nowDay > iToDay) {
            toCal.add(Calendar.DATE, 7);
            startOfEndDay = toCal.getTime();
        }
        else {
            startOfEndDay = toCal.getTime();
        }
	    return DateUtils.endOfDay(startOfEndDay);
	}
}
