/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package sdsimulator.runnable;
import java.util.*;
import myuml.*;
/**
 *
 * @author Miguel Herreros
 */
public class FrameEjecutable extends SecuenciaComponentesEjecutables implements ComponenteEjecutable{
    //Referencia a su análogo dibujable.
    protected MyFrame myFrame;
    //Cuando el frame está contenido dentro de otro, contiene el número de bloque
    //del frame padre en el que está contenido. En otro caso, contiene -1.
    protected int bloque;
    //Indica si, visto como ComponenteEjecutable, tiene permiso para ejecutarse.
    protected boolean permitido;
    
    //..........................................................................
    /** Constructor 
     * @param myFrame: dibujo del diagrama que corresponde a este frame.
     * @param eventos: todos los mensajes y frames que quedan (ordenados), localizará y eliminará todos los contenidos en este frame.
     * @param contexto: todas las variables del diagrama.
     * @param asincronos: para guardar los mensajes asíncronos que se envían, pero quedan pendientes de llegar a su destino.
     */
    public FrameEjecutable(MyFrame myFrame, VectorOrdenado eventos, ContextoEjecucion contexto, AsincronosPendientes asincronos){   
        super(contexto, asincronos);
        
        this.myFrame    = myFrame;
        bloque          = -1;
        permitido       = true;
        
        agregarComponentesEjecutables(eventos, contexto);
        
        detectarBloques();
    }
    
    //..........................................................................
    /** Extrae los componentesEjecutables contenidos en el frame del vector eventos
     *  y los deja dentro del frame en cuestión.
     *  HEREDADO de SecuenciaComponentesEjecutables.
     * @param pos
     * @param eventos
     * @param contexto
     */
     protected void agregarComponentesEjecutables(VectorOrdenado eventos, ContextoEjecucion contexto){
         int i = posicionarse(eventos);
        
         while (true){
             //si no quedan más componentes salimos
             if (i>=eventos.size()) break;
            
             MyObjects myObject = (MyObjects)eventos.get(i);
             if (dentro(myObject)){
                 if (myObject instanceof MyMessage){
                     MyMessage myMessage=(MyMessage)myObject;
                     componentesEjecutables.add(new MensajeEjecutable(myMessage, contexto));
                 }
                 else{
                     MyFrame myFrameAnidado = (MyFrame)eventos.get(i);
                     agregarFrame(myFrameAnidado, eventos, contexto, asincronos);
                 }
                
                 eventos.remove(i); 
                 //¡¡¡¡ y no incrementamos i para leer el siguiente porque borramos el leído !!!!
             }
             else//No está dentro. Sabemos que está ordenado y que partíamos desde
                 //el frame en cuestión. En el momento en el que se lea uno que 
                 //no está dentro, ninguno más lo estará.
                 break;
         }
    }
   
    //..........................................................................
    /**se salta todos los previos al frame que estamos creando (y el propio frame)
     * @param eventos
     * @return
     */
    private int posicionarse(VectorOrdenado eventos){
        int i = 0;
        while (true){
            if (eventos.get(i) instanceof MyFrame)
                 if ((MyFrame)eventos.get(i)== myFrame){
                     i++; 
                     break;
                 }
            i++;
        }
        return i;
    }
    
