/*
 *  ALLL agent 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.jAMass;

import jTGUI.TAgentInternalFrame;
import jTMass.TMass;
import jTMass.TTerminal;
import jTMass.core.jAMass.jActions.TAction;
import jTMass.core.jTMassModel.TModelConnection;
import jTMass.core.jTMassModel.TModelElement;
import jTMass.core.jALLL.jTObjects.*;
import jTMass.core.jAMass.jActions.TEOP;
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.jTMassSimulator.jTCalendar.TCalendarEvent;
import java.awt.Color;
import java.io.File;
import java.io.FileWriter;

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


public class TAgent extends TACLElement{
	   
    private static int PAgentsCount=1;
    private int PAgentState=_Agent_Ready;
    private TPlan PPlan;
    private TInputBuffer PInputBuffer;
    private TBeliefBase PBeliefBase;
    private int PPlanLevel=1;
    private FileWriter PSwapFile;
    private final static int _HistorySize=400;

    public TBBCU PBBCU;
    public TRegisters PRegisters;
	
    public final static int _Agent_Ready=1;
    public final static int _Agent_Roused=2;
    public final static int _Agent_Runs=3;
    public final static int _Agent_Stopped=4;
    public final static int _Agent_Suspended=5;
    public final static int _Agent_Frozen=6;
    public final static int _Agent_Deaf=7;
    public final static int _Agent_Finished=8;

    final static int _DefaultAgentOC=3;
    final static int _DefaultAgentIC=3;

    private Long PRunDelay=new Long(1);
    private LinkedList<TAgentEvent> PActivityHistory;

    // ===== STAT. DATA =====

    private Integer PSDMaxSize=0;

    // ===== AGENT CLONING =====

    public TAgent cloneAgent(String cloneName){
	TObject object;
	TAgent newAgent=new TAgent(cloneName, true);

        newAgent.adoptPlan((TPlan)PPlan.cloneObject());
	newAgent.getInputBuffer().setDatabase(PInputBuffer.getDatabaseClone());
	newAgent.getBeliefBase().setDatabase(PBeliefBase.getDatabaseClone());
	for(int i=1; i<=TConstants._NORegisters; i++){
            object=getRegister(i);
            newAgent.setRegister(i,object);
	}
	return(newAgent);
    }

    // ===== TERMINAL METHODS =====

    public void printPlan(){
	TMass._Terminal.printlnSystemInfo(getElementName()+":INTENTION\n"+PPlan.getContent());
    }

    public void printBeliefBase(){
	TMass._Terminal.printlnSystemInfo(getElementName()+":Belief BASE");
	PBeliefBase.printBeliefBase();
    }
	
    void printInputBuffer(){
	TMass._Terminal.printlnSystemInfo(getElementName()+":INPUT BUFFER");
	PInputBuffer.printInputBuffer();
    }
	
    void printRegisters(){
	TMass._Terminal.printlnSystemInfo(getElementName()+":REGISTERS");
	PRegisters.printRegisters();
    }

    public void printAgent(){
	TMass._Terminal.printlnSystemInfo("> AGENT STATE <"+getElementName());
	printPlan();
	printBeliefBase();
	printInputBuffer();
	printRegisters();
        printGates();
    }

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

    // ===== AGENT SIZE =====
    
    public int getAgentSize(){
        int size=0;
        size=PPlan.getContent().length() ;
        size=size+PBeliefBase.getSize();
        size=size+PInputBuffer.getSize();
        size=size+PRegisters.getSize();

        return(size);
    }

    public void checkMaxSize(){
        int size=getAgentSize();
            if(size>PSDMaxSize)
        PSDMaxSize=size;
    }

    // ===== GUI SUPPORT =====

    public Color getStateColor(){
         switch (PAgentState){
            case TAgent._Agent_Finished:
                    return(Color.BLACK);
            case TAgent._Agent_Frozen:
                    return(Color.BLUE);
            case TAgent._Agent_Deaf:
                    return(Color.RED);
            case TAgent._Agent_Ready:
                    return(Color.LIGHT_GRAY);
            case TAgent._Agent_Roused:
                    return(Color.YELLOW);
            case TAgent._Agent_Runs:
                    return(Color.GREEN);
            case TAgent._Agent_Stopped:
                    return(Color.ORANGE);
            case TAgent._Agent_Suspended:
                    return(Color.gray);
        }
        return(null);
    }

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

    @Override
    public void refreshElementFrame(){
    //    if(!TMass.simulationRunning()){
            refreshConnectionsPanel();
            try{
                if(PInternalFrame!=null){
                    PInternalFrame.setTitle(this.getElementName()+" (act:"+getAgentSize()+
                            "B, max:"+PSDMaxSize+"B)");
                    ((TAgentInternalFrame)PInternalFrame).refreshHistory();
                }
            }catch(Exception e){
                TMass._Terminal.reportError("Refreshing agent's frames");
            }
    //    }
    }

    @Override
    public JInternalFrame createElementFrame(TMass tmass){
        PInternalFrame=new TAgentInternalFrame(this, tmass);
        refreshElementFrame();
        return(PInternalFrame);
    }
	
    // ===== READERS, WRITERS =====

    @Override
    public String getTypeName(){
    	return("Agent");
    }
	
    public int getAgentState(){
	return(PAgentState);
    }

    public int getActiveRegIndex(){
        return(PRegisters.getActiveRegIndex());
    }

    public void setAgentState(int state){
        PAgentState=state;
    }

    // ===== AGENT ACTIVITY HISTORY =====

    public LinkedList<TAgentEvent> getActivityHistory(){
        return(PActivityHistory);
    }

    void storeActionActivity(TAgentEvent event){
        TAgentEvent oldEvent;

        if(PActivityHistory.size()>_HistorySize){
            for(int i=0;i<(_HistorySize/2);i++){
                oldEvent=PActivityHistory.get(0);
                try{
                    PSwapFile.append(oldEvent.getActivityName()+";"+oldEvent.getExecTime()+";"+
                        oldEvent.getParams()+";"+oldEvent.getResult()+"\n");
                    PSwapFile.flush();
                }catch(Exception e){
                    TMass._Terminal.reportError(this.getElementName()+",writting to swap file");
                }
                PActivityHistory.remove(0);
            }
        }
            
        PActivityHistory.add(event);
    }

    void storeActionActivity(TAction action, TObject actionResult){
        String actionResultS="";
        if(actionResult!=null)
            actionResultS=actionResult.getContent();
        storeActionActivity(new TAgentEvent("@"+action.getActionName(),action.getParamsS(),actionResultS));
    }

    // ===== PLAN ACCESS METHODS =====

    public String getPlan(){
        return(PPlan.getContent());
    }

    public boolean noPlan(){
	if(PPlan==null)return(true);
	return(PPlan.isEmpty());
    }

    public void erasePlan(){
	PPlan=null;
    }
	
    public TAction popAction(){
	return(PPlan.popAction());
    }

    public void adoptPlan(TPlan plan){
    	PPlan=plan;
    }

    public void increasePlanLevel(){
        PPlanLevel++;
    }

    public void decreasePlanLevel(){
        PPlanLevel--;
    }

    public void addPlan(TPlan plan, boolean substitute){
        if(plan.size()==2){
            String plan_name=(plan.get(0)).getContent();
            if(substitute)
                PBeliefBase.storePlan(plan_name,(TPlan)plan.get(1).substituteRegisters(this));
            else
                PBeliefBase.storePlan(plan_name,(TPlan)plan.get(1));
	}
	else
            TMass._Terminal.reportError("Attempt to add an invalid plan");
    }

    // ===== SUBPLANS =====

    public boolean expandByName(String plan_name){
	TPlan plan=(TPlan)(PBeliefBase.getPlan(plan_name));
	if(plan!=null)
            return(expandPlan(plan));

	TMass._Terminal.reportError("Unknown plan");
	return(false);
    }

    public boolean expandPlan(TPlan plan){
	if(PPlan==null){
            TMass._Terminal.reportError("No plan to expand");
            return(false);
	}
        PPlan.expandPlan((TPlan)plan,this);
        return(true);
    }

    // ===== BELIEF BASE ACCESS METHODS =====
	
    public Iterator getBeliefBaseIterator() {
        return PBeliefBase.getBeliefBaseIterator();
    }
    
    public Object getBeliefBaseValue(Object key) {
        return PBeliefBase.getBeliefBaseValue(key);
    }

    public void addBelief(TList list){
        PBeliefBase.storeBelief(list);
        return;
    }

    public boolean deleteBelief(TList list){
        if(!(processQuery(list) instanceof TFail)){
            PBeliefBase.removeBelief(list);
            return(true);
	}
	else
            return(false);
    }

    public TBeliefBase getBeliefBase(){
	return(PBeliefBase);
    }

    public String getBeliefBaseContent(){
        return(PBeliefBase.getContent());
    }

    public TBBCU getKBCU(){
        return(PBBCU);
    }

    public TObject processList(TList list){
        return(PBBCU.processList(list,PRegisters.getRegister()));
    }

    public TObject processQuery(TList query){
        TList list=PBeliefBase.unifyBelief(query);
	if(list!=null){
                list.matureRegisters();
                return(list);   // list.get(0) vrati jen jeden prvek z BB - ktera semantika je lepsi?
	}
	return(new TFail());
    }

    // ===== INPUT BUFFER ACCESS METHODS =====

    public TInputBuffer getInputBuffer(){
	return(PInputBuffer);
    }
    
    public void clearBuffer(){
		PInputBuffer.clearBuffer();
    }
	
    public void storeInput(TAgent agent, TList list){
	TMass._Terminal.printDebug(TTerminal._InterpreterMessage,(getElementName())+":");
	PInputBuffer.receiveMessage(agent.getElementName(),list);
    }
	
    public TObject getInput(TAtom ID){
	if(ID.getType()==TConstants._AnonymousVariable){
		return(PInputBuffer.getMessage());
	}
        else
            return(PInputBuffer.getMessageFrom(ID.getContent()));
    }

    // ===== REGISTERS =====
	 
    public TObject getRegister(){
	return(PRegisters.getRegister());
    }

    public TObject getRegister(int nor){
	if(nor==0)
            return(PRegisters.getRegister());
	else
        if(nor<=TConstants._NORegisters)
            return(PRegisters.getRegister(nor));
        else
        switch(nor){
            case TConstants._NORegisters+1: return(new TAtom(String.valueOf(PPlanLevel)));
            case TConstants._NORegisters+2: return(new TAtom(getElementName()));
            default: return(new TFail());
        }
    }

    public void setRegister(TObject value){
        if (value instanceof TFail){
            actionFailed();
            PRegisters.setRegister(new TFalse());
            return;
        }
        PRegisters.setRegister(value);
	return;
    }

    public void setRegister(int nor, TObject value){
        if (value instanceof TFail){
            actionFailed();
            PRegisters.setRegister(new TFalse());
            return;
        }
        PRegisters.setRegister(nor, value);
	return;
    }

    public int switchRegister(int regNo){
	return(PRegisters.switchRegister(regNo));
    }

    // ===== ACTION EXECUTION =====

    TObject actionFailed(){
        while(!noPlan()){
            if(popAction() instanceof TEOP){
                decreasePlanLevel();
                break;
            }
        }
        return(new TFalse());
    }

    public void executeAction(){
        TAction action;
        TObject actionResult;

        if(!noPlan()){
            action=popAction();
            if(action==null)
                TMass._Terminal.reportError(this.getElementName()+":Executing wrong action ... ignored");
            else{
		TMass._Terminal.printlnDebug(getElementName()+":Executing action "+action.getContent());
                actionResult=action.execute(this);
                storeActionActivity(action,actionResult);
		if(actionResult!=null){
                    TMass._Terminal.printlnDebug("REGISTER:"+getElementName()+actionResult.getContent());
                    setRegister(actionResult);
                }
		else
                    TMass._Terminal.printlnDebug(getElementName()+" Register: null");
            }
	}
        else
            PAgentState=_Agent_Finished;
    }

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

    @Override
    protected TSpeechAct processFailure(TSpeechAct act){
        setRegister(new TFail());//actionFailed();
        return(null);
    }

    @Override
    protected TSpeechAct processInform(TSpeechAct act){
        TALLLContent message=(TALLLContent)act.getContent();
        try{
            String receiver=message.getItem(0).getContent();
            TList messageContent=(TList)message.getItem(1);
            storeActionActivity(new TAgentEvent("#"+"STORE TO IB",messageContent.getContent(),""));
            PInputBuffer.receiveMessage(receiver, messageContent);
            return(null);
       }catch(Exception e){
           return(super.generateNotUnderstood(act));
       }
    }

    @Override
    protected TSpeechAct processPropose(TSpeechAct act){
        TALLLContent message=(TALLLContent)act.getContent();
        String proposal=message.getItem(0).getContent();
        if(proposal.equalsIgnoreCase("services")){
            String platform=message.getItem(1).getContent();
            String service;
            TList services=(TList)message.getItem(2);
            TList serviceBelief;

            for(int i=0;i<services.size();i++)
            {
                service=services.get(i).getContent();
                serviceBelief=new TList(TConstants._Add);
                serviceBelief.add(new TAtom("sys"));
                serviceBelief.add(new TAtom(platform));
                serviceBelief.add(new TAtom(service));
                PBeliefBase.storeBelief(serviceBelief);
                PBBCU.rememberService(platform,service);
            }
            
            storeActionActivity(new TAgentEvent("#"+"SERVICES INFO",
                message.getItem(1).getContent(),message.getItem(2).getContent()));
            return(null);
        }
        return(null);
    }

    @Override
    protected TSpeechAct processRequest(TSpeechAct act){
        TALLLContent message=(TALLLContent)act.getContent();
        String request=message.getItem(0).getContent();
        if(request.equalsIgnoreCase("set_register")){
            setRegister(message.getItem(1));
            this.storeActionActivity(new TAgentEvent("#"+"REGSET",message.getItem(1).getContent(),""));
        }
        
        if(request.equalsIgnoreCase("wake_up")){
            PAgentState=_Agent_Roused;
            storeActionActivity(new TAgentEvent("#"+"ROUSED","",""));
        }
        return(null);
    }

    // ===== AGENT BEHAVIOUR =====

    @Override
    public boolean acceptableInput(TModelData data){
        if(super.acceptableInput(data))
        if(data.getReceiver().equalsIgnoreCase(this.getElementName()))
        return(true);
        return(false);
    }

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

    @Override
    public int evolveElement(){

        if(((getAgentState()==TAgent._Agent_Ready)||
                (getAgentState()==TAgent._Agent_Runs)||(getAgentState()==TAgent._Agent_Deaf)))
            executeAction ();

        if((getAgentState()==TAgent._Agent_Roused)||(getAgentState()==TAgent._Agent_Ready))
            setAgentState(TAgent._Agent_Runs);

        if(((getAgentState()==TAgent._Agent_Ready)||
                (getAgentState()==TAgent._Agent_Runs)||(getAgentState()==TAgent._Agent_Deaf))){
            TSpeechAct act=new TSpeechAct(TSpeechAct._SA_Request,getElementName(),
                    TMass.getSimulator().getElementName(),TSpeechAct._CL_ALLL,
                        "(wake_me,"+PRunDelay+")");
            TCalendarEvent event=new TCalendarEvent(TMass.getSimulator(),
                    TCalendarEvent._EventExecute,
                    TMass.getSimulator().getModelTime()+PRunDelay,
                    TMass.getSimulator().getModelTime(), this);
            TMass.getSimulator().getCalendar().insertEvent(event);
            POutputDataBuffer.push(act);

        }

        if(PInternalFrame!=null){
            ((TAgentInternalFrame)PInternalFrame).updateAgentState();
            ((TAgentInternalFrame)PInternalFrame).refreshHistory();
        }
        
        checkMaxSize();
        refreshElementFrame();
        return(getAgentState());
    };

    @Override
    public int processInputs(){

        super.processInputs();
        if(PInternalFrame!=null){
            ((TAgentInternalFrame)PInternalFrame).updateAgentState();
            ((TAgentInternalFrame)PInternalFrame).refreshHistory();
        }
        return(TModelElement._ESReady);
    };

    @Override
    public int readInputs(){
        if(!(PAgentState==_Agent_Deaf))
            super.readInputs();
        return(TModelElement._ESReady);
    };

    // ===== CONSTRUCTORS =====

    public TAgent(String name, boolean element) {

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

	PType=TModelElement._Type_Agent;
	PPlan=null;
        PActivityHistory=new LinkedList<TAgentEvent>();
	PInputBuffer=new TInputBuffer();
	PBeliefBase=new TBeliefBase();
	PBBCU=new TBBCU(PBeliefBase,this);
	PRegisters=new TRegisters();
        PAgentState=_Agent_Ready;

	String connectionName=TModelConnection.generateConnectionName(this, 1, null, -1);
	TModelConnection POutToSim=new 
			TModelConnection(this,  1,  TModelElement._Type_Agent,
                                    TModelElement._Type_Simulator, connectionName);
        POutputGates.put(1,POutToSim);
        setInputGateTypes(1,_Type_Simulator);
	connectionName=TModelConnection.generateConnectionName(this, 2, null, -1);
	TModelConnection POutToPlatform=new 
        TModelConnection(this,  2,  TModelElement._Type_Agent,
                                    TModelElement._Type_Platform, connectionName);
        POutputGates.put(2,POutToPlatform);
        setInputGateTypes(2,_Type_Platform);
        connectionName=TModelConnection.generateConnectionName(this, 3, null, -1);
        POutToPlatform=new TModelConnection(this, 3, TModelElement._Type_Agent,
                                    TModelElement._Type_Platform, connectionName);
	POutputGates.put(3,POutToPlatform);
        setInputGateTypes(3,_Type_Platform);
    }

	
    public TAgent(boolean element) {
        this("Beast"+PAgentsCount++,element);
    }
	
}
