package brn.sim.handler;

import java.io.IOException;

import brn.sim.data.AveragedTimeLine;
import brn.sim.data.XplotSerializer;

import jist.runtime.JistAPI;
import jist.swans.Constants;
import jist.swans.mac.Mac802_11;
import jist.swans.mac.AbstractMac.MacModeChanged;
import jist.swans.mac.AbstractMac.MacTxFinished;
import jist.swans.mac.AbstractMac.RadioModeChanged;
import jist.swans.misc.Event;
import jist.swans.net.AbstractNet;

public class BrnEventHandlers implements Runnable {

  // ////////////////////////////////////////////////
  // constants
  //

  // ////////////////////////////////////////////////
  // locals
  //

  private String name;

  private AveragedTimeLine[] avgRadioUtilization;

  private AveragedTimeLine[] avgMacUtilization;

  private AveragedTimeLine[] avgMacPacketThroughput;

  private AveragedTimeLine[] avgMacRetries;

  private AveragedTimeLine[] avgNetPacketThroughput;

  // ////////////////////////////////////////////////
  // initialization
  //

  public BrnEventHandlers(String name) {
    this.name = name;
    long ms100 = 100 * Constants.MILLI_SECOND;
    long ms250 = 250 * Constants.MILLI_SECOND;
    double scale = Constants.SECOND / ms250;

    avgRadioUtilization = new AveragedTimeLine[6];
    avgRadioUtilization[0] = new AveragedTimeLine("radio_idle_100m", ms100);
    avgRadioUtilization[1] = new AveragedTimeLine("radio_idle_250m", ms250);

    avgRadioUtilization[2] = new AveragedTimeLine("radio_rx_100m", ms100);
    avgRadioUtilization[3] = new AveragedTimeLine("radio_rx_250m", ms250);

    avgRadioUtilization[4] = new AveragedTimeLine("radio_tx_100m", ms100);
    avgRadioUtilization[5] = new AveragedTimeLine("radio_tx_250m", ms250);

    avgMacUtilization = new AveragedTimeLine[6];
    avgMacUtilization[0] = new AveragedTimeLine("mac_free_100m", ms100);
    avgMacUtilization[1] = new AveragedTimeLine("mac_free_250m", ms250);

    avgMacUtilization[2] = new AveragedTimeLine("mac_access_100m", ms100);
    avgMacUtilization[3] = new AveragedTimeLine("mac_access_250m", ms250);

    avgMacUtilization[4] = new AveragedTimeLine("mac_load_100m", ms100);
    avgMacUtilization[5] = new AveragedTimeLine("mac_load_250m", ms250);

    avgMacPacketThroughput = new AveragedTimeLine[3];
    avgMacPacketThroughput[0] = new AveragedTimeLine("mac_tx_packets_250m", ms250, AveragedTimeLine.MODE_C, scale);
    avgMacPacketThroughput[1] = new AveragedTimeLine("mac_tx_success_250m", ms250, AveragedTimeLine.MODE_C, scale);
    avgMacPacketThroughput[2] = new AveragedTimeLine("mac_tx_fail_250m", ms250, AveragedTimeLine.MODE_C, scale);

    //avgMacPacketThroughput[0] = new AveragedTimeLine("mac_tx_success_100m", 100 * Constants.MILLI_SECOND);
    //avgMacPacketThroughput[2] = new AveragedTimeLine("mac_tx_fail_100m", 100 * Constants.MILLI_SECOND);

    avgMacRetries = new AveragedTimeLine[2];
    avgMacRetries[0] = new AveragedTimeLine("mac_retries_100m", ms100, AveragedTimeLine.MODE_A, 1.);
    avgMacRetries[1] = new AveragedTimeLine("mac_retries_250m", ms250, AveragedTimeLine.MODE_A, 1.);

    avgNetPacketThroughput = new AveragedTimeLine[4];
    avgNetPacketThroughput[0] = new AveragedTimeLine("net_tx_250m", ms250, AveragedTimeLine.MODE_C, scale);
    avgNetPacketThroughput[1] = new AveragedTimeLine("net_rx_250m", ms250, AveragedTimeLine.MODE_C, scale);
    avgNetPacketThroughput[2] = new AveragedTimeLine("net_drop_250m", ms250, AveragedTimeLine.MODE_C, scale);
    avgNetPacketThroughput[3] = new AveragedTimeLine("net_q_250m", ms250);

    Event.addHandler(RadioModeChanged.class, new RadioModeChangedHandler());
    Event.addHandler(MacModeChanged.class, new MacModeChangedHandler());
    Event.addHandler(MacTxFinished.class, new MacTxFinishedHandler());

    NetTraceHandler handler = new NetTraceHandler();
    Event.addHandler(AbstractNet.EnqueueEvent.class, handler);
    Event.addHandler(AbstractNet.DequeueEvent.class, handler);
    Event.addHandler(AbstractNet.LinkDropEvent.class, handler);
    Event.addHandler(AbstractNet.RecvFromMacEvent.class, handler);
    Event.addHandler(AbstractNet.SendToMacEvent.class, handler);


    JistAPI.runAt(this, JistAPI.END);
  }

