package de.renew.simulator.api;

import java.util.Properties;

import de.renew.simulatorontology.shadow.ShadowNetSystem;
import de.renew.simulatorontology.shadow.SyntaxException;
import de.renew.simulatorontology.simulation.NoSimulationException;
import de.renew.simulatorontology.simulation.SimulationEnvironment;
import de.renew.simulatorontology.simulation.Simulator;
import de.renew.simulatorontology.simulation.SimulatorExtension;


/**
 * This interface is responsible for managing the simulation.
 * The facade for this interface is given by the {@link SimulationManager}.
 * All implementations must allow the following functionality:
 * <ul>
 *     <li>Setup and termination of a simulation</li>
 *     <li>Retrieval of information regarding the current simulation mode</li>
 *     <li>The insertion of a {@link ShadowNetSystem} that should be simulated</li>
 * </ul>
 */
public interface ISimulationManager {

    /**
     * Sets up a new simulation environment by doing the following:
     * <ul>
     * <li>the simulation thread pool is reset</li>
     * <li>the set of properties is configured</li>
     * <li>all registered extensions become activated</li>
     * <li>a new simulation engine is set up</li>
     * <li>the initial net instance is created</li>
     * </ul>
     * The simulation will <b>not</b> be started, so no steps will be executed.
     * All added {@link SimulatorExtension}s will be notified.
     *
     * <p>
     * This method will automatically create a new thread if it is not called
     * from a simulation thread. The disadvantage is that exceptions are not
     * communicated.
     * </p>
     *
     * <p>
     * The behavior of the method has changed from Renew release 2.1 to 2.2. It
     * no longer automatically terminates a running simulation. Instead, an
     * exception is thrown (see below).
     * </p>
     *
     * <p>
     * Callers of this method that switch to a simulation thread by themselves
     * should use the simulator's thread pool. This method
     * automatically discards the new thread pool if simulation setup fails.
     * After successful execution of this method, the new thread pool becomes
     * the current thread pool and the calling thread belongs to the current
     * simulation.
     * </p>
     *
     * <p>
     * Access to this method is exclusive. The Java synchronized mechanism is
     * replaced by a specialized {@link de.renew.simulator.api.SimulationLockExecutor}.
     * How to achieve synchronization across multiple methods is explained there.
     * </p>
     *
     * @param properties additional properties that specify this simulation
     *        environment. These properties will override any default values
     *        from the plugin properties. May be <code>null</code>.
     */
    void setupSimulation(Properties properties);

    /**
     * Adds net templates based on the shadow nets in the given {@code ShadowNetSystem} to the current
     * simulation.
     * <p>
     * When this method is called on a fresh simulation setup, the given net
     * system's information about shadow net loader and compiler are extracted
     * and kept for the simulation lifetime. In this case, the given {@code ShadowNetSystem}
     * <i>must</i> be configured with a {@code ShadowNetCompiler}. However,
     * setting a {@code ShadowNetLoader} is optional.
     * </p>
     *
     * <p>
     * Access to this method is exclusive. The Java synchronized mechanism is
     * replaced by a specialized {@link de.renew.simulator.api.SimulationLockExecutor}.
     * How to achieve synchronization across multiple methods is explained there.
     * </p>
     *
     * @param shadowNetSystem holds all nets to be compiled into this simulation
     *        environment. The state of the net system will change during
     *        the insertion process: nets are marked as compiled, and the
     *        default shadow net loader is configured (optional).
     *
     * @throws NoSimulationException if there is no simulation set up.
     * @throws SyntaxException if an error occurs during the compilation process.
     * @throws NullPointerException if the {@code netSystem} is {@code null}.
     */
    void addShadowNetSystem(ShadowNetSystem shadowNetSystem)
        throws SyntaxException, NoSimulationException;

    /**
     * Returns the currently configured {@code Simulator}, if a simulation is running (see {@link #isSimulationSetup()}.
     * Otherwise, {@code null} is returned.
     *
     * @return the currently configured {@code Simulator}, if a simulation is running. Otherwise, {@code null}
     */
    Simulator getCurrentSimulator();

    /**
     * Returns the properties used in the current simulation, if a simulation is running (see {@link #isSimulationSetup()}.
     * Otherwise, {@code null} is returned.
     *
     * @return the properties used in this simulation, if a simulation is running. Otherwise, {@code null}
     **/
    Properties getSimulationProperties();

    /**
     * Returns the current simulation environment, if a simulation has been set
     * up.
     * <p>
     * Do not expect that the data provided here has any guaranteed life span -
     * if you want to be informed about the termination of the simulation, write
     * and register a {@link SimulatorExtension}.
     * </p>
     *
     * @return a <code>SimulationEnvironment</code> object describing the actual
     *         simulation setup.
     */
    SimulationEnvironment getCurrentEnvironment();

    /**
     * Terminates the current simulation. If no simulation has been set up,
     * nothing happens.
     *
     * <p>
     * Access to this method is exclusive. The Java synchronized mechanism is
     * replaced by a specialized {@link de.renew.simulator.api.SimulationLockExecutor}.
     * How to achieve synchronization across multiple methods is explained there.
     * </p>
     */
    void terminateSimulation();

    /**
     * Returns whether the simulation is currently active. If this method returns
     * {@code true}, a simulation has been set up (see {@link #isSimulationSetup}
     * <i>and</i> is still active (see documentation of {@link Simulator#isActive}).
     * <p>
     * This method will automatically create a new thread if it is not called
     * from a simulation thread
     *
     * @return {@code true}, if the simulation is active (see above), {@code false} otherwise.
     */
    boolean isSimulationActive();

    /**
     * Returns whether the simulation is currently running.  If this method returns
     * {@code true}, a simulation has been set up. The simulation does not need to be active.
     * For that, use {@link #isSimulationActive}.
     *
     * @return {@code true}, if the simulation is setup (see above), {@code false} otherwise.
     */
    boolean isSimulationSetup();

    /**
     * Configure a default net loader to be used in the next
     * simulation setup. This net loader will be connected to the plugin's
     * shadow net system.
     */
    void setDefaultNetLoader();

    /**
     * Cleans up this instance. This must be called when the plug-in gets unloaded.
     */
    void cleanup();
}
