/*
 *  Platform variable 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.jTMassPlatform;


import jTGUI.TPlatformsInternalFrame;
import jTMass.TMass;
import jTMass.core.jALLL.TALLL;
import jTMass.core.jALLL.jTObjects.TAtom;
import jTMass.core.jALLL.jTObjects.TFail;
import jTMass.core.jAMass.TAgent;
import jTMass.core.jTMassModel.TModelElement;
import jTMass.core.jTMassModel.TModelConnection;
import jTMass.core.jALLL.jTObjects.TList;
import jTMass.core.jALLL.jTObjects.TObject;
import jTMass.core.jAMass.TAgentEvent;
import jTMass.core.jTMassEnvironment.TEnvironment;
import jTMass.core.jTMassModel.TACLElement;
import jTMass.core.jTMassModel.jModelData.TALLLContent;
import jTMass.core.jTMassModel.jModelData.TModelData;
import jTMass.core.jTMassModel.jModelData.TSpeechAct;
import jTMass.core.jTMassPlatform.jTServices.TASAsynchronousService;
import jTMass.core.jTMassPlatform.jTServices.TASDeamon;
import jTMass.core.jTMassPlatform.jTServices.TAService;
import jTMass.core.jTMassSimulator.TSimulator;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendar;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendarEvent;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.util.Hashtable;

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

public class TPlatform extends TACLElement{

    public static final int _MaxSlots=10;

    private int PnoSlots;
    private int PnoEnvironments;
    private TPCU PTPCU;
    private LinkedList<TAService> PServiceHistory;
    private FileWriter PSwapFile;
    private final static int _HistorySize=140;

    private final static int _DefaultPlatformSlt=1;
    private final static int _DefaultPlatformEnv=1;


    // ===== CLONING =====

    public TPlatform clonePlatform(String platformName){
        TPlatform newPlatform=new TPlatform(platformName,PnoEnvironments,PnoSlots,true);
	newPlatform.getPCU().setVariables(getPCU().cloneVariables());
        newPlatform.getPCU().setServices(getPCU().cloneServices());
	return(newPlatform);
    }

    // ===== TERMINAL =====
		
    public void printPlatform(){
        TMass._Terminal.printlnSystemInfo("> PLATFORM STATE <"+getElementName());
        TMass._Terminal.printlnSystemInfo(getElementName()+":SERVICES");
	PTPCU.printServices();
	TMass._Terminal.printlnSystemInfo(getElementName()+":VARIABLES");
//	PTPCU.getVariables().printVariables(); TODO
        printGates();
    }

    @Override
    public void printModelElement(){
        printPlatform();
    }

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

    @Override
    public void refreshConnectionsPanel(){
        try{
            if(PInternalFrame!=null)
                ((TPlatformsInternalFrame)PInternalFrame).updateConnections();
        }catch(Exception e){
            TMass._Terminal.reportError("Updating platform's connection frame");
        }
    }

    public void refreshASPanel(){
        try{
            if(PInternalFrame!=null)
                    ((TPlatformsInternalFrame)PInternalFrame).setActiveServicesTable();
        }catch(Exception e){
            TMass._Terminal.reportError("Refreshing panel of platform's active services");
        }
    }

    public void refreshVRPanel(){
        try{
            if(PInternalFrame!=null)
                    ((TPlatformsInternalFrame)PInternalFrame).setVariablesTable();
        }catch(Exception e){
            TMass._Terminal.reportError("Refreshing panel of platform's variables");
        }
    }

    public void refreshHistory(){
        try{
            if(PInternalFrame!=null)
                    ((TPlatformsInternalFrame)PInternalFrame).refreshHistory();
        }catch(Exception e){
            TMass._Terminal.reportError("Refreshing history of platform's services");
        }
    }

    @Override
    public void refreshElementFrame(){
        refreshConnectionsPanel();
        refreshASPanel();
        refreshVRPanel();
        refreshHistory();
    }

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

    // ===== READERS =====

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

    public int getnoSlots(){
        return(PnoSlots);
    }

    public int getnoEnvironments(){
        return(PnoEnvironments);
    }

    public TPCU getPCU(){
        return(PTPCU);
    }

    public LinkedList<TModelData> getInputBuffer(){
        return(this.PInputDataBuffer);
    }

    public LinkedList<TModelData> getOutputBuffer(){
        return(this.POutputDataBuffer);
    }

    // ===== PLATFORM VARIABLES =====

    public Hashtable<String,TObject> getVariables(){
        return(PTPCU.getVariables());
    }

    public void addVariable(String variableName, TObject value){
        if(value!=null)
            TMass._Terminal.printlnDebug("Adding variable "+variableName+"="+value.getContent());
        else{
            TMass._Terminal.printlnDebug("Adding variable "+variableName);
            value=new TAtom("~null~");
        }
        
        PTPCU.addVariable(variableName, value);
    }

    public TObject getVariable(String variableName){
        return(PTPCU.getVariable(variableName));
    }

    // ===== PLATFORM SERVICES =====

    public void storeActiveService(TASAsynchronousService service){
        PTPCU.storeActiveService(service);
    }

    public void deleteActiveService(TASAsynchronousService service){
        PTPCU.deleteActiveService(service);
    }

    public LinkedList<TAService>getServiceHistory(){
        return(PServiceHistory);
    }

    public void serviceFinished(TAService service){
        TAService oldService;

        if(PServiceHistory.size()>_HistorySize){
            for(int i=0;i<(_HistorySize/2);i++){
                oldService=PServiceHistory.get(0);
                try{   
                    Long finishTime;
                    if(oldService instanceof TASAsynchronousService)
                        finishTime=((TASAsynchronousService)oldService).getFinishTime();
                    else
                        finishTime=oldService.getInitTime();

                    PSwapFile.append(oldService.getServiceName()+";"+oldService.getInitTime()+";"+
                        finishTime+";"+oldService.getClientName()+"\n");
                    PSwapFile.flush();
                }catch(Exception e){
                    TMass._Terminal.reportError(this.getElementName()+",writting to swap file");
                }
                PServiceHistory.remove(0);
            }
        }
        PServiceHistory.add(service);

        if(service instanceof TASDeamon)
            deleteActiveService((TASDeamon)service);
        if(PInternalFrame!=null)
            ((TPlatformsInternalFrame)PInternalFrame).refreshHistory();
    }

    public void registerService(String name, Constructor service){
        PTPCU.addService(name, service);
    }

    public TObject executeService(TList parameters){
	return(null);
    }

    public boolean serviceExists(String service_name){
	// TODO
        return(true);
    }

    public TAService getService(String service_name){
        return(PTPCU.getService(service_name));
    }

    // ===== RUN AGENT AND SET ITS REGISTER =====

    public boolean runandSet(String agentName, TObject message){
        TModelConnection con=this.getConnectionToElementName(agentName);
        
        TMass._Terminal.printlnDebug("Waking up agent: "+agentName);
        if(con==null)
            return(false);

        TModelElement agent=con.getInputElement();
        if(!(agent instanceof TAgent))
            return(false);

        con.setData(new
                TSpeechAct(TSpeechAct._SA_Request,getElementName(), agentName,
                TSpeechAct._CL_ALLL,"(wake_up)"));
        if(message!=null){
            if(message instanceof TFail)
                con.setData(new
                    TSpeechAct(TSpeechAct._SA_Failure,getElementName(), agentName,
                    TSpeechAct._CL_ALLL,"(FAIL)"));
            else
                con.setData(new
                    TSpeechAct(TSpeechAct._SA_Request,getElementName(), agentName,
                    TSpeechAct._CL_ALLL,"(set_register,"+message.getContent()+")"));
        }

        TMass.getSimulator().getCalendar().insertEvent(
                new TCalendarEvent(agent, TCalendarEvent._EventExecute,
                TMass.getSimulator().getModelTime(),
                TMass.getSimulator().getModelTime()+1,this));

        return(true);
    }

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

    @Override
    protected TSpeechAct processInformRef(TSpeechAct act){
        TALLLContent alllContent=(TALLLContent)act.getContent();
        String receiver, message;
        try{
            receiver=alllContent.getItemS(0);
            message=alllContent.getItemS(1);
        }catch (Exception e){
            TMass._Terminal.reportError("Wrong 'Inform ref' parameters: "+alllContent.getContent());
            return(null);
        }
        String answer="(";
        for(int i=2+this.PnoEnvironments;i<2+PnoEnvironments+PnoSlots;i++){
            TModelConnection con=getOutputConnection(i);
            if(con!=null)
                if(con.getInputElement()!=null){
                    if(!answer.contentEquals("("))
                        answer=answer+",";
                        answer=answer+con.getInputElement().getElementName();
            }
        }
        answer=answer+")";
        if(!(answer.contentEquals("()"))){
            TSpeechAct response=new TSpeechAct(TSpeechAct._SA_Inform, getElementName(), receiver,
                    TSpeechAct._CL_ALLL, answer, "", act.getReplyWith());
            return(response);
        }
        return(null);
    }

    @Override
    protected TSpeechAct processInform(TSpeechAct act){
        TALLLContent alllContent=(TALLLContent)act.getContent();
        String receiver, message;
        try{
            receiver=alllContent.getItemS(0);
            message=alllContent.getItemS(1);
        }catch (Exception e){
            TMass._Terminal.reportError("Wrong proxy parameters: "+alllContent.getContent());
            return(null);
        }

        // PROXY TO AN AGENT

        TModelConnection con=this.getConnectionToElementName(receiver);

        if(con!=null){
            TSpeechAct newAct=new TSpeechAct(TSpeechAct._SA_Inform,
                    this.getElementName(),con.getInputElement().getElementName(),
                        TSpeechAct._CL_ALLL,message);
            con.setData(newAct);
            TCalendarEvent ev;

            ev=new TCalendarEvent(con.getInputElement(),TCalendarEvent._EventReceiveInputs,
                      TMass.getSimulator().getModelTime(),TMass.getSimulator().getModelTime()+1,
                      this);
            TMass.getSimulator().getCalendar().insertEvent(ev);
        }

        else{
            if((TMass.getModel().getElementByName(act.getSender()) instanceof TAgent)||
                    (TMass.getModel().getElementByName(act.getSender())==this))
                for(int i=2;i<=PnoOutputGates;i++){
                    con=this.getOutputConnection(i);
                    if(con!=null)
                        if(con.getInputElement() instanceof TEnvironment){
                            act.setSender(getElementName());
                            act.setReceiver(TModelData._Broadcast);
                            con.setData(act);
                            TCalendarEvent ev;

                            ev=new TCalendarEvent(con.getInputElement(),
                                    TCalendarEvent._EventExecute,
                                    TMass.getSimulator().getModelTime(),
                                    TMass.getSimulator().getModelTime()+1,
                                    this);
                            TMass.getSimulator().getCalendar().insertEvent(ev);
                        }
                }
        }
        return(null);
    }

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

        if(request.equalsIgnoreCase("service"))
            try{
                String service=alllContent.getItemS(1);

                if(!(alllContent.getItem(2) instanceof TList)){
                    runandSet(act.getSender(),new TFail());
                    return(null);
                }

                String params=alllContent.getItemS(2);                                    
                TObject alllParams=TALLL.parseALLLData(params);

                TMass._Terminal.printlnDebug("Executing service: "+service+
                        " / with params: "+alllParams.getContent()+" / for: "+act.getSender());
                TObject results=PTPCU.runService(service, (TList)alllParams, act.getSender(), this);

            }catch(Exception e){
                TMass._Terminal.reportError("Service execution failed");
                runandSet(act.getSender(),new TFail());
        }
        return(null);
    }

    // ===== PROCESSING SPEECH ACTS =====

    @Override
    protected TSpeechAct processSpeechAct(TSpeechAct speechAct){
        // check services handles

        return(super.processSpeechAct(speechAct));
    }

    // ===== PLATFORM BEHAVIOUR =====

    void setToEnvironments(TModelData message){
        for(int i=2; i<2+getnoEnvironments(); i++){
            TModelConnection connection=getOutputConnection(i);
            connection.setData(message);
            Long actTime=TMass.getSimulator().getModelTime();
            Long exeTime=actTime+PRunDelay;
            if(!(connection.getInputElement() instanceof TSimulator)){
                TCalendarEvent newEvent=new TCalendarEvent(connection.getInputElement(),
                            TCalendarEvent._EventExecute, actTime, exeTime, this);
                            TMass.getSimulator().getCalendar().insertEvent(newEvent);
            }
        }
    }

    @Override
    public int setOutputs(){
        for(TModelData message:POutputDataBuffer){
            if(TMass.getModel().getElementByName(message.getReceiver()) instanceof TPlatform)
                setToEnvironments(message);
            else{
                TModelConnection connection=this.getConnectionToElementName(message.getReceiver());
                if(connection!=null){
                    connection.setData(message);
                    Long actTime=TMass.getSimulator().getModelTime();
                    Long exeTime=actTime+PRunDelay;
                    if(!(connection.getInputElement() instanceof TSimulator)){
                    TCalendarEvent newEvent=new TCalendarEvent(connection.getInputElement(),
                                TCalendarEvent._EventExecute, actTime, exeTime, this);
                                TMass.getSimulator().getCalendar().insertEvent(newEvent);
                    }
            }
        }
        }
        POutputDataBuffer.clear();
        PInputDataBuffer.clear();
        return(TModelElement._ESReady);
    };

    @Override
    public int evolveElement(){
        PTPCU.runActiveServices();
        // odpovedet NOU na neodpovezene zpravy pro sluzby (s handlem) - bude buffer zprav?
        // answerSMB();
        return(TModelElement._ESReady);
    };

    @Override
    public int processInputs(){
       for(TModelData data:PInputDataBuffer)
            if(data instanceof TSpeechAct)
                if(!((TSpeechAct)data).getInReplyTo().contentEquals(""))
                    PTPCU.storeMessage((TSpeechAct)data); 
       
        super.processInputs();
        return(TModelElement._ESReady);
    };

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

    // ===== PLATFORM INITIALIZATION =====

    @Override
    public int initElement(TCalendar calendar){
        LinkedList<TModelElement> elements=this.getConnectedElements();
        String services=PTPCU.getServicesS();
        String alllcont="(services,"+getElementName()+",("+services+"))";

        // Inform agents about available services
        for(TModelElement element:elements){
            if(element instanceof TAgent){
                TSpeechAct act=new TSpeechAct(TSpeechAct._SA_Propose,getElementName(),
                        element.getElementName(),TSpeechAct._CL_ALLL,alllcont);
                this.getConnectionToElementName(element.getElementName()).setData(act);
            }
        }
        return(TModelElement._ESReady);
    }

    //  ===== CONSTRUCTORS =====
	
    public TPlatform(String name, int environments, int slots, boolean element){

        super(name, environments+slots+1,environments+slots+1, element);

        try{
            PSwapFile=new FileWriter(TMass.getModelPath()+File.separator+"swap"+
                    File.separator+"."+name+"history.swh");
        }catch(Exception e){
            TMass._Terminal.reportError("Cannot create platform swap file");
            PSwapFile=null;
        }

        int i;
        String platformConName;
	PType=TModelElement._Type_Platform;
	PnoSlots=slots;
	PnoEnvironments=environments;
	PTPCU=new TPCU(this);
        PServiceHistory=new LinkedList<TAService>();

	platformConName=TModelConnection.generateConnectionName(this, 1, null, -1);		
	POutputGates.put(1,new TModelConnection
                (this,1,TModelElement._Type_Platform,TModelElement._Type_Simulator,platformConName));
	setInputGateTypes(1,_Type_Simulator);
        for(i=2;i<environments+2;i++){
            platformConName=TModelConnection.generateConnectionName(this, i, null, -1);
            POutputGates.put(i,new TModelConnection
			(this,i,TModelElement._Type_Platform,TModelElement._Type_Environment,
                        platformConName));
            setInputGateTypes(i,_Type_Environment);
        }

        for(i=environments+2;i<environments+2+slots;i=i+1){
            platformConName=TModelConnection.generateConnectionName(this, i, null, -1);
            POutputGates.put(i,new
				TModelConnection(this,i,TModelElement._Type_Platform,
                                TModelElement._Type_Agent, platformConName));
            setInputGateTypes(i,_Type_Agent);
        }
    }
		
    public TPlatform(boolean element){
        this("Platform",_DefaultPlatformSlt,_DefaultPlatformEnv,element);
    }
	
}