/* Learning Net - shlomy boshy 031868912
 * Reinforcement Learning network simulation  
 */
package learnnet;
import java.util.*;

public class ARRAYlookupTable extends Vector implements lookupTable {
  /* interface for the Q(neighbor,destination) table */
  /* efficient */
  /* array should be copied to increased array if new nodes are created in the network (not implemented here) */
   
          
   public static final double DEFAULT_VALUE=999999;          
/*          
   private Vector destIndex;   
   public Hashtable neighbors;
   private Random RN;
 */ 
   
   Network NT;
   Node mynode;
   double[][] QValue=null;   
   double[] minQforDest=null;
   Node[] minNodeForDest=null;
   Hashtable neighbors;
      
   
   
   ARRAYlookupTable(Network NT,Node mynode){
     super(); 
     this.NT=NT;         
     this.mynode = mynode;
     neighbors = new Hashtable();
   }  
                    
  private void initQValue(){
  /* after nodes started running -> now network is complete */ 
    Enumeration e;
  
    QValue = new double[NT.numNodes][NT.numNodes];    
    minQforDest = new double[NT.numNodes];    
    minNodeForDest = new Node[NT.numNodes];    

    for (int i=0;i<NT.numNodes;i++)
      for (int j=0;j<NT.numNodes;j++) 
        QValue[i][j]=DEFAULT_VALUE; 
    /* init mins as B.F. */    
    for (int i=0;i<NT.numNodes;i++){
         if (i!=mynode.nodeNumber) {
           minNodeForDest[i] = mynode.BF.getMinValueNode((Node)NT.elementAt(i));             
           minQforDest[i] = minNodeForDest[i].BF.getValue((Node)NT.elementAt(i));      
         }         
    }                 
  }

  public synchronized Node getMinNodeForDest(Node dest) throws NoSuchElementException {
      if (QValue==null) initQValue();                     
      return minNodeForDest[dest.nodeNumber];   
  }
                  
  public synchronized double getValue(Node N,Node dest) throws NoSuchElementException {
     /* returns Q(N,dest) */
  
     if (QValue==null) initQValue();     
     if (QValue[N.nodeNumber][dest.nodeNumber]!=DEFAULT_VALUE)
         return QValue[N.nodeNumber][dest.nodeNumber];              
        else return N.BF.getValue(dest);        
  }
     
  public synchronized void setValue(Node N,Node dest,double value) throws NoSuchElementException{  
     if (QValue==null) initQValue();     
     QValue[N.nodeNumber][dest.nodeNumber]= value;        
     if (value < minQforDest[dest.nodeNumber]) {
         minQforDest[dest.nodeNumber] = value;
         minNodeForDest[dest.nodeNumber] = N;   
       }
   }  

   public synchronized void addNeighbor(Node N,double transferTime){                 
      neighbors.put(N,String.valueOf(transferTime));
   }
   
   public synchronized Enumeration getKeys(Node dest) throws NoSuchElementException{
   /** keys list of neighbors for 'dest'. returns null if no keys for dest */
    return null;
 }
 
 public synchronized double getTransferTimeTo(Node N) throws NoSuchElementException {
   /* get transfer time from 'this' to neighbor 'N' */
   Object val;
   if ((val=neighbors.get(N))==null)
     throw new NoSuchElementException();
     else return (new Double((String)val).doubleValue());             
     
  }

 public synchronized Node getUnknownNeighbor(Node dest){
  return null;   
  }


  public synchronized Node getRandomNeighbor(){
     return null;        
  }

  public int getNumNeighbors(){
    return neighbors.size();    
  }

  public int getNumKeys(Node dest){
    return -1;
  }

  public void printQValues(){
     return;     
  }

 public Enumeration getNeighbors(){
    return neighbors.keys();        
  }
 

  public void printNeighbors(){
    Enumeration e=neighbors.keys();    
    Node node;
    while (e.hasMoreElements()){
       node = (Node)e.nextElement();
       Network.debugPrint("Neighbor="+node);
    }
  }
 
  public static void main(String args[]) {
     /** driver to check ARRAYlookupTable */
  ARRAYlookupTable HQ = new ARRAYlookupTable(null,null);
  Node x1=new Node(null,0);
  Node x2=new Node(null,1);
  Node x3=new Node(null,2);
  Node ds1=new Node(null,3);
  Node ds2=new Node(null,4);
  Enumeration e;
  double val;
  
  HQ.addNeighbor(x1,1);
  HQ.addNeighbor(x2,2);
  HQ.addNeighbor(x3,3);
  Network.debugPrint("Time to X2=" + HQ.getTransferTimeTo(x2));
  Network.debugPrint("Time to X3=" + HQ.getTransferTimeTo(x3));
  //Network.debugPrint("Time to DS1=" + HQ.getTransferTimeTo(ds1));

  //Network.debugPrint("Val=" + HQ.getValue(ds2,ds1)); //exception
  Network.debugPrint("Val=" + HQ.getValue(x1,ds1)); // 0
  HQ.setValue(x1,ds1,0.5);
  Network.debugPrint("Val=" + HQ.getValue(x1,ds1)); // 0.5
  HQ.setValue(x2,ds1,0.3);
  HQ.setValue(x2,ds2,0.1);
  Network.debugPrint("Val=" + HQ.getValue(x1,ds1)); // 0.5
  Network.debugPrint("Val=" + HQ.getValue(x2,ds1)); // 0.3
  Network.debugPrint("Val=" + HQ.getValue(x2,ds2)); // 0.1
  Network.debugPrint("Val=" + HQ.getValue(x1,ds2)); // 0

  
  Network.debugPrint("Neighbors");
  e = HQ.getNeighbors(); 
  while (e.hasMoreElements()) {
        x1 = (Node)e.nextElement();
        val = HQ.getValue(x1,ds1);
        Network.debugPrint("Val=" + val); 
  }
  

 }
 
}