  // ////////////////////////////////////////////////
  // accessors
  //

  // ////////////////////////////////////////////////
  // overwrites
  //

  public void run() {
    try {
      save();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }

  // ////////////////////////////////////////////////
  // impl
  //

  protected class NetTraceHandler implements Event.Handler {
    public void handle(Event event) {
      if (event instanceof AbstractNet.EnqueueEvent) {
        AbstractNet.EnqueueEvent enq = (AbstractNet.EnqueueEvent) event;
        avgNetPacketThroughput[3].add(JistAPI.getTime(), enq.queue.size());
      }
      else if (event instanceof AbstractNet.DequeueEvent) {
        AbstractNet.DequeueEvent deq = (AbstractNet.DequeueEvent) event;
        avgNetPacketThroughput[3].add(JistAPI.getTime(), deq.queue.size());
      }
      else if (event instanceof AbstractNet.LinkDropEvent) {
        avgNetPacketThroughput[2].add(JistAPI.getTime(), 1.);
      }
      else if (event instanceof AbstractNet.RecvFromMacEvent) {
        avgNetPacketThroughput[1].add(JistAPI.getTime(), 1.);
      }
      else if (event instanceof AbstractNet.SendToMacEvent) {
        avgNetPacketThroughput[0].add(JistAPI.getTime(), 1.);
      }
    }
  }

  protected class MacTxFinishedHandler implements Event.Handler {
    public void handle(Event event) {
      long time = JistAPI.getTime();
      MacTxFinished finished = (MacTxFinished) event;

      avgMacPacketThroughput[0].add(time, 1.);
      if (finished.success)
        avgMacPacketThroughput[1].add(time, 1.);
      else
        avgMacPacketThroughput[2].add(time, 1. );

      avgMacRetries[0].add(time, finished.longRetry);
      avgMacRetries[1].add(time, finished.longRetry);
    }
  }

  protected class RadioModeChangedHandler implements Event.Handler {
    public void handle(Event event) {
      RadioModeChanged changed = (RadioModeChanged) event;
      long time = JistAPI.getTime();

      avgRadioUtilization[0].add(time,
          Constants.RADIO_MODE_IDLE == changed.newMode ? 1.0 : .0);
      avgRadioUtilization[1].add(time,
          Constants.RADIO_MODE_IDLE == changed.newMode ? 1.0 : .0);

      avgRadioUtilization[2].add(time,
          Constants.RADIO_MODE_RECEIVING == changed.newMode ? 1.0 : .0);
      avgRadioUtilization[3].add(time,
          Constants.RADIO_MODE_RECEIVING == changed.newMode ? 1.0 : .0);

      avgRadioUtilization[4].add(time,
          Constants.RADIO_MODE_TRANSMITTING == changed.newMode ? 1.0 : .0);
      avgRadioUtilization[5].add(time,
          Constants.RADIO_MODE_TRANSMITTING == changed.newMode ? 1.0 : .0);
    }
  }

  protected class MacModeChangedHandler implements Event.Handler {
    public void handle(Event event) {
      MacModeChanged changed = (MacModeChanged) event;

      double free = .0;
      double access = .0;
      double load = .0;

      switch(changed.newMode) {
      case Mac802_11.MAC_MODE_SIDLE:
        free = 1.;
        break;

      case Mac802_11.MAC_MODE_DIFS:
      case Mac802_11.MAC_MODE_SBO:
      case Mac802_11.MAC_MODE_SNAV:
//      case Mac802_11.MAC_MODE_SNAV_RTS:
      case Mac802_11.MAC_MODE_SWFCTS:
      case Mac802_11.MAC_MODE_SWFDATA:
      case Mac802_11.MAC_MODE_SWFACK:
        access = 1.;
        break;

      case Mac802_11.MAC_MODE_XRTS:
      case Mac802_11.MAC_MODE_XCTS:
      case Mac802_11.MAC_MODE_XUNICAST:
      case Mac802_11.MAC_MODE_XBROADCAST:
      case Mac802_11.MAC_MODE_XACK:
        load = 1.;
        break;
      }

      long time = JistAPI.getTime();
      avgMacUtilization[0].add(time, free);
      avgMacUtilization[1].add(time, free);

      avgMacUtilization[2].add(time, access);
      avgMacUtilization[3].add(time, access);

      avgMacUtilization[4].add(time, load);
      avgMacUtilization[5].add(time, load);
    }
  }

  public void save() throws IOException {
    XplotSerializer seri = new XplotSerializer("radio", "time", "percent");
    seri.addLine(avgRadioUtilization[0], "red");
    seri.addLine(avgRadioUtilization[1], "orange");
    seri.addLine(avgRadioUtilization[2], "blue");
    seri.addLine(avgRadioUtilization[3], "purple");
    seri.addLine(avgRadioUtilization[4], "green");
    seri.addLine(avgRadioUtilization[5], "yellow");
    seri.saveToFile(name + "-" + seri.getTitle() + ".xpl");

    seri = new XplotSerializer("mac", "time", "percent");
    seri.addLine(avgMacUtilization[0], "red");
    seri.addLine(avgMacUtilization[1], "orange");
    seri.addLine(avgMacUtilization[2], "blue");
    seri.addLine(avgMacUtilization[3], "purple");
    seri.addLine(avgMacUtilization[4], "green");
    seri.addLine(avgMacUtilization[5], "yellow");
    seri.saveToFile(name + "-" + seri.getTitle() + ".xpl");

    seri = new XplotSerializer("mac-packet", "time", "packets per sec");
    seri.addLine(avgMacPacketThroughput[0], "red");
    seri.addLine(avgMacPacketThroughput[1], "yellow");
    seri.addLine(avgMacPacketThroughput[2], "blue");
    seri.saveToFile(name + "-" + seri.getTitle() + ".xpl");

    seri = new XplotSerializer("mac-retries", "time", "retries");
    seri.addLine(avgMacRetries[0], "green");
    seri.addLine(avgMacRetries[1], "yellow");
    seri.saveToFile(name + "-" + seri.getTitle() + ".xpl");

    seri = new XplotSerializer("net-packet", "time", "pps / packets");
    seri.addLine(avgNetPacketThroughput[0], "red");
    seri.addLine(avgNetPacketThroughput[1], "yellow");
    seri.addLine(avgNetPacketThroughput[2], "blue");
    seri.addLine(avgNetPacketThroughput[3], "green");
    seri.saveToFile(name + "-" + seri.getTitle() + ".xpl");
  }

}
