/* Learning Net - shlomy boshy 031868912
 * Reinforcement Learning network simulation  
 */

package learnnet;
import java.util.*;

public class Network extends Vector {
  /** main class for the whole network.
   * Loads network topology and starts nodes.    
   * stops nodes when simulation ends.
   */  
    
   Vector packetsCreators;
   Random R;
   public String inputPattern=null;
   public String routerType=null;
   public String lookupTableType=null;
   public double numMsgParam;   
   public boolean packetsEnded=false; 
      /* only one packet creator really updates it */
   public boolean networkStarted=false;
      /* only one Network thread updates it */
   public int numNodes=0;
   public static double startCount;
   public static int printEvery;
   static public NetApplet myApplet;

   
   /* debug flags */   
   public static boolean Debug_Nodes=false;   
   public static boolean Debug_Routing=false; 
   public static boolean Debug_Results=false;

 
 Network(Topology T,String inputPattern,double numMsgParam,String routerType,String lookupTableType){   
    R = new Random();
    this.inputPattern = new String(inputPattern);
    this.numMsgParam = numMsgParam;
    this.routerType = new String(routerType);
    this.lookupTableType = new String(lookupTableType);    
    packetsCreators = new Vector();
    T.loadTopology(this);                   
    runNodes(routerType);            
 }

   protected Node createNode(){     
     Node temp;     
     
     /* create the node (not run yet) */     
     temp = new Node(this,numNodes);
     numNodes++;
     /* create packet creator for that node */
     PacketCreator tempcre = new PacketCreator(temp,this);
     /* add to network */
     addElement(temp);
     packetsCreators.addElement(tempcre);
     return (temp);
   }
   
   protected void createLink(Node N1,Node N2,int transferTime){
     /** create symmetrical link between the nodes */
      N1.addNeighbor(N2,transferTime);
      N2.addNeighbor(N1,transferTime);
   }
      
   protected Node chooseNode(){
     /** return random node */
     int i = R.nextInt(); 
     
     if (i<0) i=-i; /* abs */                      
     return (Node)elementAt(i%size());
   }
           

     protected void runNodes(String routerType) {
       Enumeration e = elements();
       Node node;
       Enumeration ep = packetsCreators.elements();      
       PacketCreator cre;
       
       Network.debugPrint("initiating the "+size()+" network nodes...");
       /* make init estimations */
       while (e.hasMoreElements()) {
         node = (Node)e.nextElement();                  
         node.BF = new BellmanFordEstimator(this,node);                   
       }
       /* init estimations */
       e = elements();
       while (e.hasMoreElements()) {
         node = (Node)e.nextElement();                  
         if (lookupTableType.equals("ARRAY")) 
            node.Q.getValue(node,node); /* first call->init */          
       }
       
       /* run network nodes */
       e = elements();
       Network.debugPrint("Starting the "+size()+" network nodes...");
       while (e.hasMoreElements()) {
         node = (Node)e.nextElement();                         
         node.start();         
         if (Network.Debug_Nodes)
           Network.debugPrint("Starting node thread:"+node);
       }   
       
       /* run packet creators for nodes */           
       Network.debugPrint("Running packet creators for the nodes...");
       while (ep.hasMoreElements()) {
         cre = (PacketCreator)ep.nextElement(); 
         cre.start(); 
         if (Network.Debug_Nodes)
            Network.debugPrint("Starting packet creating thread:"+cre);      
       }       
     }
                      
     public void stopNodes(){
      Enumeration e=elements();       
      Node node;
      PacketCreator cre;
      
       while (e.hasMoreElements()){
            node=(Node)e.nextElement();
            node.interrupt();    
        }
      e=packetsCreators.elements();      
      while (e.hasMoreElements()){
            cre=(PacketCreator)e.nextElement();
            cre.interrupt();     
       }       
     }
     
     public void printEstimates(){
       Enumeration e=elements();
       Node n;
       
       while (e.hasMoreElements()) {
             n = (Node)e.nextElement();
             Network.debugPrint("Node="+n);
             n.Q.printQValues();    
        }
       
     }
   	 
