package org.treesched;

import java.text.ParseException;
import java.util.Date;

import org.apache.log4j.Logger;

/**
 * A base class for a Schedule which has a from and a to
 * date.
 * 
 * @author Rob Gordon
 */

abstract public class ConstrainedSchedule extends AbstractSchedule {

    private static final long serialVersionUID = 20050226;
    
    private static Logger logger = Logger.getLogger(ConstrainedSchedule.class);

    /**
     * Set the from date/time as a String.
     * 
     * @param from The from date.
     * 
     * @throws ParseException If the String can not be parsed
     * to a date.
     */
    abstract public void setFrom(String from) throws ParseException;

    /**
     * Get the from date/time as a String.
     * 
     * @return The from date.
     */
    abstract public String getFrom();
    
    /**
     * Set the to date/time as a String.
     * 
     * @param to The to date.
     * 
     * @throws ParseException If the String can not be parsed
     * to a date.
     */
    abstract public void setTo(String to) throws ParseException;

    /**
     * Get the to date/time as a String.
     * 
     * @return The to date.
     */
    abstract public String getTo();
	
    /**
     * For the given time, calculate when this schedule
     * should start or should have started.
     * 
     * @param now The date/time now.
     * @return The date/time the scheudle is/was due.
     */
	abstract public Date getStartDate(Date now);
	
	/**
	 * For the given time, calculate when this schedule
	 * interval should finish.
	 * 
	 * @param now The date/time now.
	 * @return The date/time the schedule should finish.
	 */
	abstract public Date getEndDate(Date now);
	
	/**
	 * Set the on date as a string.
	 * 
	 * @param in The on text.
	 * @throws ParseException If the string isn't a date.
	 */
	public void setOn(String on) throws ParseException {		
		setFrom(on);
		setTo(on);
	}

	/*
	 *  (non-Javadoc)
	 * @see org.treesched.Schedule#nextDue(java.util.Date)
	 */
	public Interval nextDue(Date now) {
		if (now == null) {
			throw new NullPointerException();
		}

		Interval withoutLimits = new Interval(
		        getStartDate(now), getEndDate(now));
		Interval nextInterval = withoutLimits.limit(getLimits());
		logger.debug(this + ": in date is " + now + 
				", next interval is " + nextInterval);
		if (nextInterval == null) {
		    return null;
		}
		Schedule child = getChildSchedule();
		if (child == null) {
			return nextInterval;
		}
		child.setLimits(nextInterval);
		Interval childInterval = null;
		// if now is before the start of the next interval
		// pass the start of the next interval to the child.
		if (now.compareTo(nextInterval.getFromDate()) < 0) {
		    childInterval = child.nextDue(nextInterval.getFromDate());
		}
		else {
		    childInterval = child.nextDue(now);
		}
		// if the child interval is null we must be passed
		// the end of the child for this interval so try the
		// next.
		if (childInterval == null) {
		    return nextDue(DateUtils.oneMillisAfter(
		            nextInterval.getToDate()));
		}
		return childInterval;
	}

	/**
	 * Override toString.
	 * 
	 * @return A description of the schedule.
	 */
	public String toString() {
		return this.getClass().getName() + " from " + getFrom() + " to " + getTo();
	}
}
