// Carnegie Mellon University
//   Information Networking Institute and
//   School of Computer Science
//
// Master Thesis: A Monitoring Tool for Overlay Network
// By: TungFai Chan and Annie Cheng
//
// File: SpanningTreeManager.java
// Path: eventbase/routing
// Description: Manage insertion and retrieval of spanning tree according to time

package eventbase.routing;
import java.util.*;
import eventbase.*;
import eventbase.btree.*;

public class SpanningTreeTableManager {

  final String OBJECT_NAME = "SpanningTreeTableManager";
  Vector memberArray;
  MemoryManager memoryManager;

  public SpanningTreeTableManager(MemoryManager m) {
    memberArray = new Vector();
    memoryManager = m;
  }

  public void insertMatrix(int id, double time, AdjacencyMatrix matrix) {
    SpanningTreeTable table = getTable(id);
    table.addSpanningTreeEntries(time, matrix);
  }

  public void insertVRTEntry(int id, double time, int dest, int nextHop,
                             int val, int dead, int numChild, int[] childList) {
    SpanningTreeTable table = getTable(id);
    table.addVRTEntry(time, dest, nextHop, val, dead, numChild, childList);
  }

  private SpanningTreeTable getTable(int id) {
    SpanningTreeTable table = null;
    if (memberArray.size() <= id)
      memberArray.setSize(id + 1);
    else
      table = (SpanningTreeTable) memberArray.get(id);
    if (table == null) {
      table = new SpanningTreeTable(memoryManager);
      memberArray.set(id, table);
    }
    return table;
  }

  public Vector getSpanningTreeForDisplay (double time, int rootID) {
    SpanningTreeTable table = getTable(rootID);
    return table.getSpanningTreeForDisplay(time);
  }

  public Vector getVRTableForDisplay (double time, int rootID, Vector hostMappingTable) {
    SpanningTreeTable table = getTable(rootID);
    return table.getVRTableForDisplay(time, hostMappingTable);
  }

  public void printTable() {
    System.out.println();
    System.out.println("==============Spanning Tree ==================");
    for (int i = 0; i < memberArray.size(); i++) {
        SpanningTreeTable table = (SpanningTreeTable) memberArray.get(i);
        if (table != null) {
          System.out.println("Spanning tree with root " + i);
          table.printSpanningTree();
          System.out.println();
          System.out.println("Virtual Routing Table with root " + i);
          table.printVRTable();
        }
        System.out.println();
    }
    System.out.println(" ==============================================");
  }
}


class SpanningTreeTable {

  final String OBJECT_NAME = "SpanningTreeTable";
  private BTree spanningTreeTable;
  private BTree VRTable;
  private BTree childTable;
  private MemoryManager memoryManager;

  SpanningTreeTable(MemoryManager m) {
    spanningTreeTable = new BTree();
    VRTable = new BTree();
    childTable = new BTree();
    memoryManager = m;
  }

  void addSpanningTreeEntries (double time, AdjacencyMatrix matrix) {
    for (int i=0; i < matrix.size(); i++) {
      for (int j = 0; j < matrix.size(); j++) {
        if (matrix.getCoordinate(i, j)) {
          SpanningTreeTableEntry entry = new SpanningTreeTableEntry(time, i, j);
          memoryManager.insertRecord(spanningTreeTable, entry.getTime(), entry.getBytes());
        }
      }
    }
  }

  // find the spanning tree data happen before <time>
  public Vector getSpanningTreeForDisplay (double time) {
    Vector retVal = new Vector();
    memoryManager.setIterationPointer(spanningTreeTable, time);

    double cur_time = -1;
    SpanningTreeTableEntry tblEntry = null;
    byte[] buffer;

    while ((buffer = memoryManager.getPrevRecord(spanningTreeTable))!= null) {
      tblEntry = SpanningTreeTableEntry.extractFromBytes(buffer);
      if (cur_time == -1)
        cur_time = tblEntry.getTime();
      if (tblEntry.getTime() != cur_time) // done
        return retVal;
      retVal.addElement(tblEntry);
    }
    return retVal;
  }

  void printSpanningTree() {
    SpanningTreeTableEntry entry;
    byte[] buffer;
    memoryManager.setIterationPointer(spanningTreeTable, 0);
    while ((buffer = memoryManager.getNextRecord(spanningTreeTable)) != null) {
      entry = SpanningTreeTableEntry.extractFromBytes(buffer);
      entry.print();
    }
//    spanningTreeTable.printTree(spanningTreeTable.getRoot());
  }

  public void addVRTEntry(double t, int dest, int next, int cost, int dead, int numChild, int[] childList) {
    VirtualRoutingTableEntry entry = new VirtualRoutingTableEntry(t, dest, next, cost, dead, numChild);
    memoryManager.insertRecord(VRTable, entry.getTime(), entry.getBytes());
    // insert child entry into the tree
    VRTChildrenListEntry childEntry;
    for (int i = 0; i < numChild; i++) {
	childEntry = new VRTChildrenListEntry(t, dest, childList[i]);
	memoryManager.insertRecord(childTable, childEntry.getTime(), childEntry.getBytes());
    }
  }

