/*
* This file is part of MazeSolver.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (c) 2014 MazeSolver
* Sergio M. Afonso Fumero <theSkatrak@gmail.com>
* Kevin I. Robayna Hernández <kevinirobaynahdez@gmail.com>
*/
/**
* @file SimulationManager.java
* @date 3/11/2014
*/
package es.ull.mazesolver.util;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Observable;
import javax.swing.Timer;
import es.ull.mazesolver.gui.MainWindow;
import es.ull.mazesolver.gui.environment.Environment;
import es.ull.mazesolver.gui.environment.EnvironmentSet;
/**
* Gestor de la simulación.
*/
public class SimulationManager extends Observable {
private static int DEFAULT_INTERVAL = 200;
private static int FAST_INTERVAL = 20;
private Timer m_timer;
private boolean m_paused;
private int m_steps;
private EnvironmentSet m_environments;
private boolean [] m_finished;
private boolean m_sim_finished;
private SimulationResults m_results;
/**
* Constructor por defecto del simulador.
*
* @param env_set
* Conjunto de entornos que va a manejar.
*/
public SimulationManager (EnvironmentSet env_set) {
m_steps = -1;
m_results = new SimulationResults();
setEnvironments(env_set);
m_timer = new Timer(0, new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
doStep();
}
});
m_timer.setDelay(DEFAULT_INTERVAL);
m_timer.setRepeats(true);
}
/**
* Establece la velocidad de simulación al indicar el tiempo que pasa entre
* cada paso.
*
* @param msec
* Milisegundos que pasarán entre cada paso de la simulación.
*/
public void setInterval (int msec) {
if (msec > 0)
m_timer.setDelay(msec);
}
/**
* Establece el conjunto de entornos que manipula la simulación.
*
* @param env_set
* Conjunto de entornos.
*/
public void setEnvironments (EnvironmentSet env_set) {
if (env_set == null)
throw new IllegalArgumentException(
MainWindow.getTranslations().exception().invalidEnvironment());
m_environments = env_set;
}
/**
* Comienza la simulación. Si está pausada, la reanuda. Nota: No se pueden
* agregar o eliminar entornos mientras la simulación se está ejecutando.
*/
public void startSimulation () {
m_sim_finished = false;
// Actualizamos el tamaño de la lista de entornos finalizados por si hay un
// número diferente de entornos que en la última ejecución
if (isStopped()) {
m_finished = new boolean [m_environments.getEnvironmentCount()];
m_results.clear();
}
m_paused = false;
// Lanzamos un hilo sólo si no se está ejecutando todavía
if (!isRunning()) {
m_timer.start();
m_results.startTimer();
// Avisamos a los observadores de que la simulación ha cambiado de estado
setChanged();
notifyObservers();
}
}
/**
* Comienza una simulación rápida. Hay que indicar el número de pasos que
* se desean simular, tras los cuales la simulación se pausará. Si la
* simulación acaba antes del número de pasos indicado, se parará.
*
* @param steps
* Número de pasos máximo que se simulará.
*/
public void startFastSimulation (int steps) {
if (steps > 0) {
m_steps = steps;
setInterval(FAST_INTERVAL);
startSimulation();
}
}
/**
* Pausa la simulación actual si se está ejecutando.
*/
public void pauseSimulation () {
if (isRunning()) {
m_timer.stop();
m_results.pauseTimer();
m_paused = true;
// Avisamos a los observadores de que la simulación ha cambiado de estado
setChanged();
notifyObservers();
}
}
/**
* Para la simulación actual si se está ejecutando.
*/
public void stopSimulation () {
if (isRunning() || isPaused()) {
m_timer.stop();
m_results.pauseTimer();
m_paused = false;
if (m_steps >= 0) {
m_steps = -1;
setInterval(DEFAULT_INTERVAL);
}
// Avisamos a los observadores de que la simulación ha cambiado de estado
setChanged();
notifyObservers(m_results);
}
}
/**
* Lleva a cabo un paso de la simulación, arrancándola si no está actualmente
* ejecutándose. Este método está pensado para ser utilizado por el usuario
* directamente cuando quiera hacer una ejecución paso a paso.
*/
public void stepSimulation () {
if (isStopped()) {
startSimulation();
pauseSimulation();
}
doStep();
}
/**
* Indica si la simulación se está ejecutando.
*
* @return Si la simulación se está ejecutando.
*/
public boolean isRunning () {
return m_timer.isRunning();
}
/**
* Indica si la simulación está pausada.
*
* @return Si la simulación está pausada.
*/
public boolean isPaused () {
return m_paused;
}
/**
* Indica si la simulación está parada.
*
* @return Si la simulación está parada.
*/
public boolean isStopped () {
return !m_timer.isRunning() && !m_paused;
}
/**
* Indica si la simulación ha finalizado; es decir, que todos los agentes en
* los entornos están parados.
*
* @return Si la simulación ha acabado (todos los agentes están parados).
*/
public boolean isFinished () {
return m_sim_finished;
}
/**
* Devuelve los resultados de la simulación actual. Puede ser que sean incompletos,
* dado que puede ser que la simulación no haya acabado.
*
* @return Resultados de la simulación actual.
*/
public final SimulationResults getResults () {
return m_results;
}
/**
* Lleva a cabo un paso de la simulación.
*/
private void doStep () {
// Controlamos que si el número de pasos fue especificado y ya llegó a cero,
// que se pause la simulación y se restablezca la velocidad de ejecución
if (m_steps == 0) {
pauseSimulation();
setInterval(DEFAULT_INTERVAL);
m_steps = -1;
return;
}
// Si el número de pasos fue especificado se va decrementando para parar en
// el momento oportuno
if (m_steps > 0)
--m_steps;
int amount_finished = 0;
// Hacemos que ejecuten un paso todos los agentes de todos los entornos
// donde no haya acabado algún agente
ArrayList <Environment> envs = m_environments.getEnvironmentList();
for (int i = 0; i < envs.size(); i++) {
if (!m_finished[i])
m_finished[i] = envs.get(i).runStep(m_results);
else
amount_finished++;
}
// Si todos los agentes han terminado de ejecutar, paramos la simulación
if (amount_finished == m_environments.getEnvironmentCount()) {
m_sim_finished = true;
stopSimulation();
}
}
}