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;

/**
 * Schedule for a range of months, or a month.
 * 
 * @author Rob Gordon
 */

final public class MonthSchedule extends ConstrainedSchedule implements Serializable {

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

	private Integer fromMonth;
	private String stringFrom;
	private Integer toMonth;
	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) {
	        fromMonth = null;
	    }
	    else {
	        SimpleDateFormat format = new SimpleDateFormat(getFormat());
	        fromMonth = new Integer(DateUtils.month(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) {
	        toMonth = null;
	    }
	    else {
	        SimpleDateFormat format = new SimpleDateFormat(getFormat());
	        toMonth = new Integer(DateUtils.month(format.parse(to)));
	    }
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getTo()
	 */
	public String getTo() {
	    return stringTo;
	}
			
	/**
	 * Utility function to get the start of the month as a Calendar.
	 * 
	 * @param wholeDate The whole date.
	 * @param month The month to calculate the start of.
	 * @return The start of the month.
	 */
	private Calendar calendarFor(Date wholeDate, int month) {
		
		GregorianCalendar c1 = new GregorianCalendar();
		c1.setTime(wholeDate);
		GregorianCalendar c2 = new GregorianCalendar(
				c1.get(Calendar.YEAR), month, 1);
		return c2;
	}

	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getStartDate(java.util.Date)
	 */
	public Date getStartDate(Date now) {
	    if (fromMonth == null) {
	        return new Date(0);
	    }
	    Calendar fromCal = calendarFor(now, fromMonth.intValue());
	    if (toMonth == null) {
	        return fromCal.getTime();
	    }
	    Calendar toCal = calendarFor(now, toMonth.intValue());
	    int nowMonth = DateUtils.month(now);
	    int fromMonth = fromCal.get(Calendar.MONTH);
	    int toMonth = toCal.get(Calendar.MONTH);
	    if (fromMonth > toMonth) {
	        if (nowMonth > toMonth) {
	            return new GregorianCalendar(
	                    fromCal.get(Calendar.YEAR),
	                    fromCal.get(Calendar.MONTH),
	                    1).getTime();
	        }
	        else {
	            return new GregorianCalendar(
	                    fromCal.get(Calendar.YEAR) - 1,
	                    fromCal.get(Calendar.MONTH),
	                    1).getTime();	            
	        }
	    } 
	    else {
	        if (nowMonth > toMonth) {
	            return new GregorianCalendar(
	                    fromCal.get(Calendar.YEAR) + 1,
	                    fromCal.get(Calendar.MONTH),
	                    1).getTime();
	        }
	        else {
	            return new GregorianCalendar(
	                    fromCal.get(Calendar.YEAR),
	                    fromCal.get(Calendar.MONTH),
	                    1).getTime();	            
	        }
	    }
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.ConstrainedSchedule#getEndDate(java.util.Date)
	 */
	public Date getEndDate(Date now) {
	    if (toMonth == null) {
	        return new Date(Long.MAX_VALUE);
	    }
	    Calendar toCal = calendarFor(now, toMonth.intValue());
	    if (fromMonth == null) {
	        return toCal.getTime();
	    }
	    Calendar fromCal = calendarFor(now, fromMonth.intValue());
	    int nowMonth = DateUtils.month(now);
	    int fromMonth = fromCal.get(Calendar.MONTH);
	    int toMonth = toCal.get(Calendar.MONTH);
	    Date startOfEndDay;
	    if (fromMonth > toMonth) {
	        if (nowMonth > toMonth) {
	            startOfEndDay = new GregorianCalendar(
	                    toCal.get(Calendar.YEAR) + 1,
	                    toCal.get(Calendar.MONTH) + 1,
	                    0).getTime();
	        }
	        else {
	            startOfEndDay = new GregorianCalendar(
	                    toCal.get(Calendar.YEAR),
	                    toCal.get(Calendar.MONTH) + 1,
	                    0).getTime();	            
	        }
	    } 
	    else {
	        if (nowMonth > toMonth) {
	            startOfEndDay = new GregorianCalendar(
	                    toCal.get(Calendar.YEAR) + 1,
	                    toCal.get(Calendar.MONTH) + 1,
	                    0).getTime();
	        }
	        else {
	            startOfEndDay = new GregorianCalendar(
	                    toCal.get(Calendar.YEAR),
	                    toCal.get(Calendar.MONTH) + 1,
	                    0).getTime();	            
	        }
	    }
	    return DateUtils.endOfDay(startOfEndDay);
	}
}
