// 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: SpanningTreeGlobalConstruct.java
// Path: evetbase/routing
// Description: Adjacency matrix is used as a representation of packet
//              route spanning tree

package eventbase.routing;
import eventbase.*;
import java.util.*;

public class SpanningTreeGlobalConstruct {

  // value set at VRTStart(), reset at VRTEnd()
  private double startTime;
  private double lastDumpTime;
  private EventBaseAgent dbAgent;

  // Vector of a collection of AdjacencyMatrix
  private AdjacencyMatrix [] srcCol;
  // used to determine the first dump
  private boolean [] firstDumpMap;
  private int firstDumpCount;
  private int curVRTHostCount;

  private int srcCount;
  private boolean hasInit;

  public SpanningTreeGlobalConstruct () {
    hasInit = false;
  }

  public synchronized boolean isInit() {
    return hasInit;
  }

  // every updateTimeInterval, current spanning tree view will be dumped into dbAgent
  public synchronized void init(int initSize, EventBaseAgent dbagent) {
      //    System.out.println (" $$$$$$$$$$$$$$$ SpanningTreeGlobalConstruct INIT " );
    this.hasInit = true;
    this.dbAgent = dbagent;
    this.startTime = -1;
    this.lastDumpTime = -1;
    this.srcCol = new AdjacencyMatrix[initSize];
    for (int i = 0; i < initSize; i++)
      this.srcCol[i] = null;
    this.firstDumpMap = new boolean[initSize];
    for (int i = 0; i < initSize; i++)
      this.firstDumpMap[i] = false;
    this.srcCount = this.firstDumpCount = initSize;
    this.curVRTHostCount = 0;
  }

  // set time for this run;
  // if matrixSize is greater than curMatrixSize, resize all matrix
  public synchronized void VRTStart(double timeStamp, int matrixSize, int localHostID) {
      //    System.out.println("$$$$$$$$ VRTStart - matrix size : " + matrixSize + " local Host ID " + localHostID);
    if (matrixSize > this.srcCount)
      this.resizeSrcCol(matrixSize);
    this.startTime = timeStamp;
    if (!this.firstDumpMap[localHostID]) {
      this.curVRTHostCount++;
      this.firstDumpMap[localHostID] = true;
    }
  }

  // reset the startTime;  dump the global view if the time is up
  public synchronized void VRTEnd() {
    if (this.isTimeToDump()) // do a dump to database
      dumpAllToDB();
    this.startTime = -1;
  }

  public synchronized void setMatrixCoordinate (int srcID, int forwardingHostID, int recvHostID) {
      //    System.out.println ("$$$$$ SetMatrixCoordinate " + srcID + " " + forwardingHostID + " " + recvHostID);
    AdjacencyMatrix matrix = this.getMatrix(srcID);
    matrix.setCoordinate(forwardingHostID, recvHostID);
  }

  public synchronized void cleanRow (int srcID, int forwardingHostID) {
    AdjacencyMatrix matrix = this.getMatrix(srcID);
    if (matrix != null)
      matrix.cleanRow(forwardingHostID);
  }

  // get AdjacencyMatrix from srcCol identify by srcID
  private AdjacencyMatrix getMatrix(int srcID) {
    if (srcID >= this.srcCount) // resize the srcCol array
      resizeSrcCol(srcID + 1);
    // maybe null;
    if (srcCol[srcID] == null)
      srcCol[srcID] = new AdjacencyMatrix(this.srcCount);
    return srcCol[srcID];
  }

  private void resizeSrcCol(int newSize) {
    AdjacencyMatrix [] newSrcCol = new AdjacencyMatrix[newSize];
    for (int i = 0; i < this.srcCount; i++) {
      // resize all matrix in collection
      // ahcheng - 0904 bug fix -- null pointer exception
      if (srcCol[i] != null)
	  srcCol[i].resize(newSize);
      // assign each to new source collection as resize operation
      newSrcCol[i] = srcCol[i];
    }
    // init new array
    for (int i = this.srcCount; i < newSize; i++)
      newSrcCol[i] = null;
    srcCol = newSrcCol;
    boolean [] newFirstDumpMap = new boolean[newSize];
    for (int i = 0; i < this.srcCount; i++)
      newFirstDumpMap[i] = firstDumpMap[i];
    for (int i = this.srcCount; i < newSize; i++)
      newFirstDumpMap[i] = false;
    firstDumpMap = newFirstDumpMap;
    this.srcCount = newSize;
  }


  // return true if it's time to dump the host
  // NOTE: this function can be customized for different application
  // to determine how often eventbase recieve new updates of spanning
  // tree
  // our heruistic here is first dump will occur when all the initial source has
  // at least one VRT table being parsed, subsequent dump will occur in the
  // next updateInterval period.
  // 09/09/00 Modified
  private boolean isTimeToDump() {
      return true; // 09/12/00 - always dump, spanning tree queue added for both mode
    /*
    if (dbAgent.getMonitorMode() == dbAgent.LOGFILE_MODE)
	return ((this.lastDumpTime == -1 && // never dump before
		 this.firstDumpCount == this.curVRTHostCount));
    else  // always dump when in real time mode
	return true;
    */
  }
  
  // First dump to the database can be determined by "firstDumpCount" variable.
  // current hurestic states first dump to database will occur when the number
  // source that has VRT table parsed in is equal to the "firstDumpCount".
  // this value is default to be initSize satated at SpanningTreeGlobalConstruct's
  // constructor.
  public synchronized void setFirstDumpCount(int newVal) {
    this.firstDumpCount = newVal;
  }

  // go thru all the adjacency matrix collected so far, dump them to database
  // using the latest VRTStart time stamp
  private void dumpAllToDB() {
    AdjacencyMatrix matrix;
    for (int i = 0; i < this.srcCount; i++) {
      matrix = this.srcCol[i];
      if (matrix != null)
        this.dbAgent.insertSpanningTree(this.startTime, i, matrix);
    }
    this.lastDumpTime = this.startTime;
  }

  public synchronized void print() {
    System.out.println("===============SpanningTreeGlobalConstruct========");
    for (int i = 0; i < this.srcCount; i++) {
      System.out.println("Src ID: " + i);
      if (this.srcCol[i] != null)
        this.srcCol[i].print();
      System.out.println();
    }
    System.out.println("=================================================");
  }
}