    //..........................................................................
    /**Marca los componentes ejecutables contenidos en este frame según el bloque
     * en el que se encuentre (recordar que los bloques quedan delimitados por 
     * las líneas internas del frame.
     */
    private void detectarBloques(){
        int    yFrame            = myFrame.getXY().getY(); //Para sumárselo a la posición de los separadores, ya que éstos se dibujan en posición
                                                           //relativa al frame que los contiene.
        int    indiceSeparadores = 0;
        int    indiceBloques     = 0;
        int    separadorActual   = 0 + yFrame;
  
        if (!myFrame.separadores.isEmpty()) separadorActual = (Integer) myFrame.separadores.get(0) + yFrame;
               
        for (int i=0; i<componentesEjecutables.size(); i++){
            
            ComponenteEjecutable componenteEjecutableActual = (ComponenteEjecutable) componentesEjecutables.get(i);
            
            if (componenteEjecutableActual.getY() <= separadorActual || indiceSeparadores>=myFrame.separadores.size()) 
                //Está en el bloque de los menores al separador actual.
                componenteEjecutableActual.setBloque(indiceBloques);
                   
            else{ //Buscamos el siguiente separador que vaya detrás del objeto actual. Puede ser que nos quedemos sin separadores.             
                while(componenteEjecutableActual.getY() > separadorActual){
                    indiceSeparadores++;
                    indiceBloques++; //Cambiar de separador implica cambiar de bloque.
                    
                    if  (indiceSeparadores >= myFrame.separadores.size()) 
                        break;
                    
                    separadorActual = (Integer) myFrame.separadores.get(indiceSeparadores) + yFrame;
                }
                
                componenteEjecutableActual.setBloque(indiceBloques); //Si se encontró un separador mayor, irán al bloque que delimita.
                                                                     //Si se acabaron los separadores, el resto irán al último bloque.
            }
        }
    }
    
    //..........................................................................
    /** Heredado de ComponenteEjecutable. Ejecuta y devuelve el resultado de 
     *  runStep.
     * @return
     */
    public ResultadoEjecucion run(){
        return runStep();   
    }
    
    //..........................................................................
    /** Ejecuta un paso dentro de este frame. El comportamiento por defecto es 
     * ejecutar todos los componentes contenidos en el frame, no obstante, las 
     * clases descendientes redefinirán uno o más de estos casos con el fin de 
     * modificar este comportamiento general.
     * HEREDADO de SecuenciaComponentesEjecutables
     * @return: ResultadoEjecución, con el texto que indica qué se ha hecho den-
     * tro del frame (ejecución del paso) y devuelve las coordenadas del objeto
     * del frame que se ha hecho ejecutar.
     */
    @Override
     public ResultadoEjecucion runStep(){
         if (indiceEjecucion==0)
             myFrame.setCol(sdsimulator.Main.PENDIENTE);
        return super.runStep();
    }
    
    //..........................................................................
    /**Realiza las tareas iniciales, cuando se empieza a ejecutar el componente.
     * HEREDADO de SecuenciaComponentesEjecutables
     */
    protected ResultadoEjecucion detectadoComienzo(){
        ResultadoEjecucion retorno=new ResultadoEjecucion();
        retorno.x=myFrame.getXY().getX();
        retorno.y=myFrame.getXY().getY();
        myFrame.setCol(sdsimulator.Main.EN_CURSO);
        indiceEjecucion++;
        retorno.traza = bundle.getString("FRAME");
        retorno.traza+= myFrame.getName();
        retorno.traza+= bundle.getString("FRAME1");
        retorno.traza+= myFrame.getID();
        retorno.traza+= bundle.getString("FRAME2");
        
        return retorno;
    }
    //..........................................................................
    /** Se encarga de ejecutar todos los pasos de un frame, salvo el primero.
     *  Devuelve el resultado de la ejecución del paso. El último paso del frame
     * también tiene un tratamiento especial.
     * HEREDADO de SecuenciaComponentesEjecutables
     * @return
     */
    protected ResultadoEjecucion restoPasos(){
        ComponenteEjecutable componenteEjecutable;
        ResultadoEjecucion retorno;
        
        if (indiceEjecucion>=componentesEjecutables.size())
            return detectadoFinal();
           
        componenteEjecutable=(ComponenteEjecutable) componentesEjecutables.get(indiceEjecucion);
        if (componenteEjecutable.getPermitido()) componenteEjecutable.postRun(); //Si es la primera vez, al ejecutarse Run, queda del color correcto
        
        if (componenteEjecutable.completado() || !componenteEjecutable.getPermitido()){ 
            while (true){ 
               indiceEjecucion++;
               
               if (indiceEjecucion>=componentesEjecutables.size())
                   return detectadoFinal();
               
               componenteEjecutable=(ComponenteEjecutable) componentesEjecutables.get(indiceEjecucion);
               
               if (componenteEjecutable.getPermitido())
                   break;
               else 
                   continue;
            }
        }
       
        retorno = componenteEjecutable.run();
        retorno.traza = dentroDe(componenteEjecutable) + retorno.traza;
        asincronos.add(componenteEjecutable);//no se añadirá si no es un mensaje asíncrono.
        return retorno;
    }
    
