package de.hsbo.fbg.systemdynamics.model;

import de.hsbo.fbg.systemdynamics.output.SimulationEventListener;

import java.util.ArrayList;

/**
 * This class represents a system dynamics simulation and controls the
 * simulation execution.
 *
 * @author <a href="mailto:sebastian.drost@hs-bochum.de">Sebastian Drost</a>
 * @author <a href="mailto:matthias.stein@hs-bochum.de">Matthias Stein</a>
 */
public class Simulation {

    private Model model;
    private ArrayList<SimulationEventListener> simulationListener;


    /**
     * Constructor.
     *
     * @param model the model object that describes the system dynamic model.
     */
    public Simulation(Model model) {
        this.model = model;
        this.simulationListener = new ArrayList<>();
    }

    /**
     * This method runs the simulation.
     */
    public void run() {
        this.prepareInitialValues();

        // Prepare all values for the first time step and run the simulation for
        // it. The stock converters don't have to be execute, because for the
        // first time step their current value is the same as their initial
        // value.
        this.prepareValuesForFirstTimestep();
        fireSimulationInitializedEvent(this.model);
        executeConverters();
        fireTimeStepCalculatedEvent(this.model);
        while (this.finalTimeReached()) {
            this.updateCurrentTime();
            this.prepareValuesForTimestep();
            model.getIntegration().integrate();
            executeConverters();
            System.out.println(this.model);
            fireTimeStepCalculatedEvent(model);
        }
        fireSimulationFinishedEvent(model);
    }

    /**
     * Prepare all initial model values for running the simulation.
     */
    private void prepareInitialValues() {
        this.model.setCurrentTime(this.model.getInitialTime());
        this.model.getModelEntities().forEach((k, v) -> {
            v.setCurrentValue(v.getInitialValue());
            v.setCurrentValueCalculated(false);
        });
        this.model.getIntegration().setStocks(model.getStocks());
        this.model.getIntegration().setVariableConverter(model.getConverterList());
        this.model.getIntegration().setDt(this.model.getTimeSteps());
    }

    /**
     * Method to prepare all Stocks whose current value is already calculated
     * for the first timestep.
     */
    private void prepareValuesForFirstTimestep() {
        this.model.getModelEntities().forEach((k, v) -> {
            if (v instanceof Stock && this.model.getCurrentTime() == this.model.getInitialTime()) {
                v.setCurrentValueCalculated(true);
            }
        });

    }

    /**
     * Method to prepare all values for the next timestep.
     */
    private void prepareValuesForTimestep() {
        this.model.getModelEntities().forEach((k, v) -> {
            v.setPreviousValue(v.getCurrentValue());
            v.setCurrentValueCalculated(false);
        });
    }

    /**
     * Method to update the current time by adding one time step.
     */
    private void updateCurrentTime() {
        this.model.setCurrentTime(this.model.getCurrentTime() + this.model.getTimeSteps());
    }

    /**
     * Method that controls if the final time has been reached.
     *
     * @return <tt>true</tt> only if the final time has been reached.
     */
    public boolean finalTimeReached() {
        return this.model.getCurrentTime() < this.model.getFinalTime();
    }

    /**
     * Method to execute the converters.
     */
    private void executeConverters() {
        for (Converter converter : this.model.getConverterList()) {
            if (converter != null && !converter.getTargetEntity().isCurrentValueCalculated()) {
                converter.convert();
            }
        }

    }

    /**
     * Adds an listener that handles simulation events.
     *
     * @param listener {@link SimulationEventListener}
     */
    public void addSimulationEventListener(SimulationEventListener listener) {
        this.simulationListener.add(listener);
    }

    /**
     * Removes a {@link SimulationEventListener}.
     *
     * @param listener {@link SimulationEventListener}
     */
    public void removeSimulationEventListener(SimulationEventListener listener) {
        this.simulationListener.remove(listener);
    }

    /**
     * Fires an event for the initialization of the simulation.
     *
     * @param model {@link Model} for the simulation
     */
    private void fireSimulationInitializedEvent(Model model) {
        this.simulationListener.forEach(listener -> listener.simulationInitialized(model));
    }

    /**
     * Fires an event for a finished calculation of a time step.
     *
     * @param model {@link Model} for the simulation
     */
    private void fireTimeStepCalculatedEvent(Model model) {
        this.simulationListener.forEach(listener -> listener.timeStepCalculated(model));
    }

    /**
     * Fires an event for a finished simulation.
     *
     * @param model
     */
    private void fireSimulationFinishedEvent(Model model) {
        this.simulationListener.forEach(listener -> listener.simulationFinished(model));
    }

}

@@ -1175,32 +1175,37 @@
 step();%0A        
+this.
 fireSimulationIn
@@ -1232,32 +1232,37 @@
 model);%0A        
+this.
 executeConverter
@@ -1266,32 +1266,37 @@
 ters();%0A        
+this.
 fireTimeStepCalc
@@ -1457,16 +1457,21 @@
         
+this.
 model.ge
@@ -1510,16 +1510,21 @@
         
+this.
 executeC
@@ -1588,24 +1588,29 @@
             
+this.
 fireTimeStep
@@ -1651,16 +1651,21 @@
         
+this.
 fireSimu
@@ -5045,16 +5045,49 @@
 am model
+ %7B@link Model%7D for the simulation
 %0A     */

Reformat code