package de.tu_darmstadt.tk.SmartHomeNetworkSim.core.scheduler;

import java.util.TreeSet;

import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Connection;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Model;
import de.tu_darmstadt.tk.SmartHomeNetworkSim.core.Port;

/**
 * The Scheduler manages the EventQueue of the framework and allows manipulating the queue.
 *
 * @author Andreas T. Meyer-Berg
 */
public class Scheduler {

	/**
	 * Event Queue, which stores the different events
	 */
	private TreeSet<Schedulable> eventQueue;
	
	/**
	 * Minimum time step. No earlier events can be scheduled
	 */
	private long minimumTimeStep = Long.MIN_VALUE;
	
	/**
	 * Initializes a new Scheduler with an empty event queue.
	 */
	public Scheduler() {
		eventQueue  = new TreeSet<Schedulable>(new ScheduleComparator());
	}
	
	
	/**
	 * Schedules the given {@link Schedulable}, by adding it to the event queue. 
	 * @param event Event which should be scheduled
	 * @return true if it was scheduled, false if otherwise
	 */
	public boolean scheduleEvent(Schedulable event){
		if(event.getEventTime()<minimumTimeStep){
			
			System.out.println("Min: "+minimumTimeStep+"  Event: "+event.getEventTime());
			System.out.println("Could not schedule: "+event.toString());
			System.out.println("Simulate now ");
			event.simulateEvent(minimumTimeStep);//TODO: Maybe other solution
			//throw new Error("Fail");
			
			return false;
		}else {
			return eventQueue.add(event);
		}
	}
	
	/**
	 * Schedules all network events of the given model
	 * @param m Model which should be scheduled
	 */
	public void scheduleAll(Model m){
		for(Connection c: m.getConnections())
			for(Port p:c.getParticipants()){
				if(p != null && p.getStatus() == Port.SENDING)
				scheduleEvent(p);				
			}
	}
	
	/**
	 * Return true, if the EventQueue contains another event before the given maximum time
	 * @param maxTime exclusive maximum time events should have in this simulation interval
	 * @return true, if 
	 */
	public boolean hasNext(long maxTime){
		if(eventQueue.isEmpty()) 
			return false;
		return eventQueue.first().getEventTime()<maxTime;
	}
	
	/**
	 * Returns the next Event, which will be simulated
	 * @return next event
	 */
	public Schedulable getNextEvent(){
		return eventQueue.first();
	}
	
	/**
	 * Returns the first Element of the queue and removes it.
	 * @return
	 */
	public Schedulable getAndRemoveFirst(){
		return eventQueue.pollFirst();
	}
	
	/**
	 * Removes the event from the Event Queue, it will not be simulated
	 * @param event Event to be removed
	 * @return true if it was removed
	 */
	public boolean removeEvent(Schedulable event){
		return eventQueue.remove(event);
	}
	
	/**
	 * Resets the EventQueue by removing all events and resetting the minimum timestep
	 */
	public void reset(long newMinTime){
		minimumTimeStep = newMinTime;
		eventQueue.clear();
	}
	
	/**
	 * Returns the minimum Time, events could be scheduled.
	 * @return minimum time step
	 */
	public long getMinTime(){
		return minimumTimeStep;
	}
	
	/**
	 * Sets the new minimum time. No events will be scheduled earlier.
	 * @param newMinTime new minimum time
	 */
	public void setMinTime(long newMinTime){
		this.minimumTimeStep = newMinTime;
	}
}
