package DDMT.Agents;

import jade.core.Agent;
import java.util.Vector;
import jade.lang.acl.ACLMessage;
import jade.core.AID;
import jade.core.behaviours.*;
import jade.lang.acl.MessageTemplate;
import java.lang.String;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import jade.domain.FIPAAgentManagement.*;
import jade.domain.*;
import java.lang.Integer;
import java.sql.*;
import java.util.Random;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.lang.ClassLoader;
import DDMT.core.nodeFileLoader;
import DDMT.Nodes.NodeCode;
import DDMT.Nodes.Neighbor;
import DDMT.Nodes.Node;
import java.io.*;
import DDMT.Agents.ByteLoader;

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: </p>
 * @author not attributable System.out.println
 * @version 1.0
 */

public class NodeAgent extends Agent
{
    
    //Pattern for a node definition
    //Node Number, Network Location, Data Location, Node Type, X-value, Y-value, Neighbor1, Neighbor1 delay, Neighbor2, ...
    private Pattern nodePattern = Pattern.compile("\\d+\\s\\S+\\s\\S+\\s\\S+\\s\\d+\\s\\d+[\\s+\\d+\\s\\d+\\p{Punct}\\d+]*");
    private Matcher nodeMatcher;
    
    //The standard group of patterns from the other panels
    private Pattern integerP = Pattern.compile("\\d+");
    private Pattern doubleP = Pattern.compile("\\d+\\p{Punct}\\d+");
    private Pattern stringP = Pattern.compile("\\S+\\s");
    private Pattern alphaStringP = Pattern.compile("[a-zA-Z]\\S*");
    private Pattern neighborP = Pattern.compile("\\s\\d+\\s\\d+\\p{Punct}\\d+");
    private Pattern fileP = Pattern.compile("\\S+\\s\\S+");
    private Pattern firstP = Pattern.compile("\\A(\\S+)");
    private Pattern nextP = Pattern.compile("\\s(\\S+)");
    private Pattern lastP = Pattern.compile("\\S+\\Z");
    private Matcher fileM;
    private Matcher firstM;
    private Matcher nextM;
    private Matcher lastM;
    private Matcher integerM;
    private Matcher doubleM;
    private Matcher stringM;
    private Matcher alphaStringM;
    private Matcher neighborM;
    
    //List of nodes for resetting the nodes (running the algorithm again)
    private String[] codeNames;
    private byte[][] codeDefs;
    
    //Array of bools for the code that is already loaded
    boolean[] loaded;
    
    //Nodes for this agent
    NodeCode[] nodes;
    
    //The AID for the post office agent
    AID postoffice;
    
    //The sender behaviour for the agent
    SenderBehaviour s = null;
    
    //Class loader for the node code
    ByteLoader loader;
    
    //The name of the current project being run
    String currentProject;
    
    //The postoffice
    PostOffice PO = new PostOffice();
    
    /*state of the agent
     0 - Nothing has been recieved
     1 - Code has been recieved
     2 - Code and node information has been recieved
     3 - Running the algorithm
     4 - Algorithm done*/
    public int state = 0;
    
    public void setup()
    {
        //Register with the DF
        this.register();
          
       // this.setQueueSize(1000);
        //make the filter to remove bad messages
        FilterBehavior filter = new FilterBehavior();
        MessageTemplate messTemp = MessageTemplate.MatchConversationId("postoffice");
        filter.addMessageTemplate(messTemp);
        this.addBehaviour(filter);
        
        //Add the behaviour that will wait for control messages
        Behaviour messageListener = new NodeAgent.MessageListener("postoffice",filter);
        this.addBehaviour(messageListener);
                    
        //Start the behaviour that sends messages
        s = new SenderBehaviour(filter);
        this.addBehaviour(s);
        
        //Add the behaviour to listen for all messages sent across the system
        Behaviour mListener = new MonitorListener(codeNames, codeDefs, this, filter);
        this.addBehaviour(mListener);
    }
    
    /**
     *Method to register with the DF agent
     */
    void register()
    {
        ServiceDescription sd = new ServiceDescription();
        sd.setName("NodeAgent");
        sd.setType("NodeAgent");
        Property p = new Property();
        p.setName("NodeAgent");
        sd.addProperties(p);
        DFAgentDescription dfd = new DFAgentDescription();
        dfd.setName(getAID());
        dfd.addServices(sd);

        try {
            DFAgentDescription list[] = DFService.search( this, dfd );
            if ( list.length>0 )
            {
                DFService.deregister(this);
                System.out.println("Deregistering...");
            }
    
            DFService.register(this,dfd);
            System.out.println("Registered!");
        }catch (FIPAException fe)
        {
            //fe.printStackTrace();
            System.out.println("Node already registered!");
        }
    }


