/*
 * LaunchAgents.java
 *
 * Created on January 19, 2005, 1:12 PM
 */

package DDMT.core;
//buffer not
import javax.swing.*;
import java.awt.*;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
import java.util.LinkedList;
import java.io.*;

import DDMT.core.behaviour.ClockedSenderBehaviour;
import DDMT.Nodes.*;

import jade.domain.FIPAAgentManagement.*;
import jade.core.Agent;
import jade.lang.acl.ACLMessage;
import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.CompositeBehaviour;
import jade.core.behaviours.ParallelBehaviour;
import jade.core.behaviours.SequentialBehaviour;
import jade.lang.acl.MessageTemplate;
import jade.core.behaviours.Behaviour;
import java.util.Vector;

/**
 *
 * @author  jk3
 */
public class LaunchAgentsPanel extends DDMT.core.DefaultPanel {
    
    //List model for the agent status list
    DefaultListModel agentStatusModel;
    
    //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+\\s]*");
    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;
    
    //The list of messages send in the system
    LinkedList events;
    
    //The agent that this program uses
    Agent myAgent;
    
    //the node types - class name array
    String[][] nodeTypes;
    
    //The agent type - number of nodes array
    String[] agentNames;
    int[] agentNums;
    
    //The post office agent that takes care of sending and recieving messages
    PostOffice po = null;
    
    //The arrays for the actual code that the agents are going to run
    private byte[][] codeDefs;
    private String[] codenames;
    
    /** Creates new form LaunchAgents */
    public LaunchAgentsPanel(DDMT.core.MainPanel.ControlClass cClass) {
        initComponents();

        controlClass = cClass;
        myAgent = controlClass.getAgent();
        events = new LinkedList();
    }

    /**
     *Method to refresh the panel
     */
    public void refresh()
    {
        
        //If there is not project selected, then show an error
        if( controlClass.getProjectName() == null)
        {
            controlClass.showError("Launch Agents: Error: No project selected!");
            return;
        }
        //If the nodes array is empty, then try to fill it
        else if( controlClass.nodes == null)
        {
            //Distribute the settings
            this.distributeSettings();
            try{
                //load the nodes
                this.loadNodes();
            }catch(Exception ex)
            {
                controlClass.showError("Dynamic Monitoring: Error: Please load a topology.");
                return;
            }
        }
        
        //Try to schedule a post office agent
        if( po == null )
        {
            po = new PostOffice( controlClass.getAgent(), this );
        //    myAgent.addBehaviour(po);
        }
        
        //Try to load the agents list
        this.pingNetwork();
    }
    
    /**
     *Method for distributing all the settings that each node should know
     */
     private void distributeSettings()
     {
         //Load the agent types
         File file = new File("Files\\Projects\\"+controlClass.getProjectName()+"\\NodeClassMatching.txt" );
         nodeTypes = this.readFile(file);
         //System.out.println(nodeTypes[1].length);
         
     }
    
    /**
     *Method for pinging the network.  Will ask for a list of all node-bearing 
     *agents running and querry them to see if they are active.
     */
    private void pingNetwork()
    {     
        if( controlClass.nodes == null )
        {
            this.loadNodes();
        }
        
        try{
            //Query the DF for currently running agents.
            controlClass.getAgent().search();

            //pause
            Thread.sleep(500);

            //Check for answers
            Vector dfd = controlClass.getAgent().find();

            //Save the agents that it finds
            agentNames = new String[dfd.size()];
            agentNums = new int[dfd.size()];
            String status = new String();
            agentStatusModel  = new DefaultListModel();
            for( int i = 0; i< agentNames.length; i++)
            {
                agentNames[i] = new String( (String)dfd.get(i)/*((AID)dfd.get(2*i)).getLocalName()*/ );
                agentNums[i] = 0;
                for( int j = 0; j< controlClass.nodes.length; j++)
                {
                    if( controlClass.nodes[j].getNetworkLocation().equals( agentNames[i] ))
                        agentNums[i]++;
                }
                status = ((String) (controlClass.getAgent().currentStates.get(i)) );
                progressCell cell = null;
                if( status.equals( "green" ))
                {
                    cell  = new progressCell( 
                        "green", ((String)dfd.get(i)), agentNums[i], agentNums[i]);
                }else
                {
                    cell  = new progressCell( 
                        "red", ((String)dfd.get(i)), 0, agentNums[i]);
                }
                //System.out.println(agentNames[i] + " has "+agentNums[i]+" nodes, i = "+ i );
                agentStatusModel.addElement(cell);
            }
            jList1.setCellRenderer(new DoubleCellRenderer());
            jList1.setModel(agentStatusModel);

        }catch(Exception ex)
        {
            System.out.println("Error here");
            ex.printStackTrace();
        }
    }
    