    //..........................................................................
    /** Añade a la traza el frame en el que está contenido el componente ejecutable
     * en curso.
     * @param ejecutado
     * @return
     */
    protected String dentroDe(ComponenteEjecutable ejecutado){
        String retorno = bundle.getString("DENTRO_DE")  + ejecutado.getBloque();
               retorno+= bundle.getString("DENTRO_DE1") + myFrame.getName();
               retorno+= bundle.getString("DENTRO_DE2") + myFrame.getID() + bundle.getString("DENTRO_DE3");
        return retorno;
    }
    
    //..........................................................................
    /** Paso especial del frame, generado al detectar que no quedan más pasos para
     * ejecutar dentro del frame en cuestión.
     * HEREDADO de SecuenciaComponentesEjecutables
     * @return
     */
    protected ResultadoEjecucion detectadoFinal(){
        ResultadoEjecucion retorno = new ResultadoEjecucion();
        retorno.traza = bundle.getString("FRAME3");
        retorno.traza+= myFrame.getName();
        retorno.traza+= bundle.getString("FRAME1");
        retorno.traza+= myFrame.getID();
        retorno.traza+= bundle.getString("FRAME4");

        retorno.x=myFrame.getXY().getX() + myFrame.getXY().getWidth();
        retorno.y=myFrame.getXY().getY() + myFrame.getXY().getHeight();
        
        myFrame.setCol(sdsimulator.Main.EN_CURSO);
        
        terminado=true;
        
        return retorno;
    }
    
    //..........................................................................
    /**Cuando se termina el frame, debe poner el color que indica que éste se ha 
     * ejecutado el frame.
     * HEREDADO tanto de ComponenteEjecutable como de SecuenciaComponentesEjecutables
     */
    public void postRun(){
        if (terminado) 
            myFrame.setCol(sdsimulator.Main.COMPLETO);
    }
    
    //..........................................................................
    /**Deja listo el frame para una nueva ejecución.
     * HEREDADO tanto de ComponenteEjecutable como de SecuenciaComponentesEjecutables
     */
    @Override
    public void reset(){
        super.reset();
        myFrame.setCol(sdsimulator.Main.NUEVO);
        permitido = true;
    }
    
    //..........................................................................
    /** Si este frame está contenido dentro de otro, devuelve el bloque del frame
     * padre dentro del cual éste está contenido.
     * HEREDADO de ComponenteEjecutable
     * @return Bloque en el que está contenido dentro del frame padre (-1 si no está dentro de ningún frame)
     */
    public int getBloque(){
        return bloque;    
    }
    
    //..........................................................................
    /**Será invocado por un frame cuando este esté contenido dentro de él. Esta-
     * blece el nº de bloque del frame padre donde está contenido.
     * HEREDADO de ComponenteEjecutable
     * @param bloque
     */
    public void setBloque(int bloque){
        this.bloque = bloque;   
    }
   
    //..........................................................................
    /**Para ser invocado cuando se desee saber si el componente tiene permiso para
     * ejecutarse o no
     * HEREDADO de ComponenteEjecutable
     * @return true si está permitido ejecutar el componente, false en otro caso.
     */
    public boolean getPermitido(){
        return this.permitido;
    }
   
    //..........................................................................
    /**Para ser invocado cuando se quiera prohibir o permitir la ejecución de este
     * componente ejecutable.
     * HEREDADO de ComponenteEjecutable 
     * @param permitido
     */
    public void setPermitido(boolean permitido){
        this.permitido = permitido;
    }
    
    //..........................................................................
    /**Devuelve el número de bloques detectados dentro del frame.
     * @return número de bloques.
     */
    public int numBloques(){
        return myFrame.separadores.size() + 1;
    }
    
    //..........................................................................
    /**Pone a falso el permiso de ejecución para todos los componentes ejecutables
     * contenidos el el bloque.
     * @param numBloque: Número de bloque que se desea anular.
     */
     public void anularBloque(int numBloque){
         permisosBloque(numBloque,false);
     }
      