    /** Creates a new instance of nodeFileLoader */
    public NodeCode[] loadNodeCode( File nodesFile,
                        String[] codeNames,
                        byte[][] codeDefs,
                        NodeAgent agent,
                        ByteLoader loader) throws Exception
    {
        NodeCode[] nodes = null;
        try{
            FileReader fileReader = new FileReader(nodesFile);
            BufferedReader bReader = new BufferedReader(fileReader);
            //Read the number of nodes

            String line = bReader.readLine();
            if( line == null )
            {
                throw new IOException();
            }
            nodes = new NodeCode[Integer.parseInt(line)];
            line = bReader.readLine();
            
            //Loop for each line read.
            int i = 0;
            while( line != null )
            {
                nodeMatcher = nodePattern.matcher(line);
                if( nodeMatcher.matches() )
                {
                    //Set up the matchers
                    integerM = integerP.matcher(line);
                    doubleM = doubleP.matcher(line);
                    alphaStringM = alphaStringP.matcher(line);
                    neighborM = neighborP.matcher(line);
                    
                    //create the correct type of node
                    NodeCode node = null;

                    //Read in the node.
                    integerM.find();
                    int number = Integer.parseInt(integerM.group());
                    alphaStringM.find();
                    String networkLocation = alphaStringM.group();
                    alphaStringM.find();
                    String dataLocation = new String(alphaStringM.group());
                    alphaStringM.find();
                    String type = alphaStringM.group();
                    //System.out.println("|"+type+"|");
                    
                    //Find the correct type of agent to start
                    for( int j = 0; j<codeNames.length; j++)
                    {
                        
                        if( codeNames[j].equals( type ))
                        {
                            //System.out.println(codeDefs[j]+" - "+type);
                            
                            //Load the class from bytes
                            try{
                            node = loader.defineClass(new String("DDMT.Nodes."+codeNames[j]), codeDefs[j]);
                            }catch(Exception ex)
                            {
                                ex = new Exception("j is "+j+" size is "+codeNames.length);
                                throw ex;
                            }
                            //Set the agent interface
                            AgentInterface iface = new AgentInterface( this );
                            iface.setNodeNumber(number );
                            //System.out.println("Setting node number: "+number );
                            node.setIface(iface);
                            node.setAgent(this);
                        }
                    }
                    
                    //System.out.println(number);
                    node.setNumber( number );
                    node.setNetworkLocation( networkLocation );
                    node.setDataLocation( dataLocation );
                    node.setType(type);
                    node.setDataLocation(dataLocation);
                    integerM.find();
                    node.setX(Integer.parseInt(integerM.group()));
                    integerM.find();
                    node.setY(Integer.parseInt(integerM.group()));

                    //Load the neighbors
                    while( neighborM.find() )
                    {
                        String neighbor = new String( neighborM.group() );
                        integerM = integerP.matcher(neighbor);
                        doubleM = doubleP.matcher(neighbor);
                        Neighbor nodeNeighbor = new Neighbor();
                        integerM.find();
                        nodeNeighbor.setNumber(Integer.parseInt(integerM.group()));
                        doubleM.find();
                        nodeNeighbor.setDelay(Double.parseDouble(doubleM.group()));
                        node.addNeighbor(nodeNeighbor);       
                    }
                    //Add node to nodes list
                    nodes[i] = node;
            
                    i++;
                }
                line = bReader.readLine();
            }
            System.out.println("Loaded "+i+" nodes");
            //Close the files
            bReader.close();
            fileReader.close();
        }
        
        //If there was any problem, then return false
        catch(Exception ex)
        {
            System.out.println("reading of the file failed...");
            ex.printStackTrace();
            throw new Exception();
        }
        
        //If everything went well, return true
        return nodes;
    }
    
    protected void takeDown(){

        //de-register from DF
	try
        {
            DFService.deregister(this);
            System.out.println("De-registering from the DF");
	}catch( FIPAException fe )
        {
            fe.printStackTrace();
        }
    }
    