    /**
     *Method to load the nodes into the system
     */
    private void loadNodes()
    {
        //Open the Nodes.txt file
        File nodesFile = null;

        nodesFile = new File("Files\\Projects\\" + controlClass.getProjectName() + "\\nodes.txt");
        
        try{
            controlClass.fileLoader.loadNodes(nodesFile);
        }catch(Exception ex)
        {
            controlClass.showError("Data Import: Could not read file "+nodesFile.getPath());
        }
    }
    
    /**
     *Method takes two arrays of strings and a file and reads the file as a 
     *matched pair of data items, which it puts into the arrays.
     */
    private String[][] readFile( File file )
    {
        String[][] array = null;
         try{   
                //Read from the file
                file.createNewFile();
                FileReader reader = new FileReader(file);
                BufferedReader bReader = new BufferedReader(reader);
                String line = bReader.readLine();
                
                //First line contains the number of pairs
                array = new String[2][Integer.parseInt(line)];
                
                //Read in all the pairs
                line = bReader.readLine();
                int j = 0;
                while(line != null )
                {
                    fileM = fileP.matcher(line);
                    if( fileM.matches() )
                    {
                        //Set up matchers
                        firstM = firstP.matcher(line);
                        lastM = lastP.matcher(line);
                        
                        //Find the pair
                        firstM.find();
                        array[0][j] = firstM.group();
                        lastM.find();
                        array[1][j] = lastM.group();
                    }
                    line = bReader.readLine();
                    j++;
                }
                
                //Close the readers
                reader.close();
                bReader.close();
                
            }catch(Exception ex)
            {
                controlClass.showError("Agent Types: Error: Unable to read file "+file.getPath()+". Delete the file and try again or fix the file.");
            }
        
        return array;
    }
    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    private void initComponents() {//GEN-BEGIN:initComponents
        jPanel1 = new javax.swing.JPanel();
        jButton1 = new javax.swing.JButton();
        jButton4 = new javax.swing.JButton();
        jPanel2 = new javax.swing.JPanel();
        jPanel3 = new javax.swing.JPanel();
        jPanel11 = new javax.swing.JPanel();
        jPanel12 = new javax.swing.JPanel();
        jPanel13 = new javax.swing.JPanel();
        jButton3 = new javax.swing.JButton();
        jPanel15 = new javax.swing.JPanel();
        jButton2 = new javax.swing.JButton();
        jPanel14 = new javax.swing.JPanel();
        jButton5 = new javax.swing.JButton();
        jPanel4 = new javax.swing.JPanel();
        jPanel5 = new javax.swing.JPanel();
        jPanel6 = new javax.swing.JPanel();
        jPanel7 = new javax.swing.JPanel();
        jPanel8 = new javax.swing.JPanel();
        jLabel1 = new javax.swing.JLabel();
        jScrollPane1 = new javax.swing.JScrollPane();
        jList1 = new javax.swing.JList();
        jPanel9 = new javax.swing.JPanel();

        setName("Launch Agents");
        jPanel1.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.CENTER, 5, 0));

        jPanel1.setPreferredSize(new java.awt.Dimension(25, 25));
        jPanel1.setMaximumSize(new java.awt.Dimension(25, 25));
        jPanel1.setMinimumSize(new java.awt.Dimension(25, 25));
        jButton1.setText("Start Algorithm");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jPanel1.add(jButton1);

        jButton4.setText("Stop Algorithm");
        jButton4.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton4ActionPerformed(evt);
            }
        });

        jPanel1.add(jButton4);

        add(jPanel1, java.awt.BorderLayout.SOUTH);

        jPanel2.setLayout(new javax.swing.BoxLayout(jPanel2, javax.swing.BoxLayout.X_AXIS));

        jPanel3.setLayout(new javax.swing.BoxLayout(jPanel3, javax.swing.BoxLayout.Y_AXIS));

        jPanel11.setMaximumSize(new java.awt.Dimension(50, 10));
        jPanel3.add(jPanel11);

        jPanel12.setMaximumSize(new java.awt.Dimension(50, 10));
        jPanel3.add(jPanel12);

        jPanel2.add(jPanel3);

        jPanel13.setLayout(new javax.swing.BoxLayout(jPanel13, javax.swing.BoxLayout.Y_AXIS));

        jButton3.setText("Refresh List");
        jButton3.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton3ActionPerformed(evt);
            }
        });

        jPanel13.add(jButton3);

        jPanel15.setMaximumSize(new java.awt.Dimension(10, 10));
        jPanel13.add(jPanel15);

        jButton2.setText("Launch Agents");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        jPanel13.add(jButton2);

        jPanel14.setMaximumSize(new java.awt.Dimension(10, 10));
        jPanel13.add(jPanel14);

        jButton5.setText("Launch Monitor");
        jButton5.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton5ActionPerformed(evt);
            }
        });

        jPanel13.add(jButton5);

        jPanel2.add(jPanel13);

        jPanel4.setLayout(new java.awt.BorderLayout());

        jPanel4.add(jPanel5, java.awt.BorderLayout.NORTH);

        jPanel4.add(jPanel6, java.awt.BorderLayout.EAST);

        jPanel4.add(jPanel7, java.awt.BorderLayout.SOUTH);

        jPanel8.setLayout(new java.awt.BorderLayout());

        jLabel1.setText("Current Status of Agent(s)");
        jPanel8.add(jLabel1, java.awt.BorderLayout.NORTH);

        jScrollPane1.setViewportView(jList1);

        jPanel8.add(jScrollPane1, java.awt.BorderLayout.CENTER);

        jPanel4.add(jPanel8, java.awt.BorderLayout.CENTER);

        jPanel4.add(jPanel9, java.awt.BorderLayout.WEST);

        jPanel2.add(jPanel4);

        add(jPanel2, java.awt.BorderLayout.CENTER);

    }//GEN-END:initComponents

    /**
    *Method for the 'Launch Monitor' button clicked
     **/
    private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed
        //create a new monitor window and pass it the control class and the messages vector
        MonitorWindow w = new MonitorWindow( controlClass, events );
        w.setTitle(controlClass.getProjectName() + " Monitoring");
        w.show();
    }//GEN-LAST:event_jButton5ActionPerformed

    private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed

        //Send the cancel message
        ACLMessage message = new ACLMessage();
        message.setContent("cancel");
        message.setEncoding("cancel");
        message.setConversationId("monitor");
        message.setInReplyTo(controlClass.getProjectName());
        message.setPerformative(ACLMessage.INFORM);
        
        controlClass.getAgent().broadcast(message);
    }//GEN-LAST:event_jButton4ActionPerformed

    //Method for 'Refresh' pressed
    private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed
        this.pingNetwork();
    }//GEN-LAST:event_jButton3ActionPerformed

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed
        //Send message that says "start" to all the agents
        ACLMessage message = new ACLMessage();
        message.setContent("start");
        message.setEncoding("start");
        message.setConversationId("monitor");
        message.setPerformative(ACLMessage.INFORM);
        
        controlClass.getAgent().broadcast(message);
    }//GEN-LAST:event_jButton1ActionPerformed

    /**
     *Method to load the code from the files
     */
    private void loadCode()
    {
        
        //Load the code names
        this.distributeSettings();
        codenames = nodeTypes[0];
        
        //Load the bytecode from the files
       codeDefs = new byte[codenames.length][];

        //open the right file
        try
        {
            codeDefs = new byte[codenames.length][];
            for( int i = 0; i<codenames.length; i++)
            {
                File file = new File("DDMT\\Nodes\\"+codenames[i]+".class");
                if( !file.exists() )
                {
                    file = new File("Nodes\\"+codenames[i]+".class");
                }
                FileInputStream is = new FileInputStream(file);

                //Read in the bytes
                long len = file.length();
                if( len > Integer.MAX_VALUE )
                {
                    System.out.println("File too big!");
                }
                System.out.println("Read in "+codenames[i]);
                codeDefs[i] = new byte[(int)len];

                // Read in the bytes
                int offset = 0;
                int numRead = 0;
                while (offset < codeDefs[i].length
                       && (numRead = is.read(codeDefs[i], offset, codeDefs[i].length-offset)) >= 0) {
                    offset += numRead;
                }

                is.close();
                System.out.println("Read done!");
            }
        }catch( Exception ex)
        {
            System.out.println("Error reading file!");
        }
    }
    
    /**
     *Method for 'Launch Agents' clicked
     */
    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
        //If there are no agents, throw an error
        if( agentStatusModel.size() <= 0 )
        {
            controlClass.showError("Launch Agents: Error: No agents to communicate with!");
            return;
        }
        
        //Load the code to distribute
        this.loadCode();
        
        //loop for each agent
        SequentialBehaviour base = new SequentialBehaviour();
        ParallelBehaviour child = new ParallelBehaviour();
        base.addSubBehaviour(child);
        for( int i = 0; i< agentStatusModel.size(); i++)
        {
            //Reset the progress cells
            ((progressCell)agentStatusModel.elementAt(i)).reset();
            
            //add the behaviour for this conversation
            AID address = new AID();
            address.setLocalName(((progressCell)agentStatusModel.elementAt(i)).getName() );
            Behaviour distribute = new LoadCodeBehaviour(controlClass.nodes,
                                                address,
                                                ( (progressCell)agentStatusModel.elementAt(i) ).getName(),
                                                this.nodeTypes, codeDefs,
                                                this.agentStatusModel,
                                                i);
            SetupBehaviour sendNodes = new LaunchAgentsPanel.SetupBehaviour( 
                    controlClass.nodes,address,
                    ( (progressCell)agentStatusModel.elementAt(i) ).getName(),
                    this.nodeTypes, this.agentStatusModel, i);
            SequentialBehaviour load = new SequentialBehaviour(  );
            load.addSubBehaviour(distribute);
            
            //If data redistribution is being forced, do it
            //if( jCheckBox1.isSelected() )
            {
                System.out.println("first case");
                load.addSubBehaviour(sendNodes);
            }
            
            //Otherwise don't send the data unless the agent asks for it
            /*else
            {
                System.out.println("second case");
                ACLMessage message = new ACLMessage();
                AID add = new AID();
                add.setLocalName(( (progressCell)agentStatusModel.elementAt(i) ).getName());
                message.addReceiver(add);
                message.setConversationId("monitor");
                message.setInReplyTo(controlClass.getProjectName());
                message.setEncoding("reset");
                message.setOntology(new String(""+i));
                
                load.addSubBehaviour( new dataFileLoader( message ));
            }*/
            
            child.addSubBehaviour(load);
        }
        myAgent.addBehaviour(base);
    }//GEN-LAST:event_jButton2ActionPerformed
    
    /***
    *Class that asks the agent if it needs the data to be resent, and schedules
    *the data distribution if so.
    ***/
    public class dataFileLoader extends jade.core.behaviours.OneShotBehaviour{
        
        ACLMessage message;
        
        public dataFileLoader(ACLMessage m)
        {
            message = m;
        }
        
        public void action() {
            System.out.println("*******************sending message...");
            //Ask the node to load nodes from files, if it can't schedule the node transfer to take place
            myAgent.send(message);
        }
};
    
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JButton jButton3;
    private javax.swing.JButton jButton4;
    private javax.swing.JButton jButton5;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JList jList1;
    private javax.swing.JPanel jPanel1;
    private javax.swing.JPanel jPanel11;
    private javax.swing.JPanel jPanel12;
    private javax.swing.JPanel jPanel13;
    private javax.swing.JPanel jPanel14;
    private javax.swing.JPanel jPanel15;
    private javax.swing.JPanel jPanel2;
    private javax.swing.JPanel jPanel3;
    private javax.swing.JPanel jPanel4;
    private javax.swing.JPanel jPanel5;
    private javax.swing.JPanel jPanel6;
    private javax.swing.JPanel jPanel7;
    private javax.swing.JPanel jPanel8;
    private javax.swing.JPanel jPanel9;
    private javax.swing.JScrollPane jScrollPane1;
    // End of variables declaration//GEN-END:variables
    
    /**
     *Behaviour to take messages and get them to the correct node
     */
    public class PostOffice extends CyclicBehaviour
    {
        //Filter for messages to the post office
        MessageTemplate mt;
        String conversationId;
        
        //the panel that this behaviour belongs to
        LaunchAgentsPanel panel;
        
        //The agent to which this node belongs
        MonitorAgent agent;
        
        //sender agent that takes care of all delay/clock issues
        ClockedSenderBehaviour sender;
        
        public PostOffice(MonitorAgent a, LaunchAgentsPanel p)
        {
            //myAgent.setQueueSize(1000);
            
            panel = p;
            agent = a;
            conversationId = new String("postoffice");
            mt = MessageTemplate.MatchConversationId("postoffice");
            sender = new ClockedSenderBehaviour( controlClass.nodes, events );
            //not needed
        //    agent.addBehaviour(sender);
        }
        
        public void action() 
        {
        
            ACLMessage message = myAgent.receive(mt);
            
         //   System.out.println(myAgent.getCurQueueSize());

            
            if( message != null)
            {
                //System.out.println(message.getContent());
                if( message.getPerformative() == ACLMessage.FAILURE )
                {
                    System.out.println("Error: Node no longer active!");
                }
                //else if it is a 'cancel' message
                else if( message.getPerformative() == ACLMessage.CANCEL )
                {
                    //Send the cancel message to the nodes
                    ACLMessage cancel = new ACLMessage();
                    cancel.setPerformative(ACLMessage.CANCEL);
                    cancel.setConversationId("monitor");
                    cancel.setEncoding("cancel");
                    //System.out.println("Sending cancel message...");
                    agent.broadcast(cancel);
                }
                //else if it is a control message
                else if( message.getPerformative() == ACLMessage.PROPOSE )
                {
                    //if it is a node state message
                    if( message.getEncoding().equals("ping") )
                    {
                        
                    }else if( message.getEncoding().equals("print"))
                    {
                        //System.out.println("Node "
                        //    +Integer.parseInt(message.getLanguage())
                        //    +": "+message.getContent());
                        sender.processPrint(message);
                    }else if( message.getEncoding().equals("sendnodes"))
                    {
                        //create a node sending behaviour and activate it
                        SetupBehaviour sendNodes = new LaunchAgentsPanel.SetupBehaviour( 
                                    panel.controlClass.nodes, message.getSender(),
                                    message.getSender().getLocalName(),
                                    panel.nodeTypes, panel.agentStatusModel, 
                                    Integer.parseInt(message.getOntology()) );
                        myAgent.addBehaviour(sendNodes);
                    }
                }
                //else if it's a callback message
                else if( message.getPerformative() == message.CONFIRM )
                {
                    sender.action(message);
                }
                //else if it is a node message
                else if( message.getPerformative() == message.PROPAGATE )
                { 
                    
                    //System.out.println("PostOffice got message: "+message.getEncoding()+": "+message.getContent());
                    //Confirm it's receipt
                    ACLMessage reply = message.createReply();
                    reply.setConversationId("postofficeconfirm");
                    reply.setPerformative(ACLMessage.CONFIRM);
                //    myAgent.send(reply);
                    
                    //Record the message
                    int i = Integer.parseInt(message.getLanguage());
                    int j = Integer.parseInt(message.getEncoding());
                    
                    if( i > j )
                    {
                        int k = j;
                        j = i;
                        i = k;
                    }
                    
                    //Send the message to it's correct place
                    reply = new ACLMessage();
                    int num = Integer.parseInt(message.getEncoding());
                    AID rec = new AID();
                    rec.setLocalName(controlClass.nodes[num].getNetworkLocation());
                    reply.setReplyWith(message.getReplyWith() );
                    reply.addReceiver(rec);
                    reply.setConversationId("postoffice");
                    reply.setContent(message.getContent());
                    reply.setEncoding(message.getEncoding());
                    reply.setLanguage(message.getLanguage());
                    //myAgent.send(reply);
                    
                    //Add the message to the outgoing message queue
                //    sender.sendMessage(reply);
                    //System.out.println("sending message");
                    //if( sender.isRunnable() )
                    //{
                    //    sender.restart();
                   // }
                }
                else
                {
                    System.out.println("Coming Here !!!!");
                    System.exit(0);
                }
                
            }
            else
            {
                block(100);
            }
        }
        
    }
    
    /**
     *Behaviour that will load all of the code defs from the main program
     */
    public class LoadCodeBehaviour extends Behaviour
    {
        //Boolean that conrols when the behaviour ends
        boolean isDone = false;
        
        //Message template used to filter messages
        MessageTemplate mt;
        
        //The address of the node to talk to
        AID neighbor;
        
        //The state of the conversation
        int state = 0;
        
        //The current class being discussed
        int curr = 0;
        
        //The conversation ID
        String conversationId;
        
        //The types of nodes available
        String[] codenames;
        
        //The bytecode for the classes
        byte[][] codeDefs;
        
        public LoadCodeBehaviour(Node[] n, AID ne, String cid, String[][] nt, byte[][] cd, DefaultListModel dlm, int i)
        {

           codenames = nt[0];
           codeDefs = cd;
           neighbor = ne;
           conversationId = new String(cid+"sendnodes");
           mt = MessageTemplate.MatchConversationId(conversationId);
        }
        
        public void action() {
           ACLMessage message = new ACLMessage();
           switch(state)
            {
                //Initial case
                case 0:
                    //System.out.println("Started sending code!");
                    
                    //Send the preliminary code message
                    message.addReceiver(neighbor);
                    message.setConversationId("monitor");
                    message.setContent(conversationId);
                    message.setEncoding("sendcode");
                    message.setLanguage(""+codenames.length);
                    message.setPerformative(message.ACCEPT_PROPOSAL);
                    myAgent.send(message);
                    
                    state = 1;
                    break;
                //Recieving code
                case 1:
                    message = myAgent.receive(mt);
                    if( message != null )
                    {
                        if( message.getPerformative() == message.ACCEPT_PROPOSAL )
                        {
                            //Check if this is the last one...
                            if( curr == codenames.length)
                            {
                                
                                //System.out.println("sending last message");
                                //Send done message
                               ACLMessage reply = message.createReply();
                               reply.setConversationId(conversationId);
                               reply.setPerformative(reply.CANCEL);

                               myAgent.send(reply);
                   
                                state = 3;
                                break;
                            }
                    
                            //Send the first class name message
                            ACLMessage reply = message.createReply();
                            reply.setPerformative(reply.INFORM);
                            reply.setContent(this.codenames[curr]);

                            myAgent.send(reply);

                        }else if( message.getPerformative() == message.REFUSE )
                        {
                            //Check if the next one is the last one...
                            curr++;
                            if( curr == codenames.length)
                            {
                                
                                //System.out.println("sending last message");
                                //Send done message
                               ACLMessage reply = message.createReply();
                               reply.setConversationId(conversationId);
                               reply.setPerformative(reply.CANCEL);

                               myAgent.send(reply);
                   
                                state = 3;
                                break;
                            }
                    
                            //Send the first class name message
                            ACLMessage reply = message.createReply();
                            reply.setPerformative(reply.INFORM);
                            reply.setContent(this.codenames[curr]);

                            myAgent.send(reply);
                        }else if( message.getPerformative() == message.AGREE )
                        {
                            state = 2;
                        }
                    }else
                    {
                        block(100);
                    }
                    break;
                //Send the class bytecode
                case 2:
                    //Send the code message
                    message = new ACLMessage();
                    message.addReceiver(this.neighbor);
                    message.setConversationId(conversationId);
                    message.setLanguage(codenames[curr]);
                    message.setByteSequenceContent(codeDefs[curr]);
                    
                    myAgent.send(message);
                    
                    //Increment the class being examined
                    curr++;
                    
                    state = 1;
                    break;
                    
                    //Done
               case 3:
                   //System.out.println("Done!!!");
                   
                   isDone = true;
                   break;
            }
        }
        
        public boolean done() {
            return isDone;
        }
        
    }
    
    /**
     *Behaviour to have the setup conversation with the NodeAgents
     *Sends all the nodes across the system
     */
    public class SetupBehaviour extends Behaviour
    {   
        //The neighbor that this behaviour chats with
        AID neighbor;
        
        //The template that this behaviour uses to filter messages
        MessageTemplate mt;
        
        //The string for the conversation ID
        String conversationId;
        
        //The list of nodes in the program
        Node[] nodes;
        
        //The matching between the node and the class type
        String[][] nodeTypes;
        
        //Whether the behavior is done or not.
        boolean isDone = false;
        
        //The state of the agent
        int state = 0;

        //The current node being sent
        int currNode = 0;
        
        //boolean that shows whether the current node's data has been sent //start
        boolean moreData = true;
        
        //Stream to a data file
        File file;
        FileReader reader;
        BufferedReader bReader;
        
        //Reference to the progress bar
        DefaultListModel dlm;
        int agentnum;
        
        
        /**
         *Constructor for the class
         */
        public SetupBehaviour(Node[] n, AID ne, String cID, String[][] nodetypes,
                                DefaultListModel d, int i)
        {
            dlm =  d;
            agentnum = i;
            neighbor = ne;
            mt = MessageTemplate.MatchConversationId(cID);
            nodeTypes = nodetypes;
            conversationId = cID;
            nodes = n;
            //System.out.println(conversationId);
        }
        
        /**
         *Method that defines the action 
         */
        public void action() {
            
            //Large switch statement based on the state of the conversation
            switch(state)
            {
                //send preliminary message
                case 0:
                {
                    //Send the first prelim message
                    ACLMessage message = new ACLMessage();
                    message.setPerformative(ACLMessage.PROPOSE);
                    message.addReceiver(neighbor);
                    message.setConversationId("monitor");
                    message.setEncoding("sendnodes");
                    message.setContent(conversationId);
                    message.setLanguage("true");
                    message.setInReplyTo(controlClass.getProjectName());
                    myAgent.send(message);
                    
                    //send the second prelim message
                    message = new jade.lang.acl.ACLMessage();
                    message.setPerformative( ACLMessage.PROPOSE);
                    message.addReceiver(neighbor);
                    message.setInReplyTo( ""+((int)((progressCell)(dlm.get(agentnum)) ).getMax() ) ) ;
                    message.setConversationId(conversationId);
                    message.setEncoding(controlClass.getProjectName());
                    myAgent.send(message);  
                    
                    state = 1;
                    break;
                }
                
                //Wait for the response...
                case 1:
                {       
                    //Block until the next message arrives
                    ACLMessage message = myAgent.receive(mt);
            
                    //only take messages that are of performative (accept)
                    if( message != null )
                    {
                        if( message.getPerformative() == ACLMessage.ACCEPT_PROPOSAL )
                        {     

                            //Send the first node message
                            this.sendNodeMessage();

                            try{
                                //Open the file that contains the data for this node
                                this.file = new File(nodes[currNode-1].getDataLocation());

                                if( !file.exists() )
                                {
                                    //The weird case where there isn't any data for this node
                                    state = 5;
                                }else
                                {
                                    //Otherwise, send the data
                                    this.reader = new FileReader(file);
                                    this.bReader = new BufferedReader(reader);
                                    state = 3;
                                }

                            }catch( IOException ex)
                            {
                                System.out.println("Error opening data file for node "+nodes[currNode-1].getDataLocation());
                            }

                        }
                    }else
                    {
                        this.block(100);
                    }
                    break;
                }
                
                //Send the nodes until there aren't any left
                case 2:
                {
                    //Block until the next message arrives
                    ACLMessage message = myAgent.receive(mt);
                    
                    //only take messages that are of performative (accept)
                    if( message != null && message.getPerformative() == ACLMessage.CONFIRM )
                    {
                        moreData = this.sendNodeMessage();
                        if( !moreData )
                        {
                            //Send the ending message to the agent
                            ACLMessage end = message.createReply();
                            end.setPerformative(ACLMessage.PROPOSE);
                            end.setConversationId(this.conversationId);
                            myAgent.send(end);
                            
                            //Move to the state past all the node and data states.
                            state = 4;
                            currNode = 0;
                            
                            break;
                        }else
                        {
                        
                            try{
                                //Open the file that contains the data for this node
                                this.file = new File(nodes[currNode-1].getDataLocation());

                                //If the file isn't found, then there probably isn't any data supposed to be sent to this node.
                                if( !file.exists() )
                                {
                                    System.out.println("no data to send");
                                    //The weird case where there isn't any data for this node
                                    state = 5;
                                }else
                                {
                                    this.reader = new FileReader(file);
                                    this.bReader = new BufferedReader(reader);
                                    //System.out.println("Error reading from file "+file.getPath());
                                }
                            }
                        
                            catch( IOException ex)
                            {
                                System.out.println("Error reading from file "+file.getPath());
                                ex.printStackTrace();
                            }
                            state = 3;
                        }
                    }else if(message != null && message.getPerformative() == ACLMessage.FAILURE)
                    {
                        System.out.println("Failure!");
                    }else
                    {
                       block(100);
                    }
                    break;
                }
                
                //Case for distributing the data files
                case 3:
                {
                    //Block until the next message arrives
                    ACLMessage message = myAgent.receive(mt);

                    //Send the first data file message
                    if( message != null && message.getPerformative() == ACLMessage.CONFIRM )
                    {
                        try 
                        {
                            //Define the message
                            ACLMessage reply = new ACLMessage();
                            if( bReader == null)
                            {
                                
                                System.out.println("BufferedReader not initial!"+file.getPath());
                                break;
                            }
                            
                            //If there are data lines left to send...
                            String line = null;

                            line = bReader.readLine();
                            reply = message.createReply();
                            reply.setPerformative(ACLMessage.INFORM);
                            reply.setConversationId(conversationId);
                            reply.setContent(line);

                            myAgent.send(reply);
                        
                            //If that was the last line, then move on to the next node
                            if( line == null )
                            {
                                //Send a propose message
                                //reply = message.createReply();
                                //reply.setConversationId(conversationId);
                                //reply.setPerformative(ACLMessage.PROPOSE);
                                //myAgent.send(reply);
                                
                                state = 5;
                            }
                        } catch (IOException ex)
                        {
                            System.out.println("Error reading data file!");
                        }
                    }else{
                        block(100);
                    }
                    break;
                }
                
                //When there isn't any data to send
                case 5:
                {
                    //Block until the next message arrives
                    ACLMessage message = myAgent.receive(mt);

                    //Send a blank data file message
                    if( message != null && message.getPerformative() == ACLMessage.CONFIRM )
                    {
                        //System.out.println("In state 5");
                        ACLMessage reply = message.createReply();
                        reply.setConversationId(conversationId);
                        reply.setPerformative(ACLMessage.PROPOSE);
                        reply.setContent("test");
                        myAgent.send(reply);
                        state = 2;
                    }else
                    {
                        block(100);
                    }
                    break;
                }
                
                //Done state
                case 4:
                {
                    System.out.println("Monitor Agent done");
                    isDone = true;
                    
                    //Set this agent to 'green' status
                    break;
                }
            }
        }
        
        /**
         *Method for sending a node message
         *will return true if there are still nodes to send, false otherwise
         */
        private boolean sendNodeMessage()
        {
            //Starting with the last node not looked at, get the next node to send.
            boolean sent = false;
            while(sent == false)
            {       
                 if( currNode >= nodes.length )
                {
                    //System.out.println("done sending nodes");
                    return false;
                }
                 
                //If this node is supposed to go, then send it
                if( this.nodes[currNode].getNetworkLocation().equals(this.neighbor.getLocalName()) )
                {
                    ACLMessage temp = new ACLMessage();
                    temp.addReceiver(neighbor);
                    temp.setPerformative(ACLMessage.INFORM);
                    temp.setConversationId(conversationId);
                    
                    //Find the class name for this piece of code
                    String type = null;
                    for( int i = 0; i<nodeTypes[1].length; i++)
                    {
                        if( nodes[currNode].getType().equals( nodeTypes[1][i]))
                        {
                            temp.setEncoding(nodeTypes[0][i]);
                            type = nodeTypes[0][i];
                        }
                    }
                    
                    //Add the neighbors
                    Vector n = nodes[currNode].getNeighbors();
                    String content = new String(""+nodes[currNode].getNumber()
                        +" "+nodes[currNode].getNetworkLocation()+" "
                        + controlClass.getProjectName() 
                        +"\\Data\\data"+ nodes[currNode].getNumber()+".txt "
                        +/*nodes[currNode].getType()*/type+" "+nodes[currNode].getX()
                        +" "+nodes[currNode].getY());
                    
                    for( int i = 0; i<n.size(); i++)
                    {
                        content = content + " " 
                        + ((Neighbor)n.elementAt(i)).getNumber()
                        +" "+((Neighbor)n.elementAt(i)).getDelay();
                    }
                    
                    temp.setContent(content);
                    //System.out.println(content);
                    sent = true;
                    myAgent.send(temp);
                    
                    //Set the progress bar
                    dlm.set(agentnum, (((progressCell)dlm.elementAt(agentnum)).addOne()) );
                }
                currNode++;
                
            }
            
            //must be inform messages
            return true;
        }
    
        /**
         *Method that returns the current state of the behavior, used to judge how much longer it will take
         */
        public int getState()
        {
            return state;
        }
        
        /**
         *Method that returns the maximun state of the system
         */
        public int maxState()
        {
            return nodes.length;
        }
        
        public boolean done() {
            return isDone;
        }
        
    }
    
    /**
     *Cell renderer for the list.  Shows an icon and a text message.
     */
    class DoubleCellRenderer extends JPanel implements ListCellRenderer {
        
        //The left label
        JLabel left;
        
        //The right label
        JLabel right;
        
        //The progress bar on the right
        JProgressBar progressBar;
        
        /**
         *Method to create a new cell renderer
         */
        DoubleCellRenderer() 
        {
            setLayout(new GridLayout(1,2));
            left = new JLabel();
            right = new JLabel();
            progressBar = new JProgressBar(0, 100);
            progressBar.setValue(0);
            progressBar.setStringPainted(true);
            left.setOpaque(true);
            right.setOpaque(true);
            add(left);
            add(right);
            add(progressBar);
        }
      
        /**
         *Method to get one component of the cell renderer
         */
        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) 
        {
            //The first string
            String type = ((progressCell)value).getType();
            if( type.equals("red"))
            {
                left.setIcon(new ImageIcon("Files\\Core\\Icons\\stop.GIF"));
            }else if( type.equals("green"))
            {
                left.setIcon(new ImageIcon("Files\\Core\\Icons\\go.GIF"));
            }
            String name = ((progressCell)value).getName();
            
            left.setText(name);
            progressBar.setMaximum( (((progressCell)value)).getMax() );
            progressBar.setValue( (((progressCell)value)).getCurrent() );
            right.setBackground(list.getBackground());
            right.setForeground(list.getForeground());
            left.setBackground(list.getBackground());
            left.setForeground(list.getForeground());
            setEnabled(list.isEnabled());
            setFont(list.getFont());
            return this;
        }
    }

    
    /**
     *Class to define one cell of the agent list
     */
    class progressCell
    {
        String type;
        String agentname;
        int current;
        int max;
        
        public progressCell(String t, String a, int c, int m)
        {
            max = m;
            current = c;
            agentname = a;
            type = t;
        }
        
        public String getType()
        {
            return type;
        }

        public String getName()
        {
            return agentname;
        }
        
        public int getCurrent()
        {
            return current;
        }
        
        public int getMax()
        {
            return max;
        }
        
        public void reset()
        {
            current = 0;
        }
       
        public progressCell addOne()
        {
            current++;
            //if( current == max.intValue())
            //    System.out.println(current+", "+max.intValue());
            return this;
        }
    }
}
