// 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: EventBaseAget.java
// Path: eventbase
// Description: EventBaseAgent provides interface of EventBase component

package eventbase;
import java.util.*;
import eventbase.event.*;
import eventbase.link.*;
import eventbase.neighbor.*;
import eventbase.routing.*;

public class EventBaseAgent implements EventType, NeighborType{

  final String OBJECTNAME = "EventBaseAgent";
  final public static int LOGFILE_MODE = 0;
  final public static int NETWORK_MODE = 1;

  // 09-03 database buffer time.  database will buffer all info up to
  // WAIT_TIME_IN_SECOND before presenting it to GUI
  public static final int DEFAULT_WAIT_TIME_IN_SECOND = 10;

  private EventTableManager eventTblManager;
  private NeighborTableManager nbrTblManager;
  private LinkInfoTableManager linkInfoManager;
  private SpanningTreeTableManager sTreeManager;
  private MemoryManager memoryManager;

  private Vector hostMappingTable;
  private int hostIDCount;
  private int eventCount;
  private double systemBaseTime;  // System time of first recieved event
    // 09/09/00 - added for a more accurate time server time
  private double godBaseTime;     // time of global time server in second
  private double dbBufferTime;    // defult: assign to DEFAULT_WAIT_TIME_IN_SECOND

    private int monitorMode = -1;

  // 09/05/00 - ahcheng: fix getTimeOfLastEvent
  private double latestEventTime;

  public EventBaseAgent() {
    // initiate all the tables
    eventTblManager = new EventTableManager();
    nbrTblManager = new NeighborTableManager();
    memoryManager = new MemoryManager();
    linkInfoManager = new LinkInfoTableManager(memoryManager);
    sTreeManager = new SpanningTreeTableManager(memoryManager);

    hostMappingTable = new Vector();
    hostIDCount = 0;
    eventCount = 0;
    systemBaseTime = -1;
    godBaseTime = -1;
    latestEventTime = -1;
    dbBufferTime = DEFAULT_WAIT_TIME_IN_SECOND;
  }

  public int getDiskSize() {
    return memoryManager.getDiskSize();
  }

  public int getVMSize() {
    return memoryManager.getMemorySize();
  }

  public int getNeighborTableSize() {
    return nbrTblManager.getMemorySize();
  }

  public int getEventTableSize() {
    return eventTblManager.getMemorySize();
  }

  public double getDBBufferTime() {
    return dbBufferTime;
  }

    // 09/09/00 - added for a more accurate time server time
    // when monitor start, this time will be set to serve as a reference point to
    // calculate the future "time server(god)" time
  public void setGodBaseTime (double time) {
      if (godBaseTime == -1 && systemBaseTime == -1) {
	  godBaseTime = time;
	  Date t = new Date();
	  systemBaseTime = t.getTime();
      } else {
	  System.err.println("ERROR:EventBAseAgent:setGodBaseTime: try to set base time twice !!!");
      }
  }

  public void setMonitorMode (int mode) {
    monitorMode = mode;
  }

    public int getMonitorMode () {
      return monitorMode;
    }

  public synchronized void InsertEventIntoTables(double time, OverlayEvent e) {

    switch (e.getEventId()) {
      case JOIN_GROUP:   // JoinGroupEvent
      case LEAVE_GROUP:   // LeaveGroupEvent
      // TODO : remember leave group is different from Detect Dead Host
      case INTENT_TO_LEAVE:   // IntentToLeaveGroupEvent
        break;
      case ADD_LINK:   // AddLinkEvent
        nbrTblManager.addNeighbor(time, e.getSourceHost(), e.getTargetHost());
        break;
      case DROP_LINK:   // DropLinkEvent
        nbrTblManager.deleteNeighbor(time, e.getSourceHost(), e.getTargetHost(), DROP);
        break;
      case DETECT_DEAD_HOST:   // DetectDeadHostEvent
        nbrTblManager.deleteNeighbor(time, e.getSourceHost(), e.getTargetHost(), DEAD);
        break;
      default:
        System.err.println(OBJECTNAME + "InsertEventIntoTables: event not defined");
        return;
    }
    EventTableEntry tblEntry = new EventTableEntry(time, e.getSourceHost(), e);
    eventTblManager.InsertEventToTable(tblEntry);
    eventCount++;
  }

  public int getEventcount() {
    return eventCount;
  }

  // insert link information into table
  public synchronized void InsertLinkInfoIntoTable (double t, int srcID, int tgtID,
                                       int type, int val) {
    LinkInfoTableEntry entry = new LinkInfoTableEntry(t, srcID, tgtID, type, (double) val);
    linkInfoManager.InsertLinkInfoToTable (entry);
  }

  public synchronized void insertSpanningTree (double t, int rootID, AdjacencyMatrix matrix) {
    sTreeManager.insertMatrix(rootID, t, matrix);
  }