    //..........................................................................
    /**Pone a true el permiso de ejecución para todos los componentes ejecutables
     * contenidos el el bloque.
     * @param numBloque: Número de bloque que se desea anular.
     */
     public void activarBloque(int numBloque){
         permisosBloque(numBloque,true);  
     }
      
    //........................................................................
    /**Pone el permiso de ejecución al valor indicado para todos los 
    * componentes contenidos el el bloque.
    * @param numBloque Número de bloque sobre cuyos permisos de ejecución se quiere actuar.
    * @param permiso   Permiso que se quiere poner (true para ejecutar, false para no ejecutar)
    */
    private void permisosBloque(int numBloque, boolean permiso){
        ComponenteEjecutable componenteEjecutableActual;
        int bloqueActual;
          
        for (int i=0; i<componentesEjecutables.size(); i++){
            componenteEjecutableActual = (ComponenteEjecutable)componentesEjecutables.get(i);
            bloqueActual=componenteEjecutableActual.getBloque();
            if (bloqueActual==numBloque)
                componenteEjecutableActual.setPermitido(permiso);
            else if (bloqueActual > numBloque)
                break;
        }
    } 
      
    //..........................................................................
    /**Impide la ejecución de todos los componentes del frame.
    */
    public void anularTodos(){
        permisosTodos(false);
     }
     
    //..........................................................................
    /**Da permiso de ejecución a todos los componentes del frame.
    */
    public void activarTodos(){
        permisosTodos(true);
    }
     
    //..........................................................................
    /**Establece el mismo permiso de ejecución para todos los componentes conte-
     * tenidos en el diagrama. El permiso a establecer, se pasa como parámetro.
     * @param valor
     */
    private void permisosTodos(boolean valor){
        for (int i=0; i<componentesEjecutables.size(); i++)
             ((ComponenteEjecutable)componentesEjecutables.get(i)).setPermitido(valor);
    }
     
    //..........................................................................
    /**Devuelve un vector en el que cada una de sus ocurrencias es a su vez un
     * vector que contiene las referencias a todos los componentes Ejecutables 
     * contenidos en un bloque.
     * @return
     */
    public Vector bloquesEjecutables(){
        Vector retorno           = new Vector(50,10);
        Vector bloqueComponentes;
        ComponenteEjecutable componenteEjecutableActual;
        int numBloque=0;
         
        for (int i=0; i<numBloques(); i++){
            bloqueComponentes = new Vector(10,10);
            retorno.add(bloqueComponentes);
        }
         
        bloqueComponentes = (Vector) retorno.get(0);
        for (int i=0; i<componentesEjecutables.size(); i++){
            componenteEjecutableActual = (ComponenteEjecutable)componentesEjecutables.get(i);
           
            if (componenteEjecutableActual.getBloque()!=numBloque){
                numBloque=componenteEjecutableActual.getBloque();
                bloqueComponentes = (Vector) retorno.get(numBloque); 
            }
              
            bloqueComponentes.add(componenteEjecutableActual);
        } 
          
        return retorno;
    }
     
    //......................................................................
    /** Devuelve verdadero si el MyObject que se le pasa como parámetro está con-
     * tenido dentro del myFrame asociado a este objeto.
     * 
     * @param myObject
     * @return
     */
    private boolean dentro(MyObjects myObject){
       int limiteSuperior = myFrame.getXY().getY();
       int limiteInferior = limiteSuperior + myFrame.getXY().getHeight();
       int y              = myObject.getXY().getY();
       
       if (y > limiteSuperior && y < limiteInferior)
           return true;
       else
           return false;
    }
    
    //..........................................................................
    /** Devuelve la coordenada X donde está ubicado el dibujo de este frame.
     * @return
     */
    public int getX(){
        return myFrame.getXY().getX();
    }        
    
    //..........................................................................
    /** Devuelve la coordenada Y donde está ubicado el dibujo de este frame.
     * @return
     */
    public int getY(){
        return myFrame.getXY().getY();
    }
}