package sema;

import java.util.TreeMap;
import java.util.Stack;

/**
 * Engine of the modelling.
 * The current time is the one of the last treated event (t=0 at initialisation).
 * It simulates a continious time (coded on a {@link java.lang.Double}).
 * An {@link Event} executes itself with no duration in the simulation.
 * The lasting of a series of event is emulated by the virtual time to wait until next event.
 */
public class Engine {
    
    private Double t = new Double(0); //temps dans le monde simulé
    private TreeMap events = new TreeMap(); //événements du monde simulé
    private int futureEvents = 0; //nombre d'événements à traiter, pour affichage dans l'interface de Sema
    private int pastEvents = 0; //nombre d'événement traités, pour affichage dans l'interface de Sema
    
    /**
     * Returns the current simulated time in milliseconde
     */
    public double getTime() {
        return t.doubleValue();
    }
    
    /**
     * Add an event to the engine
     * Events with strict inferior dates to the current simulated time, are ignored.
     */
    protected void add(Event ev) {
        Double tEv = ev.date;
        if (tEv.compareTo(t)>=0) //vérifie que l'événement à ajouter n'a pas lieu dans le passé
        {
            if (events.containsKey(tEv)) {
                Stack s = (Stack) events.get(tEv);
                s.add(ev);
            } else {
                Stack s = new Stack();
                s.push(ev);
                events.put(tEv, s);
            }
        }
        futureEvents++;
    }
    
    
    /**
     * Delete an event from the engine.
     */
    public void delete(Event ev){
        ev.delete();
    }
    
    /**
     * Indicates that the simulation has ended. (no more events)
     */
    public static class EndOfSimulation extends Exception {
    }
    
    /**
     * The last event has raised an exception.
     */
    public static class SimulationError extends Exception {
        public SimulationError(String error){
            new Exception(error);
        }
    }
    
    /**
     * Executes the events of the smallest date.
     */
    public void next() throws EndOfSimulation, SimulationError {
        try {t = (Double) events.firstKey();} catch (Exception e) {throw new EndOfSimulation();}
        Stack s = (Stack) events.remove(t);
       // try {
            while (!(s.empty())) {
                Event ev = (Event) s.pop();
                futureEvents--;
                pastEvents++;
                if (ev!=null)
                    ev.execute();
            }
       // } catch (Exception e) {System.out.println(e);events.put(t,s); throw new SimulationError(e.toString());} //si erreur, on remet les événements restants dans le TreeMap.
    }
    
    /**
     * Executes every events until it reachs the date.
     */
    public void gotoDate(Double date) throws EndOfSimulation, SimulationError {
        while (t.compareTo(date)<0) {
            next();
        }
    }
    
    
    /**
     * Returns the world's properties. The properties are formated as a table with two columns,
     * the first with the name of the property and the second with the value corresponding : ( everything must be of string type )
     * {{"prop1", "value1"}, ..., {"propN", "valueN"}}.<br>
     */
    public String[][] getProperties(){
        String[] p8 = {"Future events",new Integer(futureEvents).toString()};
        String[] p9 = {"Past events",new Integer(pastEvents).toString()};
        String[][] a = {p8,p9};
        return a;
    }
    
    /**
     * Allows the modification of the properties through the interface.<br>
     * It takes the name of the property and the value (as a string) we want to associated to it.
     * It changes it if it decides it but no waranties are made.
     * The name of the property must be the same as the one returned by {@link #getProperties()}.
     */
    public void setProperties(String property, String value){
    }
    
}