  public synchronized void insertVRTEntry (int id, double time, int dest,
					   int nextHop, int val, int dead,
					   int numChild, int [] childList) {
    sTreeManager.insertVRTEntry(id, time, dest, nextHop, val, dead, numChild, childList);
  }

  // get or assign the hostID for the host;
  public synchronized int getHostID(String nodeName) {
    int id = hostMappingTable.indexOf(nodeName.trim());
    if (id == -1) {// insert node into our table
      hostMappingTable.add(nodeName);
      id = hostIDCount;
      OverlayHost host = new OverlayHost(id, nodeName.toLowerCase());
      nbrTblManager.addMember(host);
      hostIDCount++ ;
    }
    return id;
  }

  // TODO: fix so ALL info_type work also, now assume only DELAY
  public DisplayLinkInfo getLinkInfo (double t, int srcID, int tgtID, int type) {
    return linkInfoManager.getLinkInfo(t, srcID, tgtID, type);
  }

  public String getHostName (int nodeID) {
    return (String) hostMappingTable.get(nodeID);
  }

  public void printEventTable() {
    eventTblManager.printTable();
  }

  public void printNeighborTable() {
    nbrTblManager.printAllNbrTable();
  }

  public void printSpanningTreeTables() {
    sTreeManager.printTable();
  }

  public void printMappingTable() {
    for (int i = 0; i < hostMappingTable.size(); i++) {
      System.out.println(i + " " + hostMappingTable.get(i));
    }
  }

  public void printLinkInfoTable() {
    linkInfoManager.printTable();
  }

  public int getNumOfOverlayHosts() {
    return hostMappingTable.size();
  }

  public void getNextEventInit(double time) {
    eventTblManager.getNextEventInit(time);
  }

  public EventTableEntry getNextEvent() {
    return eventTblManager.getNextEvent();
  }

  // 09-03-00 ahcheng
  // This function has been rewritten to support real time monitoring
  // Base time will be valid to GUI 60 second after db recieved its first
  // event.
  // The function return -1 when this information is not ready to be exposed.
  public double getBaseTime() {
    // to support log file mode
    if (monitorMode == LOGFILE_MODE)
      return eventTblManager.getTimeOfFirstEvent();
    // to support real time
    if (systemBaseTime == -1 || godBaseTime == -1)  // no base time info yet
      return -1;
    double firstEventTime = eventTblManager.getTimeOfFirstEvent();
    if (isTimeValid(firstEventTime)) // no first event yet
      return  firstEventTime;
    // either no event arrive, or not time to show it yet
    return -1;
  }

  // used by GUI to determine whether the slider time is valid
  public boolean isTimeValid(double checkedTimeInSec) {
    if (godBaseTime == -1 || systemBaseTime == -1 || checkedTimeInSec == -1)
      return false;
    Date curTimeObj = new Date();
    double curSysTime = curTimeObj.getTime();
    double sysToMonitorDifference = curSysTime - systemBaseTime;
    double monitorCurrentTime = godBaseTime + (double)sysToMonitorDifference / 1000;
    double validMonitorTime = monitorCurrentTime - dbBufferTime;

    if (checkedTimeInSec > validMonitorTime)
      return false;
    else
      return true;
  }

  public double getCurrentGodTime() {
      if (godBaseTime == -1 || systemBaseTime == -1)
	  return -1;
      Date curTimeObj = new Date();
      double curSysTime = curTimeObj.getTime();
      double sysToMonitorDifference = curSysTime - systemBaseTime;
      return godBaseTime + (double)sysToMonitorDifference / 1000;
  }

  // 09/05/00 - ahcheng: fix getTimeOfLastEvent
  public void setLatestEventTime(double t) {
      if (t > this.latestEventTime)
	  this.latestEventTime = t;
  }

  // 09/05/00 - ahcheng: fix getTimeOfLastEvent
  public double getTimeOfLastEvent() {
      //      System.out.println ("$$$$$$$$$$$$$$$$$$$$$$$ lastEventTime : " + latestEventTime);
      return this.latestEventTime;
      //    return eventTblManager.getTimeOfLastEvent();
  }

  public Vector getNbrTableForDisplay(int hostid, double time) {
    return nbrTblManager.getNbrTableForDisplay(hostid, time);
  }

  public Vector getSpanningTreeForDisplay (double time, int rootID) {
    return sTreeManager.getSpanningTreeForDisplay(time, rootID);
  }

  public Vector getRoutingTableForDisplay (double time, int rootID) {
    return sTreeManager.getVRTableForDisplay(time, rootID, hostMappingTable);
  }

  private void verifyHostID (int nodeID, String nodeName) {
    if (nodeID != hostMappingTable.indexOf(nodeName))
      System.err.println(OBJECTNAME + "verifyHostID: FAILED");
  }

}
