/*
 *  T-Mass Model element root 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.jTMassModel;

import java.util.Hashtable;
import jTMass.TMass;
import jTMass.core.jTMassModel.jModelData.TModelData;
import jTMass.core.jTMassSimulator.TSimulator;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendar;
import jTMass.core.jTMassSimulator.jTCalendar.TCalendarEvent;
import java.util.LinkedList;
import javax.swing.JInternalFrame;

public abstract class TModelElement 
{
    // MODEL ELEMENT TYPES
    public final static Integer _Type_ModelElement=0;
    public final static Integer _Type_Agent=1;
    public final static Integer _Type_Platform=2;
    public final static Integer _Type_Environment=3;
    public final static Integer _Type_Simulator=4;

    // ELEMENT STATES
    public static final int _ESNotAvailable=0;
    public static final int _ESTerminated=1;
    public static final int _ESUninitialized=2;
    public static final int _ESRunning=3;
    public static final int _ESReady=4;

    protected int PType;
    protected int PnoInputGates,PnoOutputGates;
    protected TCalendar PCalendar;
    protected JInternalFrame PInternalFrame;
    protected Hashtable<Integer,TModelConnection> PInputGates,POutputGates;
    protected Hashtable<Integer,Integer> PInputGateTypes; 
    protected LinkedList<TModelData> PInputDataBuffer, POutputDataBuffer;

    private String PName;
    protected Long PRunDelay=new Long(1);

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

    protected void printGates(){
        int slotType;
        TModelConnection con;

        TMass._Terminal.printlnSystemInfo("NIG="+PnoInputGates+" NOG="+PnoOutputGates);

        for(int i=1;i<=PnoOutputGates;i++){
            TMass._Terminal.printSystemInfo("Output gate "+i+" : ");
            con=getOutputConnection(i);
            if(con==null)
                TMass._Terminal.printlnSystemInfo("No connection for output gate "+i);
            else
                con.printConnection();
            }

        for(int i=1;i<=PnoInputGates;i++){
            TMass._Terminal.printSystemInfo("Input gate "+i+" : ");
            con=getInputConnection(i);
            if(con==null){
                slotType=PInputGateTypes.get(i);
                TMass._Terminal.printSystemInfo("No connection for a slot of type "+slotType);
            }
            else
                con.printConnection();
        }
    }

    public abstract void printModelElement();

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

    public int getType(){
        return(PType);
    }

    public int getPnoInputGates() {
        return(PnoInputGates);
    }

    public int getPnoOutputGates() {
        return(PnoOutputGates);
    }

    public abstract String getTypeName();

    public String getElementName(){
        return(PName);
    }

     // ===== GUI METHODS =====

     public abstract void refreshConnectionsPanel();

     public abstract void refreshElementFrame();

     public abstract JInternalFrame createElementFrame(TMass tmass);

    // ===== GATES METHODS =====

    protected boolean setInputGateTypes(int gate, int type){
        if(gate>PnoInputGates)
            return(false);
        PInputGateTypes.put(gate, type);
        return(true);
    }

    public Hashtable<Integer,TModelConnection> getOutputGates(){
        return(POutputGates);
    }

    // ===== CONNECTION SUPPORTING METHODS =====

    private TModelConnection findFreeOutputConnectionByType(TModelElement element){
        TModelConnection pConnection;

        for(int i=1;i<=PnoOutputGates;i++){
            pConnection=POutputGates.get(i);
            if((pConnection.getInputElement()==null)&&(pConnection.typeMatch(element)))
                return(pConnection);
	}
	return(null);
    }

    protected int findFreeInputGateByType(TModelElement element){
        int elementType=element.getType();

        for(int i=1;i<=PnoInputGates;i++){
            if(PInputGateTypes.get(i)==elementType)
                if(PInputGates.get(i)==null)
                    return(i);
        }
        return(-1);
    }


    public TModelConnection getInputConnection(int gate){
        if((gate>0)&&(gate<=PnoInputGates))
            return(PInputGates.get(new Integer(gate)));
	else
            return(null);
    }
	
    public TModelConnection getOutputConnection(int gate){
        if((gate>0)&&(gate<=PnoOutputGates))
            return(POutputGates.get(new Integer(gate)));
	else
            return(null);
    }
	
    public void setInputConnection(TModelConnection connection, int gate){
        if((gate>=0)&&(gate<=PnoInputGates)){
            PInputGates.put(new Integer(gate),connection);
            connection.setInputElement(this, gate);
        }
        else
            TMass._Terminal.reportError("Setting input connection");

        return;
    }
	
    public void connectElement(TModelElement element){
        int gate;
	TModelConnection connection=this.findFreeOutputConnectionByType(element);

        if (connection==null){
            TMass._Terminal.reportError("Connecting element:"+element.getElementName()+
                    " / no output gate");
            return;
	}

        gate=element.findFreeInputGateByType(this);
        if(gate==-1){
            TMass._Terminal.reportError("Connecting element:"+getElementName()+" / no input gate");
            return;
        }
        else
            element.setInputConnection(connection, gate);
            
        return;
    }
	
    public void disconnectElement(TModelElement element){
        // not implemented yet
	return;
    }
	
    public void disconnectAll(){
        // not implemented yet
        return;
    }

    // ===== CONNECTED ELEMENTS =====

    public LinkedList getConnectedElements(){
        Integer gate;
        LinkedList<TModelElement> elementList=new LinkedList<TModelElement>();

        for(int i=1;i<=PnoOutputGates;i++){
            gate=new Integer(i);
            if(POutputGates.get(gate)!=null)
                elementList.add(POutputGates.get(gate).getInputElement());
        }

        return(elementList);
    }

    public TModelConnection getConnectionToElementName(String elementName){
        TModelElement element;

        for(int i=1;i<=PnoOutputGates;i++){
            element=POutputGates.get(i).getInputElement();
            if(element!=null)
                if(element.getElementName().equalsIgnoreCase(elementName))
                    return(POutputGates.get(i));
        }
        return(null);
    }

    // ===== ELEMENT BEHAVIOUR SUPPOERING METHODS =====

    public void addOutputData(TModelData data){
        POutputDataBuffer.add(data);
    }

    public int waitEvent(){
        // may handle 'nothing to do' event here
        return _ESReady;
    };

    public boolean acceptableInput(TModelData data){
        return(true);
    }

   // ===== ELEMENT BEHAVIOUR =====

    public int setOutputs(){
        TModelConnection connection;

        for(TModelData data:POutputDataBuffer){
            connection=getConnectionToElementName(data.getReceiver());
            if(connection!=null){
                connection.setData(data);
                TMass._Terminal.printlnDebug("Data sent by: "+getElementName()+
                            " / data content: "+data.getContentS()+" / for: "+data.getReceiver());
                 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);
                    }
            }
        }

        PInputDataBuffer.clear();
        POutputDataBuffer.clear();
        return(_ESReady);
    }

    public abstract int evolveElement();

    public abstract int processInputs();

    public int readInputs(){
        TModelData data;
        TModelConnection connection;

        for(int i=1;i<=PnoInputGates;i++){
            connection=this.getInputConnection(i);
            if(connection!=null){
                data=connection.popData();
                while(data!=null){
                    if(acceptableInput(data)){
                        PInputDataBuffer.add(data);
                        TMass._Terminal.printlnDebug("Data stored for: "+getElementName()+
                            " / data content: "+data.getContentS());
                   }
                   data=connection.popData();
                }
                connection.setData(null);
            }
        }
        return(TModelElement._ESReady);
    }

    // ===== ELEMENT INITIALIZATION =====
	
    public int initElement(TCalendar calendar){
        PCalendar=calendar;
        PInputDataBuffer.clear();
        return(TModelElement._ESReady);
    }

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

    protected TModelElement(String name, int nig, int nog, boolean element){
        PType=TModelElement._Type_ModelElement;
        PName=name;
	PnoOutputGates=nog;
	PnoInputGates=nig;
        PInputGates=new Hashtable<Integer,TModelConnection>();
	POutputGates=new Hashtable<Integer,TModelConnection>();
        PInputGateTypes=new Hashtable<Integer,Integer>();
        PInputDataBuffer=new LinkedList<TModelData>();
        POutputDataBuffer=new LinkedList<TModelData>();

        for(int i=1;i<=PnoInputGates;i++)
            setInputGateTypes(i,_Type_ModelElement);
    }

}