/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package sdsimulator.runnable;
import myuml.*;

/**
 *
 * @author Miguel Herreros
 */
        
public class DiagramaEjecutable {
    private java.util.ResourceBundle  bundle;    //  Para recuperar los strings.
    public  int                       paso;      //  Número de paso que se está ejecutando.
    private ContextoEjecucion         contexto;  //  Objetos, Actores y variables miembro.
    private GlobalEjecutable          global;    //  Componentes ejecutables del diagrama.
    private AsincronosPendientes      asincronos;//  Cola donde se almacenan los mensajes 
                                                 //asíncronos enviados que todaviía no han llegado.
    private boolean                   terminado; //  A cierto si se ha terminado de ejecutar todo (in-
                                                 //cluído que asíncronos esté vacío).
    //..........................................................................
    /** Constructor de la clase. Recibe un arreglo con todos los componentes del 
     * diagrama tal y como trabaja con él la herramienta WST (referencias a myObjects.
     * @param todos
     */
    public DiagramaEjecutable(Object[] todos) throws Exception{
        bundle = java.util.ResourceBundle.getBundle("sdsimulator/resources/runnable");
        paso      = 0;
        terminado = false;
        
        VectorOrdenado eventos   = buscarEventos(todos);   //todos los MyMessage y MyFrame ordenados por coordenada Y
        VectorOrdenado entidades = buscarEntidades(todos); //todos los MyLifeLine y Actors ordenados por coordenada X
        contexto    = new ContextoEjecucion(entidades);
        asincronos  = new AsincronosPendientes();
        global      = new GlobalEjecutable(eventos,contexto,asincronos);
    }

    //..........................................................................
    /** Invoca al método runStep del último componente ejecutable que no esté termi-
     *  nado.
     *  En siguientes pasos, además, también se controla qué mensajes asíncronos
     *  van llegando (vacía asíncronos).
     *  Se dejará de invocar cuando todo esté terminado (antes de llamar a este 
     *  método, se invocará a Completado() para detectar que no hace falta eje-
     *  cutar nada más).
     * @return
     */
    public ResultadoEjecucion runStep(){
        ResultadoEjecucion retorno;
        
        asincronos.setPaso(paso);
        
        if (paso==0){
            retorno = primerPaso();
            paso++;
        }
        else{
            retorno = siguientesPasos();
            paso++;
        }

        return retorno;
    }
    
    //..........................................................................
    /** Deja el diagrama listo para una nueva simulación.
     */
    public void reset(){
        contexto.reset();
        global.reset();
        asincronos.reset();
        paso=0;
        terminado=false;
    }
        
    //..........................................................................
    /** El primer paso es especial ...
     * @return
     */
    private ResultadoEjecucion primerPaso(){
        ResultadoEjecucion retorno = global.runStep();
        
        retorno.traza = bundle.getString("COMIENZO_DE_TRAZA") + 
                        bundle.getString("COMIENZO_DE_PASO")  + paso + 
                        bundle.getString("COMIENZO_DE_PASO1") + retorno.traza +
                        asincronos.asincronosPendientes() + contexto.estado();
        
        return retorno;
    }
    //..........................................................................
    /** Los siguientes pasos son iguales, salvo el último, que vuelve a ser espe-
     * cial.
     * @return
     */
    private ResultadoEjecucion siguientesPasos(){
        String bufferLlegadas        = asincronos.llegadas(); //Si no hay pendientes, devuelve "".
        ResultadoEjecucion resultado = global.runStep();       //Si no hay más, devuelve resultado inicializado (0,0,""). 
        String tituloPaso = bundle.getString("COMIENZO_DE_PASO") + paso + bundle.getString("COMIENZO_DE_PASO1");
        
        if (terminado){
            //Esto no se ejecuta nunca. Pero por si acaso...
            System.out.println("¡NO QUEDAN MÁS PASOS POR EJECUTAR!");
            return new ResultadoEjecucion();
        }
        
        if (global.completado() && asincronos.isEmpty() && bufferLlegadas.isEmpty() && resultado.traza.isEmpty()){
            terminado=true;
            contexto.ejecucionTerminada(); //Pinta de azul los actores y objetos
            resultado.traza = bundle.getString("FIN_DE_TRAZA");//como realmente no hay
            //nada más que mostrar, no dibuja ni cabecera de paso ni contexto.
        }
        else{
            resultado.traza = bufferLlegadas + resultado.traza;
            resultado.traza = tituloPaso + resultado.traza + asincronos.asincronosPendientes() + contexto.estado();
        }
        
        return resultado;
    }
    
    //..........................................................................
    /**Devuelve cierto si el diagrama está completamente ejecutado. La condición
     * que detecta que no hay nada más que ejecutar, es que global esté terminado
     * y que en asíncronos no quede nada más por llegar. 
     * @return
     */
    public boolean completado(){
        return terminado;
    }

    //..........................................................................
    /** Establece el comportamiento de asíncronos (prioridad
     * @param frecuencia velocidad a la que llegan los mensajes asíncronos.
     * @param prioridadAntiguos  da prioridad a la llegada de los mensajes asíncronos más antiguos.
     */
    public void configurarAsincronos(int frecuencia, boolean prioridadAntiguos){
        asincronos.paramametros(frecuencia, prioridadAntiguos);
    }
    
    //..........................................................................
    /** Devuelve la frecuencia a la que estaba configurada la llegada de mensajes
     *  asíncronos.
     * @return
     */
    public int getFrecuencia(){
        return asincronos.getFrecuenciaLlegadas();    
    }
    
    //..........................................................................
    /**  Devuelve si se está dando prioridad a la llegada de los mensajes más an-
     *   tiguos.
     * @return
     */
    public boolean getPrioridadAntiguos(){
        return asincronos.getPrioridadAntiguos();
    }
    
    //..........................................................................
    private VectorOrdenado buscarEntidades(Object[] todos){
        VectorOrdenado entidades;
        int cuantasEntidades=0;
        
        for (int i=0; i<todos.length;i++)
            if  (todos[i] instanceof MyActor || todos[i] instanceof MyLifeLine)
                cuantasEntidades++;
        
        //Vector con todas las entidades ordenadas por coordenadas
        entidades = new VectorOrdenado(cuantasEntidades,'X');
         
        for (int i=0;i<todos.length;i++)
            if (todos[i] instanceof MyActor || todos[i] instanceof MyLifeLine)
                entidades.agregarMyObjects((MyObjects)todos[i]);
            
        return entidades;
    }
    
    //..........................................................................
    private VectorOrdenado buscarEventos(Object[] todos){
        VectorOrdenado eventos;
        int cuantosEventos=0;
        
        for (int i=0; i<todos.length;i++)
            if  (todos[i] instanceof MyMessage || todos[i] instanceof MyFrame)
                cuantosEventos++;
        
        //Vector con todos los eventos ordenados por coordenadas
        eventos = new VectorOrdenado(cuantosEventos,'Y');
         
        for (int i=0;i<todos.length;i++)
            if (todos[i] instanceof MyMessage || todos[i] instanceof MyFrame)
                eventos.agregarMyObjects((MyObjects)todos[i]);
        
        
        return eventos;
    }
}