   public void printQueues(){
     Enumeration e=elements();
     Node n;
            
        while (e.hasMoreElements()) {
           n =(Node)e.nextElement();
           Network.debugPrint("Node "+n+":queue size="+n.nodeQueue.numItems()+","+n.incomingMsgQueue.numItems());
        }
   }
      
   	 public static void main_func(NetApplet myApplet,String[] args) {
        Topology T;
        Network NT;        
        String topologyTypeParam=null;   
        /* GRID6X6/SMALL */
        String inputPatternParam=null; 
        /* ONEDEST/RANDOM/UPLEFTTODOWNRIGHT */        
        String routerType = null;
        /* QROUTING/SHORTEST_PATHS */
        String lookupTableType = null;
        /* HASHTABLE/ARRAY*/
        double numMsgParam=0;        
        double startCount;
     
        Network.myApplet = myApplet;                 
        
        /* read parameters */    
        if (args.length != 9 ) {
           Network.debugPrint("Usage:learnnet.Network topologyType[GRID6X6/SMALL] inputPattern[ONEDEST/RANDOM/UPLEFTTODOWNRIGHT]  routingAlgorithm[QROUTING/SHORTEST_PATHS/DUALQROUTING] lookupTableType[HASHTABLE/ARRAY]  messagesNumber(number) startCount[FROMSTART/FROMHALF] debugNodes[ON/OFF] debugRouting[ON/OFF] debugResults[ON/OFF]");           
           System.exit(1);  
         }   
         topologyTypeParam=args[0];           
         inputPatternParam=args[1];         
         routerType=args[2];         
         lookupTableType=args[3];                  
         numMsgParam= new Double(args[4]).doubleValue();             
         startCount = (args[5].equals("FROMHALF"))?numMsgParam/2:0;
         Network.Debug_Nodes = args[6].equals("ON");
         Network.Debug_Routing = args[7].equals("ON");
         Network.Debug_Results = args[8].equals("ON");
         
         Network.debugPrint("Network Topology="+topologyTypeParam+",Input Pattern="+inputPatternParam+",Routing Algorithm="+routerType+",lookup Table Type="+lookupTableType+",Messages Num="+ numMsgParam+",start count from="+startCount+",Debug_Nodes="+args[6]+",Debug_Routing="+args[7]+",Debug_Results="+args[8]+".");  
            
       /* choose topology */       
        if (topologyTypeParam.equals("SMALL")) 
            T=new SMALLTopology();
        else if (topologyTypeParam.equals("GRID6X6")) 
            T=new GRID6X6Topology();    
        else throw new NoSuchElementException();    
        
        Network.startCount = startCount; 
        Network.printEvery = ((int)numMsgParam)/10;
        if (Network.printEvery<1) Network.printEvery = 1;
        NT= new Network(T,inputPatternParam,numMsgParam,routerType,lookupTableType);                
                      
        Network.debugPrint("Sending "+numMsgParam+" packets through the network...");                                  
        NT.networkStarted=true;
        while ( (! NT.packetsEnded));  /* packets creating and sending */               
        Network.debugPrint("Messages sent.waiting for messages to reach destinations...");
        while (PacketCreator.getDeliveredMsgNum()<0.95*NT.numMsgParam);         
           /* wait for messages to reach destinations (we only wait for 95% so that a few delaying ones will not bias the average)*/
        Network.debugPrint("Stopping the nodes...");
        NT.stopNodes();
        if ((Network.Debug_Results) && (!routerType.equals("SHORTEST_PATHS")))
           NT.printEstimates();               
        Network.debugPrint("Avg delivery time in Network per packet is:"+ PacketCreator.getAvgTime());             
        Network.debugPrint("simulation ended.");
    }     	
    
    public static void main(String[] args){
      main_func(null,args);
    }
    
    public static synchronized void debugPrint(String str) {
      if (Network.myApplet != null){
         myApplet.appendPrint(str+"\n");              
       }
         else System.out.println(str);
  
    }
    
} 