    /**
     *Method to try and load the data for a given project from a local file.
     *If successfull, return true
     *Otherwise return false
     */
    boolean loadProject( String projectName )
    {
        try{
            
        
        }catch(Exception ex)
        {
            return false;
        }
        return true;
    }
    
    /**
     *Behaviour that loads the node bytecode over the network
     */
    class CodeLoader extends jade.core.behaviours.Behaviour
    {
        //State control variable for the behaviour
        int state = 0;
        
        //The boolean that determines whether this behaviour is done or not
        boolean isDone = false;
        
        //Mesage filter for the conversation
        MessageTemplate mt;
        
        //Arrays to hold the actual code information
        NodeAgent agent;
        
        //Constructor
        public CodeLoader(String cid, NodeAgent a)
        {
            mt = MessageTemplate.MatchConversationId(cid);
            agent = a;
        }
        
        public void action() {
            switch(state)
            {
                //Expecting the first class name
                case 0:
                    ACLMessage message = myAgent.receive(mt);
                    
                    if( message != null)
                    {
                        //Check if the conversation is done
                        if( message.getPerformative()== message.CANCEL)
                        {
                            System.out.println("Break...");
                            
                            //Set the state of the agent
                            agent.state = 1;
                    
                            state = 3;
                            break;
                        }
                        //System.out.println("Got here! "+codeNames.length+", "+message.getContent());
                        
                        //Create the reply message
                        ACLMessage reply = message.createReply();
                        
                        //See if this class is already here
                        String className = message.getContent();
                        for( int i = 0; i<codeNames.length; i++)
                        {
                            if( codeNames[i] == null || className.equals(codeNames[i]))
                            {
                                codeNames[i] = "here";
                            }
                        }
                        
                        //...send the accept message
                        reply.setPerformative(ACLMessage.AGREE);
                        System.out.print(className+"..");
                        //Look for the bytecode in the next message
                        state = 1;
                        myAgent.send(reply);
                    }else
                    {
                        block(100);
                    }
                    
                    break;
                //The class bytecode is in this message...
                case 1:
                    //Get the message
                    message = myAgent.receive(mt);
                    if( message != null )
                    {
                        //System.out.println("Processing...");
                        for( int i = 0; i<codeNames.length; i++)
                        {
                            if( codeNames[i].equals("here") )
                            {
                                System.out.println(".Agree "+message.getLanguage());
                                codeNames[i] = message.getLanguage();
                                codeDefs[i] = message.getByteSequenceContent();
                                
                                break;
                            }
                        }
                        
                        //Reply with an accept_proposal message
                        ACLMessage reply = message.createReply();
                        reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
                        
                        myAgent.send(reply);
                        
                        state = 0;
                    }else
                    {
                        block(100);
                    }
                    break;
                //Done sending code
                case 3:
                    
                    //Load a new byteloader to load the code
                    loader = new ByteLoader();
                    
                    //System.out.println("Node done!");
                    isDone = true;
                    break;
            }
        }
        
        public boolean done() {
            return isDone;
        }
        
    }
    
    /**
     *Behaviour that listens for data messages
     */
    class MessageListener extends jade.core.behaviours.CyclicBehaviour
    {
        //message template to filter by
        MessageTemplate mt;
        
        //
        String conversationId;
        private FilterBehavior filter;
        
        public MessageListener(String cId, FilterBehavior filter)
        {
            conversationId = cId;
            mt = MessageTemplate.MatchConversationId("postoffice");
            this.filter = filter;
            filter.addMessageTemplate(MessageTemplate.MatchConversationId("postoffice"));
        }
        
        //new code which takes the message directly from the local POBox
        
