import java.util.*;

        // The Scheduler for an event-driven simulation.
public class Scheduler {
        // Continuously process the events in the queue,
        // until stopTime.
    public void run(long stopTime){
        ArrayList events = getEvents();
        long now = getTime();

        // Terminate if there are no more events or if time
        // has expired.
        while((events.size() > 0) && (now <= stopTime)){
            // Obtain the next event
            Event e = (Event) events.remove(0);

            setTime(e.getWhen());
            now = getTime();

            if(now <= stopTime){
                if(getTraceEvents()){
                    // Print a trace.
                    System.out.println("=> "+e);
                }
                // Give the event a reference to the scheduler.
                e.perform(this);
            }
        }
    }

        // Add the given event to the appropriate place in
        // the event queue. The queue is ordered by event time.
    public void schedule(Event newEvent) throws RuntimeException {
        long eventTime = newEvent.getWhen();
        if(eventTime >= getTime()){
            ArrayList events = getEvents();
            final int numEvents = events.size();
            for(int index = 0; index < numEvents; index++){
                Event e = (Event) events.get(index);
                if(eventTime < e.getWhen()){
                    // Insert the new one here.
                    events.add(index,newEvent);
                    return;
                }
            }
            // Place it at the end.
            events.add(newEvent);
        }
        else{
            throw new RuntimeException(
                        "Attempt to schedule an event too late.");
        }
    }

    public boolean getTraceEvents(){
        return traceEvents;
    }

    public void setTraceEvents(boolean trace){
        traceEvents = trace;
    }

    public long getTime(){
        return time;
    }

        // Make sure time only moves forward.
    protected void setTime(long t) throws RuntimeException {
        if(t >= getTime()){
            time = t;
        }
        else{
            throw new RuntimeException(
                "Attempt to set the clock back to "+t+" from "+getTime());
        }
    }

    protected ArrayList getEvents(){
        return events;
    }

    // Whether to trace events or not.
    private boolean traceEvents = true;
    // The event queue ordered by event time.
    private ArrayList events = new ArrayList();
    // The current time.
    private long time = 0L;
}