  // find the spanning tree data happen before <time>
  public Vector getVRTableForDisplay (double time, Vector hostMappingTable) {

    Vector retVal = new Vector();
    memoryManager.setIterationPointer(VRTable, time);

    double cur_time = -1;
    VirtualRoutingTableEntry tblEntry = null;
    byte[] buffer;

    while ((buffer = memoryManager.getPrevRecord(VRTable))!= null) {
      tblEntry = VirtualRoutingTableEntry.extractFromBytes(buffer);
      OverlayHost dest =
          new OverlayHost(tblEntry.getDestID(),
                          (String) hostMappingTable.get(tblEntry.getDestID()));
      OverlayHost nextHop;
      if (tblEntry.getNextHopID() != -1)
          nextHop =
              new OverlayHost (tblEntry.getNextHopID(),
                               (String) hostMappingTable.get(tblEntry.getNextHopID()));
      else
          nextHop = new OverlayHost(tblEntry.getNextHopID(), "Not Reachable");

      tblEntry.setOverlayHostForDisplay(dest, nextHop);

      if (cur_time == -1)
        cur_time = tblEntry.getTime();
      if (tblEntry.getTime() != cur_time) // done
	  break; // out of while loop
      retVal.addElement(tblEntry);
    }

    Vector childEntries = getChildEntries(cur_time, hostMappingTable);
    if (childEntries.size() > 0)
	placeChildList(retVal, childEntries);
    return retVal;
  }

    // with the time provided, we should be able to retrieved the entries
    // specified by the time, with exact time match
    // get the child entries associated with this VRT table
  Vector getChildEntries(double time, Vector hostMappingTable) {
      Vector retVal = new Vector();
      memoryManager.setIterationPointer(childTable, time + 0.000001);
      VRTChildrenListEntry tblEntry = null;
      byte[] buffer;
      while ((buffer = memoryManager.getPrevRecord(childTable))!= null) {
	  tblEntry = VRTChildrenListEntry.extractFromBytes(buffer);
	  tblEntry.setChildStringForDisplay((String) hostMappingTable.get(tblEntry.getChildID()));
	  if (tblEntry.getTime() != time) // done
	      return retVal; // out of while loop
	  retVal.addElement(tblEntry);
      }
      return retVal;
  }

    // place child from childEntriesVector into the cooresponding VRT entries
  void placeChildList(Vector vrtEntries, Vector childEntries) {

      VirtualRoutingTableEntry vrtEntry;
      VRTChildrenListEntry childEntry;
      /* debugging code
      for (int i = 0; i < childEntries.size(); i++) {
	  childEntry = (VRTChildrenListEntry) childEntries.elementAt(i);
	  childEntry.print();
      }
      for (int i = 0; i < vrtEntries.size(); i++) {
	  vrtEntry = (VirtualRoutingTableEntry) vrtEntries.elementAt(i);
	  vrtEntry.print();
      }
      */
      for (int i = 0; i < childEntries.size(); i++) {
	  childEntry = (VRTChildrenListEntry) childEntries.elementAt(i);
	  for (int j = 0; j < vrtEntries.size(); j++) {
	      vrtEntry = (VirtualRoutingTableEntry) vrtEntries.elementAt(j);
	      if (vrtEntry.getTime() != childEntry.getTime()) {
		  System.err.println ("ERROR: SpanningTreeTableManager: placeChildList()");
		  return;
	      }
	      if (vrtEntry.getDestID() == childEntry.getDestID()) {
		  vrtEntry.addChild(childEntry.getChildStr(), childEntry.getChildID());
		  break;
	      }
	  }
      }


      // validating
      for (int i = 0; i < vrtEntries.size(); i++) {
	  vrtEntry = (VirtualRoutingTableEntry) vrtEntries.elementAt(i);
	  if (!vrtEntry.gotAllChildren())
	      System.err.println("ERROR: SpanningTreeTableManager:CHILD_LOST !! - VRT Entry hasn't get all children");
      }

  }

  void printVRTable() {
    VirtualRoutingTableEntry entry;
    byte[] buffer;
    memoryManager.setIterationPointer(VRTable, 0);
    while ((buffer = memoryManager.getNextRecord(VRTable)) != null) {
      entry = VirtualRoutingTableEntry.extractFromBytes(buffer);
      entry.print();
    }
  }

  void printChildListTable() {
      VRTChildrenListEntry entry;
      byte[] buffer;
      memoryManager.setIterationPointer(childTable, 0);
      while ((buffer = memoryManager.getNextRecord(childTable)) != null) {
	  entry = VRTChildrenListEntry.extractFromBytes(buffer);
	  entry.print();
      }

  }

}