        public void action()
        {
            Vector Messages = new Vector();
            if (PO.LeftToReply()==0)
            {
                Messages = PO.SendMessages();
                
                try
                {
                    for (int i=0;i<Messages.size();i++)
                    {
                        MsgElement msgElem = (MsgElement)Messages.elementAt(i);

                        int num = msgElem.dest;

                        for( int j = 0; j< nodes.length; j++)
                        {
                            if( nodes[j].getNumber() == num)
                            {
                                //Special try block to catch all exceptions thrown
                                //by the users code.
                                try
                                {
                                    //Process the node
                                    nodes[j].processMessage( msgElem.Msg,  msgElem.dest );
                                   //nodes[i].run( message.getContent(), Integer.parseInt(message.getLanguage()) );
                                }
                                catch(Exception ex)
                                {
                                    System.out.println("Exception thrown!");
                                    ex.printStackTrace();
                                }
                                break;
                            }
                        }
                    }
                }
                catch(Exception ex)
                {
                    System.out.println("Error! Number was incorrect!");
                }
            }
            else
                block(1000);
            
            
        }
        
        
       //old code which delivers the message through jade
        
//        public void action() 
//        {
//            ACLMessage message = null;
//            do
//            {
//            //    System.out.println(myAgent.getCurQueueSize());
//                //Listen for a data message
//                message = myAgent.receive(mt);
//               
//                if( message != null )
//                {
//                 //   System.out.println("Agent got message: "+message.getContent());
//                    //Send the message to the correct node
//                    try
//                    {
//                        int num = Integer.parseInt(message.getEncoding());
//
//                        for( int i = 0; i< nodes.length; i++)
//                        {
//                            if( nodes[i].getNumber() == num)
//                            {
//                                //Special try block to catch all exceptions thrown
//                                //by the users code.
//                                try
//                                {
//                                    //Process the node
//                                    nodes[i].processMessage( message );
//                                   //nodes[i].run( message.getContent(), Integer.parseInt(message.getLanguage()) );
//                                }
//                                catch(Exception ex)
//                                {
//                                    System.out.println("Exception thrown!");
//                                    ex.printStackTrace();
//                                }
//                                break;
//                            }
//                        }
//
//                    }catch(Exception ex)
//                    {
//                        System.out.println("Error! Number was incorrect!");
//                    }
//                }
//                else
//                {
//                    block(100);
//                 //   System.out.println("!!!!!!!!!!!!!!!!!!!!!!!!In Block----------------");
//                }
//            }
//            while (message != null);
//        }
    }
    
    /**
     *Behaviour to listen for the start of the setup sequence
     */
    class MonitorListener extends jade.core.behaviours.CyclicBehaviour
    {//start
        FilterBehavior filter;
        
        //Message filter for monitor messages
        MessageTemplate mt;
        
        //Control behavior of the agent
        Behaviour controlB;
        
        //Code loader for the agent
        Behaviour codeLoader;
        
        //Link to the agent
        NodeAgent agent;
        
        //Constructor
        public MonitorListener(String[] cn, byte[][] cd, NodeAgent a, FilterBehavior filter)
        {
            this.filter = filter;
            mt = MessageTemplate.MatchConversationId(new String("monitor"));
            filter.addMessageTemplate(MessageTemplate.MatchConversationId(new String("monitor")));
            agent = a;
            codeNames = cn;
            codeDefs = cd;
        }
        
