/**
* Clase Controlador para coordinar la Vista con el Modelo (Patrón MVC).<br>
* Además de esa coordinación, se encarga de guardar y recuperar la copia del Modelo
* que se hace en disco.<br>También se encarga de analizar el tiempo que la aplicación
* ha estado cerrada tras la última sesión para calcular cuántas monedas han podido
* recuperarse en ese lapso de tiempo y decidir si hay que reactivar el Temporizador o no.
* @author Kabuto
* @see <a href="https://www.lawebdelprogramador.com/foros/Java/1751736-Continuacion-de-un-timer-despues-de-cerrar-la-aplicacion.html">
* Web del Programador</a>
*/
public class Controlador {
private Vista vista;
private Modelo modelo;
private Temporizador timer;
public Controlador() {
if (!leerModelo()) //No se pudieron leer datos de disco
modelo = new Modelo();
}
/**
* Establece la referencia a la Vista y ordena actualizar
* los valores que se muestran para las monedas y los segundos
* de recuperación, según lo que indique el Modelo.
* @param vista Objeto Vista que muestra la GUI al usuario.
*/
public void setVista(Vista vista) {
this.vista = vista;
this.vista.setMonedas();
this.vista.setSegundos();
}
/**
* Pregunta al Modelo cuántas monedas le constan
* y retorna dicho valor.<br>Este método lo invoca
* la Vista para mostrar el dato en pantalla.
* @return Cantidad de monedas que constan en el Modelo.
*/
public int getMonedasModelo() {
return modelo.getMonedas();
}
/**
* Informa al Modelo que ha de recuperar una moneda,
* pide a la Vista que se actualice y se hace un guardado
* en disco.<br>Este método lo invoca la clase Temporizador
* cuando han transcurrido los segundos necesarios para recuperar
* una moneda.
*/
public void modeloRecuperaMoneda() {
modelo.recuperaMoneda();
vista.setMonedas();
guardarModelo();
}
/**
* Pregunta al Modelo cuantos segundos necesarios
* para recuperar monedas le constan y retorna dicho valor.<br>
* Este método lo invoca la Vista para mostrar el dato
* en pantalla.
* @return Segundos necesarios para recuperar monedas.
*/
public int getSegundosModelo() {
return modelo.getTiempoNuevaMoneda();
}
/**
* Pregunta al Modelo cuál es el momento del día a partir
* del cuál el Temporizador tiene que iniciarse. Este momento
* se decide cuando se lanza la primera de las 5 monedas.<br>
* El Temporizador invoca este método cuando se inicia.
* @return Objeto LocalTime que representa el momento del día
* a partir del cuál el Temporizador ha de empezar a contar segundos.
*/
public LocalTime getTimerModelo() {
return modelo.getInicioTimer();
}
/**
* Transmite al Modelo un nuevo momento de inicio para el Temporizador.<br>
* Este método lo invoca el Temporizador, cuando se reactiva tras haber estado
* la aplicación cerrada y el propio Controlador le informa de cuántos segundos
* ha de incrementar su momento de inicio para seguir contando.
* @param nuevoTimer Objeto LocalTime con el momento de empezar a contar actualizado.
*/
public void setTimerModelo(LocalTime nuevoTimer) {
modelo.setInicioTimer(nuevoTimer);
}
/**
* Comunica al Modelo la nueva selección de segundos necesarios para
* recuperar monedas y se hace un guardado en disco.<br>Este método
* se invoca desde la Vista, cuando el usuario pulsa el botón
* "Establecer Tiempo".
* @param tiempo Valor seleccionado por el usuario en el JSpinner
* de la Vista.
*/
public void setTiempoModelo(Object tiempo) {
modelo.setTiempoNuevaMoneda((int) tiempo);
guardarModelo();
}
/**
* Solicita lanzar una moneda.<br>Si el Modelo indica que tiene monedas disponibles
* se hace el lanzamiento. A continuación se consulta si esta ha sido la primera de las
* cinco monedas disponibles, en cuyo caso, se activa una nueva instancia del Temporizador.<br>
* @return <i>True</i> si se lanzó una moneda,<i>False</i> en caso contrario, debido a que no
* hay monedas disponibles.
*/
public boolean lanzarMoneda() {
if (modelo.getMonedas() > 0) {
modelo.lanzarMoneda();
if (modelo.getMonedas() == 4) {//En este caso es que hemos lanzado la primera moneda
//Hay que activar el temporizador
timer = new Temporizador(this);
timer.start();
}
return true;
}
else
return false;
}
/**
* Hace una copia en disco de la instancia del Modelo que hay en memoria, de este modo
* se pueden recuperar los datos que alberga cada vez que se inicia la aplicación.<br>
* La copia se hace en el directorio <i>home</i> del usuario, según indique el sistema
* operativo.
*/
public void guardarModelo() {
File fichero = new File(System.getProperty("user.home") + "/LanzaMonedas/modelo.dat");
try {
if (!fichero.exists()) {
File carpeta = new File(System.getProperty("user.home") + "/LanzaMonedas");
carpeta.mkdir();
fichero.createNewFile();
}
ObjectOutputStream obs = new ObjectOutputStream(new FileOutputStream(fichero));
obs.writeObject(modelo);
obs.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Trata de recuperar la copia guardada del Modelo en disco, que debería estar
* en el directorio <i>home</i> del usuario indicado por el sistema operativo.<br>
* Si logra recuperar con éxito esta copia, el Controlador analizará si es necesario
* reactivar el Temporizador, actualizando sus valores según el tiempo que haya estado
* inactiva la aplicación.
* @return<i>True</i> si se pudo recuperar el Modelo guardado, <i>False</i> si no fue posible
* lo que implica que se creará un nuevo Modelo con los valores por defecto.
*/
public boolean leerModelo() {
File fichero = new File(System.getProperty("user.home") + "/LanzaMonedas/modelo.dat");
if (fichero.exists()) {
ObjectInputStream ois;
try {
ois = new ObjectInputStream(new FileInputStream(fichero));
Object aux = ois.readObject();
if (aux instanceof Modelo) {
modelo = (Modelo)aux;
analizarTiempoGuardado();
}
ois.close();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
else
return false;
}
/**
* Al recuperar el Modelo guardado en disco, hay que analizar si es
* necesario reactivar el Temporizador, para simular que ha permanecido
* contando segundos incluso con la aplicación cerrada.<br>
* Si el Modelo recuperado indica que se tenían las cinco monedas intactas
* no será necesario reactivar Temporizador, porque ya estaba inactivo cuando
* se cerró aplicación la última vez.<br>
* Si no se tienen las cinco monedas, signfica que el Temporizador sí estaba
* funcionando cuando se cerró la aplicación. En este caso se compara el tiempo
* que según el Modelo se activó el Temporizador anterior con el tiempo actual.<br>
* De esta comparación se determina cuántas monedas han podido ser recuperadas durante
* el tiempo que la aplicación ha estado inactiva y si aún no se han recuperado las
* cinco monedas, se activará un nuevo Temporizador con los valores actualizados como si
* hubiera seguido funcionando todo el tiempo de inactividad.
*/
private void analizarTiempoGuardado() {
if (modelo.getMonedas() != 5) {
/*
* El Modelo recuperado de disco indica que cuando se guardó
* no se tenían las 5 monedas. Esto implica que había un timer
* activo cuando se cerró el programa.
* Ahora debemos decidir si hay que reactivar el timer, pero primero
* hay que analizar cuánto tiempo ha transcurrido desde que se cerró el
* programa para determinar si durante este lapso de tiempo ya se ha
* recuperado alguna moneda, o todas.
*/
int monedasRecuperadas = 0;
int segundosAhora = 0;
int segundosTimer = 0;
LocalTime inicioTimer = modelo.getInicioTimer();
LocalTime ahora = LocalTime.now();
/*
* Primero comprobamos si los "momentos" de tiempo
* son del mismo dia o no. Si el "ahora" es de un dia posterior,
* las monedas se consideran recuperadas
*/
if (LocalDate.now().getDayOfYear() > modelo.getFechaInicio().getDayOfYear())
monedasRecuperadas = 5;
else {
segundosAhora = ahora.toSecondOfDay();
segundosTimer = inicioTimer.toSecondOfDay();
monedasRecuperadas = (segundosAhora - segundosTimer) / modelo.getTiempoNuevaMoneda();
}
modelo.setMonedas(modelo.getMonedas() + monedasRecuperadas);
if (modelo.getMonedas() != 5) {
/*
* Seguimos sin tener las 5 monedas. Hay que activar timer.
* A su momento de inicio, le añadiremos el tiempo que no ha estado contando mientras
* el programa estuvo cerrado.
*/
timer = new Temporizador(this);
timer.agregarTiempo(segundosAhora - segundosTimer);
timer.start();
}
}
}
}