package org.treesched;

import java.util.Date;

import org.apache.log4j.Logger;

/**
 * This is a base class for schedule which repeat at intervals.
 * 
 * @author Rob Gordon
 */

abstract public class RegularSchedule extends AbstractSchedule {

    private static final long serialVersionUID = 20050226;
    
    private static Logger logger = Logger.getLogger(RegularSchedule.class);
    
    /**
     * Given a date, return the date which is interval of this
     * RegularScheudle later.
     * 
     * @param from The date from which this schedule runs.
     * @return The interval later.
     */
    abstract public Date next(Date from);
    
    /*
     *  (non-Javadoc)
     * @see org.treesched.Schedule#nextDue(java.util.Date)
     */
	public Interval nextDue(Date now) {
		if (now == null) {
		    throw new NullPointerException();
		}
		
		Interval nextInterval = null;
		if (getLimits() == null) {
		    // no limits so just return an interval later.
		    nextInterval = new Interval(now, DateUtils.oneMillisBefore(next(now)));
		}
		else {
		    // if there is limits then calculate the interval 
		    // that might be due from the beginning.
		    Date start = getLimits().getFromDate();
		    Date end = getLimits().getToDate();
		    // if before start - take the first interval.
			if (now.compareTo(start) < 0) {				
				nextInterval = new Interval(start, 
				        DateUtils.oneMillisBefore(next(start)));
			}
			// if the given date is greater than the to time, the schedule is never due.
			else if (now.compareTo(end) > 0) {
				return null;
			}
			else {
			    // work up in incrememnts of interval to the one 
			    // that's now due.
			    while (true) {		
			        Date next = next(start);
			        if (now.compareTo(next) < 0) {
			            nextInterval = new Interval(start, DateUtils.oneMillisBefore(next));
			            break;
			        }
			        start = next;
			    }
			}
		}
		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 = 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 to be more meaningful.
	 * 
	 * @return a Description of this schedule.
	 */	
	public String toString() {		
		return this.getClass().getName() 
		+ ", interval=" + next(new Date(0)).getTime()
		+ ", limits=" + getLimits();
	}
}