        public void action() {
            
            //Listen for a monitor starting call
            ACLMessage message = myAgent.receive(mt);
            if( message != null )
            {
                //System.out.println(message.getEncoding());
                //If the message is to start the algorithm, then do it
                if( message.getEncoding().equals("start"))
                {
                    if( agent.state == 2 )
                    {
                        System.out.println("Starting nodes...");
                        //Start each of the nodes
                        for( int i = 0; i<nodes.length; i++)
                        {
                            //System.out.println(nodes[i].getDataLocation());
                            nodes[i].start();
                        }
                    }
                }
                //If the message is to send node info, then do it
                else if(message.getEncoding().equals("sendnodes"))
                {
                    if( agent.state == 1 )
                    {
                        
                        //Save this address as the postoffice
                        agent.postoffice = message.getSender();

                        //Start the node distribution
                        controlB = new NodeAgent.ControlBehaviour(message.getContent(), agent, filter);
                        myAgent.addBehaviour(controlB);

                    }else
                    {
                        System.out.println("Can't send the nodes now, state is "+agent.state);
                    }
                }
                //If the message is to send node code then do it
                else if(message.getEncoding().equals("sendcode"))
                {
                    if( agent.state == 0 || agent.state == 4 || agent.state == 2 )
                    {
                        //Save this address as the postoffice
                        agent.postoffice = message.getSender();
                        
                        System.out.println("Starting code setup...");

                        //Initialize the codenames/codedefs vars if null
                        if( codeNames == null || codeNames.length != Integer.parseInt(message.getLanguage()))
                        {                        
                            agent.codeNames = new String[Integer.parseInt(message.getLanguage())];
                            agent.codeDefs = new byte[Integer.parseInt(message.getLanguage())][];
                        }

                        codeLoader = new CodeLoader(message.getContent(), agent);
                        MessageTemplate messTemp = MessageTemplate.MatchConversationId( message.getContent() );
                        filter.addMessageTemplate(messTemp);
                        myAgent.addBehaviour(codeLoader);

                        state = 1;
                        
                        //reply in the affermative
                        ACLMessage reply = message.createReply();
                        reply.setConversationId(message.getContent());
                        reply.setPerformative(reply.ACCEPT_PROPOSAL);
                        myAgent.send(reply);
                    }else
                    {
                        System.out.println("Can't send node now, state is "+agent.state);
                    }
                }
                //If it's a ping message, send reply
                else if( message.getEncoding().equals("ping"))
                {
                    ACLMessage reply = message.createReply();
                    reply.setPerformative(reply.CONFIRM);
                    reply.setConversationId("ping");

                    //If the agent is supposed to be working on a different project
                    //than the one currently loaded, return 'red' status
                    //if( !agent.currentProject.equals(reply.getContent()) )
                    //{
                    //    reply.setContent("red");
                    //}
                    //...Or if the agent state is 'green' return that
                    if( agent.state == 2)
                    {
                        reply.setContent("green");
                    }
                    //...Otherwise return red status
                    else
                    {
                        reply.setContent("red");
                    }
                    myAgent.send(reply);
                }
                
                //else if this is a reset message,
                else if( message.getEncoding().equals( "reset"))
                {
                    //System.out.println("recieved reset message...");
                    //Save the current project name
                    agent.currentProject = message.getInReplyTo();
                    
                    //Load the data from the files.
                    try{
                        File file = new File( agent.getLocalName()+"\\"+message.getInReplyTo()+"\\nodes.txt");
                        agent.nodes = loadNodeCode( 
                                file, codeNames, codeDefs,
                                agent, loader);
                        System.out.println("Agent reset");
                        agent.state = 2;
                    }catch(Exception ex)
                    {
                        System.out.println("Error!");
                        ACLMessage reply = message.createReply();
                        reply.setConversationId("monitor");
                        reply.setEncoding("sendnodes");
                        myAgent.send(reply);
                    }
                }
                //else if this is a cancel message,
                else if(message.getEncoding().equals( "cancel"))
                {
                    //Run the end method for each node
                    for( int i = 0; i< nodes.length; i++)
                    {
                        nodes[i].end();
                    }
                    
                    //Reset the nodes
                    message.setEncoding("reset");
                    message.setInReplyTo(agent.currentProject);
                    agent.send(message);
                }
            }else{
             
                block(100);
            }
        }
    }
    
    /**
     *Behaviour that takes care of recieving the nodes from the main agent
     */
    class ControlBehaviour extends jade.core.behaviours.Behaviour
    {   
        //The state of the behaviour
        int state = 0;
        
        //Temporary vector for the neighbors
        Vector n;
        
        //Message template for the conversation
        MessageTemplate mt;
        
        //Boolean that controls whether this behaviour gets rescheduled
        boolean isDone = false;
        
        //File and readers to read the data in
        File file;
        FileWriter writer;
        BufferedWriter bWriter;
        
        File nodeFile;
        FileWriter nodeWriter;
        BufferedWriter nodeBWriter;
        FileReader nodeRreader;
        BufferedReader nodeBReader;
        
        //Agent Interface
        AgentInterface iface;
        
        //Agent
        NodeAgent agent;
        
        private FilterBehavior filter;
        
        /**
         *Constructor for the behavior
         */
        public ControlBehaviour(String cid, NodeAgent a, FilterBehavior filter)
        {
            agent = a;
            n = new Vector();
            mt = MessageTemplate.MatchConversationId(cid);
            this.filter = filter;
            filter.addMessageTemplate(MessageTemplate.MatchConversationId(cid));
        }
        
