package org.treesched;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

/**
 * Provide a schedule based on a list of schedules. All schedules are
 * evaluated and that schedule which is due to start first is used.
 * 
 * <p>
 * Most schedules use this class to support a list of child scheudules
 * such as this.
 * <code><pre>
 * &lt;schedule&gt;
 *   &lt;day-of-week on="mon"/&gt;
 *   &lt;day-of-week on="fri"/&gt;
 * &lt;/schedule&gt;
 * </pre></code>
 * 
 * @author Rob Gordon
 */

final public class ScheduleList implements Serializable, RefineableSchedule {

    private static final long serialVersionUID = 20050226;
    
    private static final Logger logger = Logger.getLogger(ScheduleList.class);
    
    /** List of Schedules. */
	private final List schedules = new ArrayList();

	/** Limits */
	private Interval limits;
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.Schedule#setLimits(org.treesched.Interval)
	 */
	public void setLimits(Interval limits) {
	    this.limits = limits;
	}
	
	/**
	 * Get any limits set for this schedule.
	 * 
	 * @return The limits, may be null.
	 */
	public Interval getLimits() {
	    return limits;
	}
	
	/**
	 * The number of subschedules this list contains.
	 * 
	 * @return The number of subschedules.
	 */
	public int size() {
		return schedules.size();	
	}
	
	/**
	 * Add a schedule to the list.
	 * 
	 * @param schedule The schedule to add.
	 */
	public void addChildSchedule(Schedule schedule) {
		schedules.add(schedule);
	}
	
	/*
	 *  (non-Javadoc)
	 * @see org.treesched.Schedule#nextDue(java.util.Date)
	 */
	public Interval nextDue(Date now) {
		if (now  == null) {
		    throw new NullPointerException();
		}
		logger.debug(this + ": in date " + now);
		Interval candidate = null;

		int i = 1;		
		for	(Iterator it = schedules.iterator(); it.hasNext(); ++i) {
			Schedule schedule = (Schedule)it.next();
			
			logger.debug(this + ": evaluating schedule " + i + " (" + schedule + ")");
			
			schedule.setLimits(limits);
			Interval nextDue = schedule.nextDue(now);
			if (nextDue != null) {
			    if (limits != null) {
			        nextDue = nextDue.limit(limits);
			    }			
			    if (candidate == null || nextDue.isBefore(candidate)) {
			        candidate = nextDue;
			    }
			}
		}
		logger.debug(this + ": returning " + candidate);
		return candidate;
	}

	/**
	 * Reset the child schedules.
	 */	
	public void reset() {
		for	(Iterator it = schedules.iterator(); it.hasNext();) {
			((Schedule) it.next()).reset();
		}
	}

	/**
	 * Override toString to be more useful.
	 * 
	 * @return A description of the schedule.
	 */
	public String toString() {
		return "Schedule List, size " + size();
	}
}
