import java.util.*;
import java.io.*;
import java.net.*;
import java.util.*;

class AgentThread extends Thread {
   Agent agent;
   TreeMap< String, String > runningMap = null;
   AgentThread(Agent agent, TreeMap< String, String> runningMap) 
   { this.agent = agent; this.runningMap = runningMap; }
   public void run() {
      boolean found = false;
      synchronized(runningMap) {
         if(runningMap.get(agent.get_name()) == null) {
            runningMap.put(agent.get_name(), "true");
            found = true;
         }
      }
      if(found) {
         agent.init();
         agent.start();
         agent.run();
         agent.stop();
         synchronized(runningMap) {
            runningMap.remove(agent.get_name());
         }
      }
   }
}

public class AgentServer implements Runnable {
   private Vector< String > linkVector = null;
   private TreeMap< String, String > visitedMap = null;
   public static final int PORT = 3030;
   public static final String HOST = "localhost";
   public static final String VERSION = "1.0.0";

   public AgentServer() {
      linkVector = new Vector< String >();
      linkVector.add("127.0.0.1");
      visitedMap = new TreeMap< String, String >();
   }

   public void sendAgent(String ip, int port, String name, byte agent[]) {
      try {
         Socket sock = new Socket(ip, port);
         ObjectOutputStream oout = new ObjectOutputStream(sock.getOutputStream());
         oout.writeObject(name); 
         oout.writeObject(agent);
         oout.flush();
         oout.close();
         sock.close();
      }
      catch(Exception ex) {
         ex.printStackTrace();
      }
   }

   class ConnectThread extends Thread {
      Vector< String > linkVector;
      byte data[];
      String className;
      int port;
      ConnectThread(Vector< String > linkVector, int port, String className, byte data[]) {
         this.linkVector = linkVector;
         this.data = data;
         this.className = className;
         this.port = port;
      }
      public void run() {
         for(int i = 0; i < linkVector.size(); i++) {
            sendAgent(linkVector.get(i), port, className, data);
         }
      }
   }


   public Agent readAgent(Socket cli) {
      Agent ret;
      byte []data;
      String className;
      try {
         ObjectInputStream oin = new ObjectInputStream(cli.getInputStream());
         className = (String)oin.readObject(); 
         data = (byte[])oin.readObject();
         cli.close();
         ServerClassLoader cl = new ServerClassLoader(className, data);
         Class cls  = cl.findClass(className);
         ret = (Agent)cls.newInstance();
         String str;
         int i;
         String found = visitedMap.get(className);
         if(found == null) {
            visitedMap.put(className, "true");
            new ConnectThread(linkVector, PORT, className, data).start();
         }
      }
      catch(Exception e) {
         e.printStackTrace();
         ret = null;
      } 
      return ret;
   }

   public void run() {
      System.out.println(">>>Roaming Agents Server: " + VERSION);
      System.out.println("Author: Matthew W. Coan");
      System.out.println("URL: http://slack.net/~mcoan/");
      System.out.println("Email: matthewcoan@hotmail.com");
      System.out.println();
      System.out.println("port number = " + PORT);
      try {
         ServerSocket svr = new ServerSocket(PORT, 1024, InetAddress.getByName(HOST));
         System.out.println("server socket...");
         Socket cli;
         Agent agent;
         TreeMap< String, String > runningMap = new TreeMap< String, String >();
         System.out.println("server running...");
         while((cli = svr.accept()) != null) {
            System.out.println("accept connection...");
            agent = readAgent(cli);
            if(agent != null) {
               new AgentThread(agent, runningMap).start();
            }
            System.out.println("accept next...");
         }
      }
      catch(Exception ex) {
         ex.printStackTrace();
      } 
   }

   public static void main(String args[]) {
      AgentServer agentServer = new AgentServer();
      agentServer.run();
   }
}