        /**
         *Method that defines what this behavior does
         */
        public void action()
        {
            ACLMessage message = myAgent.receive(mt);
            
            //Large switch statement based on the state of the conversation
            switch(state)
            {
                //blocking recieve
                case 0:
                {
                    ACLMessage reply;
                    
                    if( message != null && message.getPerformative() == ACLMessage.PROPOSE )
                    {                        
                        //Open the file to save the node information to
                        try
                        {
                            agent.currentProject = message.getEncoding();
                            nodeFile = new File(agent.getLocalName()+"\\"+message.getEncoding()+"\\nodes.txt");
                            new File(nodeFile.getParent()).mkdirs();
                            if( !nodeFile.exists() )
                            {
                                nodeFile.createNewFile();
                            }
                            
                            nodeWriter = new FileWriter(nodeFile);
                            nodeBWriter = new BufferedWriter(nodeWriter);
                            
                            //Write the number of nodes
                            //System.out.println(message.getInReplyTo());
                            nodeBWriter.write( message.getInReplyTo() );
                            nodeBWriter.newLine();
                            
                        }catch(Exception ex)
                        {
                            System.out.println("Error opening node file!");
                            ex.printStackTrace();
                        }

                        //reply to the post office
                        reply = message.createReply();
                        reply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
                        myAgent.send(reply);
                        this.state = 1;
                        //mt = MessageTemplate.MatchConversationId(message.getConversationId());
                    }else
                    {
                        block(100);
                    }
                    break;
                }
                
                //The case for the node data distribution
                case 1:
                {
                    //If this is a node message, acknowledge that it has been recieved
                    if( message != null /*&& message.getPerformative() == ACLMessage.INFORM*/)
                    {
                        if( message.getPerformative() == ACLMessage.INFORM )
                        {       
                            //Get the first line of the node file
                            String content = new String(message.getContent());
                           
                            //System.out.println("Got first message: "+content);                        
                            nodeMatcher = nodePattern.matcher(content);
                            if( nodeMatcher.matches() )
                            {
                                //System.out.println("Node message");
                                //Set up the matchers
                                integerM = integerP.matcher(content);
                                doubleM = doubleP.matcher(content);
                                alphaStringM = alphaStringP.matcher(content);
                                neighborM = neighborP.matcher(content);

                                //create the correct type of node
                                NodeCode node = null;
                                String type = new String(message.getEncoding());
                                
                                //Find the correct type of agent to start
                                for( int i = 0; i<codeNames.length; i++)
                                {
                                    if( codeNames[i].equals( type ))
                                    {
                                        //Load the class from bytes
                                        node = loader.defineClass(new String("DDMT.Nodes."+codeNames[i]), codeDefs[i]);

                                        //Set the agent interface
                                        iface = new AgentInterface( agent);
                                        node.setIface(iface);
                                        node.setAgent(myAgent);
                                    }
                                }

                                //Read in the node.
                                integerM.find();
                                node.setNumber(Integer.parseInt(integerM.group()));
                                iface.setNodeNumber(node.getNumber() );
                                alphaStringM.find();
                                node.setNetworkLocation(alphaStringM.group());
                                alphaStringM.find();
                                node.setDataLocation(agent.getLocalName()+"\\"+alphaStringM.group());
                                
                                alphaStringM.find();
                                alphaStringM.group();
                                
                                //Set the type to be the class name
                                node.setType(message.getEncoding());
                                alphaStringM.group();
                                
                                integerM.find();
                                node.setX(Integer.parseInt(integerM.group()));
                                integerM.find();
                                node.setY(Integer.parseInt(integerM.group()));

                                //Load the neighbors
                                while( neighborM.find() )
                                {
                                    String neighbor = new String( neighborM.group() );
                                    integerM = integerP.matcher(neighbor);
                                    doubleM = doubleP.matcher(neighbor);
                                    Neighbor nodeNeighbor = new Neighbor();
                                    integerM.find();
                                    nodeNeighbor.setNumber(Integer.parseInt(integerM.group()));
                                    doubleM.find();
                                    nodeNeighbor.setDelay(Double.parseDouble(doubleM.group()));
                                    node.addNeighbor(nodeNeighbor);

                                }
                                //Add the node to the list
                                n.add(node);

                                //reply with a success message
                                ACLMessage reply = message.createReply();
                                reply.setPerformative(ACLMessage.CONFIRM);
                                myAgent.send(reply);

                                try{
                                    //Save this line
                                    String output = new String(""+node.getNumber()
                                        +" "+node.getNetworkLocation()+" " 
                                        +node.getDataLocation()+" "
                                        +node.getType()+" "+node.getX()
                                        +" "+node.getY() +" "); 
                                    
                                    //Save the neighbors
                                    Vector n = node.getNeighbors();
                                    for( int i = 0; i<n.size(); i++)
                                    { 
                                        output = output + ((Neighbor)n.elementAt(i)).getNumber()
                                        +" "+((Neighbor)n.elementAt(i)).getDelay() + " ";
                                    }
                                    //System.out.println(output);
                                    nodeBWriter.write(output);
                                    nodeBWriter.newLine();
                                }catch(Exception ex)
                                {
                                    System.out.println("Error writing line to node file!");
                                    System.out.println("Line: "+content);
                                    ex.printStackTrace();
                                }
                                try{
                                    //Open the file to save the data to
                                    file = new File(node.getDataLocation());
                                    //System.out.println(node.getDataLocation());
                                    //If the directory doesn't exist, then create it
                                    new File(file.getParent()).mkdirs();
                                    writer = new FileWriter(file);
                                    bWriter = new BufferedWriter(writer);

                                }catch(Exception ex)
                                {
                                    System.out.println("Error opening data file!");
                                    ex.printStackTrace();
                                    break;
                                }

                                //Set the state to recieve the data file for this node
                                state = 2;
                                //System.out.println("State = 2");
                            }
                        }else if(message.getPerformative() == ACLMessage.PROPOSE)
                        {
                            
                            state = 3;
                            agent.state = 2;
                        }else
                        {
                            //send error message back to the monitor agent
                            ACLMessage reply = message.createReply();
                            reply.setPerformative(ACLMessage.FAILURE);
                            myAgent.send(reply);
                        }
                    }else
                    {
                        block(100);
                    }
                    break;
                }
                
                //Read in the data file
                case 2:
                {
                    try
                    {
                        if( message != null && message.getPerformative() == ACLMessage.INFORM )
                        {
                            if( message.getContent() != null)
                            {
                                //Read the line from the message and print it to file
                                bWriter.write(message.getContent());
                                bWriter.newLine();
                            }
                            
                            //Send confirmation message
                            ACLMessage reply = message.createReply();
                            reply.setPerformative(ACLMessage.CONFIRM);
                            myAgent.send(reply);
                            
                        }else if( message != null && message.getPerformative() == ACLMessage.PROPOSE )
                        {
                            //Flush the writer and close the file
                            bWriter.flush();
                            bWriter.close();
                            writer.close();
                            
                            //Return to state 1
                            //System.out.println("Returning to state 1");
                            state = 1;
                            
                            //Send out confirmation message
                            ACLMessage reply = message.createReply();
                            reply.setPerformative(ACLMessage.CONFIRM);
                            myAgent.send(reply);
                        }else
                        {
                            block(100);
                        }
                    }catch(Exception ex)
                    {
                        //System.out.println("Error writing to file!");
                    }
                    break;
                }
                
                //Done
                case 3:
                {
                    //Wrap the agent up, cause it's done successfully
                    nodes = new NodeCode[n.size()];
                    for( int i = 0; i< n.size(); i++)
                    {
                        nodes[i] = (NodeCode)n.elementAt(i);
                    }
                    
                    try{
                        //Close the node file
                        nodeBWriter.flush();
                        nodeBWriter.close();
                        nodeWriter.close();
                    }catch(Exception ex)
                    {
                        System.out.println("Error closing node streams/file");
                    }
                    
                    System.out.println("Node agent done");
                    
                    //Set this agent to terminate
                    isDone = true;
                }
            }
        }
        
