/*
 *  T-Mass simulator class
 *
 *  2004 - 2011 Frantisek Zboril jr.
 *  Faculty of Information Technology
 *  zborilf@fit.vutbr.cz
 *  http://www.fit.vutbr.cz/~zborilf
 *
 * (C) Brno University of Technology
 * Freeware licence VUT v Brně
 * https://wis.fit.vutbr.cz/FIT/db/dir.php/dr/VZ2007/externiInformace/softwareNaWebu/Priloha2_rozh23.pdf

 */

package jTMass.core.jTMassSimulator;

import jTGUI.TSimulatorInternalFrame;
import jTMass.TMass;
import jTMass.core.jAMass.TAgent;
import jTMass.core.jTMassModel.TACLElement;
import jTMass.core.jTMassModel.TModel;
import jTMass.core.jTMassModel.TModelConnection;
import jTMass.core.jTMassModel.TModelElement;
import jTMass.core.jTMassModel.jModelData.TALLLContent;
import jTMass.core.jTMassModel.jModelData.TSpeechAct;
import jTMass.core.jTMassPlatform.TPlatform;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendar;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendarEvent;

import java.util.LinkedList;
import javax.swing.JInternalFrame;

public class TSimulator extends TACLElement{

    // TODO simulace pri prvnim step skoci az za cas 2

    TModel PTMassModel;

    // ===== SIMULATION TIME =====
    private static final int _ST_Synchronous=0;
    private static final int _ST_Asynchronous=1;

    Long PElementaryTM=new Long(1);
    Long PModelTime=PElementaryTM;
    TCalendarEvent PRevokeEvent=null;

    int PSimulatorState=TModelElement._ESUninitialized;
    int PSimulationType=_ST_Synchronous;

    // ===== TERMINAL =====

    @Override
    public void printModelElement(){
        TMass._Terminal.printlnSystemInfo("Model element: Simulator");
        // TODO
    }

    // ===== GUI =====

    public void refreshElements(){
        TMass.getModel().resetElementList();
        TModelElement element=TMass.getModel().getElement();
        while(element!=null){
            element.refreshElementFrame();
            element=TMass.getModel().getElement();
        }
    }
    @Override
    public void refreshConnectionsPanel(){
        try{
            if(PInternalFrame!=null)
                ((TSimulatorInternalFrame)PInternalFrame).updateConnections();
        }catch(Exception e){
            TMass._Terminal.reportError("Updating simulator's connection frame");
        }
    }

    @Override
    public void refreshElementFrame(){
        refreshConnectionsPanel();
    }

    @Override
    public JInternalFrame createElementFrame(TMass tmass){
        PInternalFrame=new TSimulatorInternalFrame(this, tmass);
        return(PInternalFrame);
    }

    // ===== READERS / WRITERS =====

    @Override
    public String getTypeName(){
        return("Simulator");
    }

    public TCalendar getCalendar(){
        return(PCalendar);
    }

    public Long getModelTime(){
        return(PModelTime);
    }

    public void setModelTime(Long time){
        if(time>PModelTime)
            PModelTime=time;
    }

    // ===== ACL ACTS =====

    @Override
    protected TSpeechAct processRequest(TSpeechAct act){
        TALLLContent alllContent=(TALLLContent)act.getContent();
        String request=alllContent.getItemS(0);

        if(request.equalsIgnoreCase("wake_me")){
            int delay=alllContent.getItemI(1);
            TMass._Terminal.printlnDebug("PROCESSING SIM ACL / REQUEST WAKE ME "+delay);
            TCalendarEvent evt=new TCalendarEvent(TMass.getModel().getElementByName(act.getSender()),
                    TCalendarEvent._EventExecute, TMass.getSimulator().getModelTime(),
                    TMass.getSimulator().getModelTime()+delay,this);
            PCalendar.insertEvent(evt);
            return(null); 
        }

        TSpeechAct reply=generateNotUnderstood(act);
        return(reply);  
    }

    // ===== MODEL TIME / CLOCKS =====

    public void clockStep(){
        PModelTime+=PElementaryTM;
    }

    // ===== SIMULATOR BEHAVIOUR =====

    @Override
    public int setOutputs(){
        super.setOutputs();
        return(TModelElement._ESReady);
    };

    @Override
    public int evolveElement(){

        return(_ESReady);
    };

    @Override
    public int processInputs(){
        super.processInputs();
        return(TModelElement._ESReady);
    };

    // ===== SIMULATOR INITIALIZATION =====

    void initSynchronousSimulation(){
        LinkedList<TModelElement> elements=this.getConnectedElements();

        for(TModelElement element:elements){
            if(element instanceof TAgent)
                PCalendar.insertEvent(element,  TCalendarEvent._EventExecute,
                        this.getModelTime());

            if(element instanceof TPlatform)
                element.initElement(PCalendar);
        }
    }

    void initAsynchronousSimulation(){
        PSimulatorState=TModelElement._ESReady;
    }

    public void initSimulation(){
        if(PSimulationType==_ST_Synchronous)
            initSynchronousSimulation();
        else
            if(PSimulationType==_ST_Asynchronous)

        initAsynchronousSimulation();
        PSimulatorState=TModelElement._ESReady;
        TMass._Terminal.printlnSystemInfo("Simulator Initialized");
    }

    @Override
    public int initElement(TCalendar calendar){
        super.initElement(calendar);
        initSimulation();
        return(TModelElement._ESReady);
    }

    // ===== CONSTRUCTOR =====

    public TSimulator(String name, TModel model){
        super(name,0,0,true);
        int noe=model.getnoElements();
        PSimulatorState=TModelElement._ESUninitialized;
        PCalendar=new TCalendar();
	PType=TModelElement._Type_Simulator;
	TModelConnection connection;

	model.resetElementList();
        PnoInputGates=noe+1;
        PnoOutputGates=noe+1;
        TMass._Terminal.printlnSystemInfo("Connectiong Simulator to Universe");

        this.setInputGateTypes(1, this.getType());
        String conNameSpec=TModelConnection.generateConnectionName(this, 1, this, 1);
        connection=new TModelConnection
                (this,1, TModelElement._Type_Simulator, TModelElement._Type_Simulator, conNameSpec);
        POutputGates.put(1,connection);

        this.connectElement(this);
        for(int i=2;i<=noe+1;i++){
            TModelElement element=model.getElement();

            this.setInputGateTypes(i, element.getType());
            element.connectElement(this);
            String conName=TModelConnection.generateConnectionName(this, i, element, 1);
            connection=new TModelConnection(this,i,TModelElement._Type_Simulator, element.getType(), conName);
            POutputGates.put(i,connection);
            connectElement(element);
        }
    }
	
    public TSimulator(TModel model){
        this("Simulator", model);
    }

}