        public boolean done() {
            return isDone;
        }
        
    }
    
    public class SenderBehaviour extends jade.core.behaviours.CyclicBehaviour
    {
        //Boolean that controls when this agent sleeps
        MessageTemplate mt;
        
        //The current state of the agent
        int state = 0;
        
        //Queue of messages to send
        Vector messages;
        
        FilterBehavior filter;
        
        public SenderBehaviour(FilterBehavior filter)
        {
            this.filter = filter;
            messages = new Vector();
            mt = MessageTemplate.MatchConversationId("postofficeconfirm");
            filter.addMessageTemplate(MessageTemplate.MatchConversationId("postofficeconfirm"));
            
        }
        
        public void action() 
        {

        //    System.out.println("Num Messages : "+messages.size());
            //Check for messages to send
            if( messages.size() > 0)
            {
                myAgent.send((ACLMessage)messages.elementAt(0));
                messages.removeElementAt(0);
             //   System.out.println("Sent message...");
                //state = 1;
            }else
            {
                block(100);
            }
        }  
        
        public void addMessage(ACLMessage m)
        {
            messages.addElement(m);
        }
        
    }
    
    public class AgentInterface
    {
        //The agent that this interface belongs to
        private NodeAgent agent;
        
        //The node that this interface belongs to
        private int number;
        
        //The current message being processed
        private ACLMessage message;
        
        private FilterBehavior filter;
        
        public AgentInterface(NodeAgent a){
            agent = a;
            
        }
        
        /**
         *Method to set the current incoming message that is being replied to.
         *Used to stamp messages with the time that they should be considered
         *as having been sent by.
         */
        public void setMessage( ACLMessage m )
        {
            message = m;
        }
        
        /**
         *Method to set the node number for the agent interface.  Appended
         *to all message sent from the node.
         */
        public void setNodeNumber( int n )
        {
            number = n;
        }
        
        //Method to signal the end of the algorithm
        public void done()
        {
            //send done message to the main agent
            ACLMessage message = new ACLMessage();
            message.addReceiver(this.agent.postoffice);
            message.setPerformative(message.CANCEL);
            message.setConversationId("postoffice");
            message.setContent("Cancel");
            agent.send(message);
            agent.state = 4;
        }
        
        
        // adds the message directly to the POQueue
        public void sendMessage(ACLMessage message)
        {
        //    PO.AddMessageToBin(message.getContent(),)
        }
        
        /*
        //Method to send an ACLMessage through the sender behaviour
        public void sendMessage(ACLMessage message)
        {
            s.addMessage(message);
            if( !s.isRunnable() )
            {
                s.restart();
            }
        }
        */ 
         
        
        //Method that the user's code calls to send a message
        public void sendMessage(String message, int n )
        {
//            ACLMessage temp = new ACLMessage();
//            temp.setPerformative( temp.PROPAGATE );
//            temp.addReceiver(postoffice);
//            temp.setContent(message);
//            if( this.message != null && this.message.getReplyWith() != null)
//            {
//                temp.setReplyWith( this.message.getReplyWith() );
//            }
//            else
//            {
//                System.out.println("WARNING: no timestamp available for message!");
//                temp.setReplyWith("0");
//            }
//            temp.setEncoding(String.valueOf(n));
//            temp.setLanguage(String.valueOf(number));
//            temp.setConversationId("postoffice");
//            
//            this.sendMessage(temp);
            
            //add message to the PoBin
            PO.reply(message,n);
            
        }
        
        //Method that the users code calls to pause the agent for x amount of time
        public void pauseNode(double delay)
        {
            //Create the message
            ACLMessage temp = new ACLMessage();
            temp.addReceiver(postoffice);
            temp.setContent(String.valueOf(delay));
            temp.setLanguage(String.valueOf(number));
            temp.setEncoding("pause");
            temp.setConversationId("postoffice");
            temp.setPerformative(temp.PROPOSE);
            if( this.message != null &&
                this.message.getReplyWith() != null)
            {
                temp.setReplyWith( this.message.getReplyWith() );
            }else
            {
                System.out.println("WARNING: no timestamp available for message!");
                temp.setReplyWith("0");
            }
                        
            //Add this message to the outgoing queue
            //System.out.println("added message to queue");
            s.addMessage(temp);
            if( !s.isRunnable() )
            {
                s.restart();
            }
        }
        
        //Method that the user's code calls to print out a message setIface
        public void printMessage(String message)
        {
            ACLMessage temp = new ACLMessage();
            temp.addReceiver(postoffice);
            temp.setContent(message);
            temp.setLanguage(new String(""+number));
            temp.setEncoding(new String("print"));
            temp.setConversationId("postoffice");
            temp.setPerformative(temp.PROPOSE );
            if( this.message != null &&
                this.message.getReplyWith() != null)
            {
                temp.setReplyWith( this.message.getReplyWith() );
            }else
            {
                System.out.println("WARNING: no timestamp available for message!");
                temp.setReplyWith("0");
            }
            
            //Add this message to the outgoing queue
            //System.out.println("added message to queue");
            s.addMessage(temp);
            if( !s.isRunnable() )
            {
                s.restart();
            }
        }
    }
    
    public class FilterBehavior extends jade.core.behaviours.CyclicBehaviour
    {   
        //Boolean that controls when this agent sleeps
        MessageTemplate mt;
        
        //The current state of the agent
        int state = 0;
        
        //Queue of messages to send
        Vector messages;
        
        public FilterBehavior()
        {
            messages = new Vector();
            mt = MessageTemplate.MatchAll();
        }
        
        public void action() 
        {

            /*ACLMessage msg = myAgent.receive(mt); 
            if( msg != null)
                System.out.println("cleared");
            if( msg == null )
                block(100);*/
        }  
        
        public void addMessageTemplate(MessageTemplate newTemplate)
        {
            mt = MessageTemplate.and(mt, MessageTemplate.not( newTemplate ) );
        }
        
    }
}
