package brn.sim.handler;

import jist.swans.Constants;
import jist.swans.mac.AbstractMac;
import jist.swans.misc.Event;
import jist.swans.radio.AbstractRadio;
import brn.sim.DataManager;
import brn.sim.DataManager.DataContributor;
import brn.sim.data.AbstractDiagramData;
import brn.sim.data.AveragedTimeLine;
import brn.sim.data.DiagramData;
import brn.sim.data.DiagramDataHist;

public class StatsRadioHandler extends DataContributor {

  public class NodeData {
    protected DiagramData radioUtilization = null;
    protected DiagramData radioSend = null;
    protected DiagramData radioReceive = null;
    protected DiagramData radioDrop = null;

    protected int id;
    private String category;

    public NodeData(int id) {
      this.id = id;
      this.category = getCategory(id);
    }

    public String getCategory(int id) {
      return "Node " + Integer.toString(id);
    }

    public String getCategory() {
      return category;
    }

    protected DiagramData getRadioSend() {
      // Radio send counter
      if (null == radioSend) {
        radioSend = new DiagramData(
            new String[] {category, "Radio", "send vs time"}, "time", "s",
                "radio send packets", "n/a", AbstractDiagramData.MARK);
        addData(radioSend, DataManager.LEVEL_ALL);

        DiagramData radioSendAvg = new DiagramData(
            new AveragedTimeLine("send (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "send (avg)"}, "time", "s",
                "radio send packets", "n/a");
        radioSend.addChain(radioSendAvg);
        addData(radioSendAvg, DataManager.LEVEL_ADDITIONAL);
      }
      return radioSend;
    }

    protected DiagramData getRadioDrop() {
      // Radio drop counter
      if (null == radioDrop) {
        radioDrop = new DiagramData(
            new String[] {category, "Radio", "drop vs time"}, "time", "s",
                "radio drop packets", "n/a", AbstractDiagramData.MARK);
        addData(radioDrop, DataManager.LEVEL_ALL);

        DiagramData radioDropAvg = new DiagramData(
            new AveragedTimeLine("drop (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
              new String[] {category, "Radio", "drop (avg)"}, "time", "s",
                "radio drop packets", "n/a");
        radioDrop.addChain(radioDropAvg);
        addData(radioDropAvg, DataManager.LEVEL_ADDITIONAL);
      }
      return radioDrop;
    }

    protected DiagramData getRadioReceive() {
      // Radio receive counter
      if (null == radioReceive) {
        radioReceive = new DiagramData(
            new String[] {category, "Radio", "receive vs time"}, "time", "s",
                "radio receive packets", "n/a", AbstractDiagramData.MARK);
        addData(radioReceive, DataManager.LEVEL_ALL);

        DiagramData radioReceiveAvg = new DiagramData(
            new AveragedTimeLine("receive (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "receive (avg)"}, "time", "s",
                "radio recv packets", "n/a");
        radioReceive.addChain(radioReceiveAvg);
        addData(radioReceiveAvg, DataManager.LEVEL_ADDITIONAL);
      }
      return radioReceive;
    }

    protected DiagramData getRadioUtilization() {
      if (null == radioUtilization) {
        radioUtilization = new DiagramData(
            new AveragedTimeLine("radio utilization", sampleLen),
            new String[] {category, "Radio", "utilization"},
                "time", "s", "medium utilization", "fraction");
        addData(radioUtilization, DataManager.LEVEL_ADDITIONAL);
      }

      return radioUtilization;
    }
  }

  public class GlobalData {
    private static final String category = "Global";

    protected DiagramData radioSend = null;
    protected DiagramData radioReceive = null;
    protected DiagramData radioDrop = null;

    private DiagramDataHist radioSendHist = null;

    private DiagramDataHist  radioReceiveHist = null;

    private DiagramDataHist radioDropHist = null;
    protected DiagramDataHist radioUtilizationMax = null;

    protected DiagramData getRadioDrop() {
      // Radio drop counter
      if (null == radioDrop) {
        radioDrop = new DiagramData(
            new AveragedTimeLine("drop (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
              new String[] {category, "Radio", "drop (avg)"}, "time", "s",
                "radio drop packets", "n/a");
        addData(radioDrop, DataManager.LEVEL_ADDITIONAL);
      }
      return radioDrop;
    }

    protected DiagramDataHist getRadioDropHist() {
      // Radio drop counter
      if (null == radioDropHist) {
        radioDropHist = new DiagramDataHist(
            new String[] {category, "Radio", "drop (hist)"},
            "addr", "ip", "occurence", "count");
        addData(radioDropHist, DataManager.LEVEL_BASIC);
      }
      return radioDropHist;
    }

    protected DiagramData getRadioSend() {
      // Radio send counter
      if (null == radioSend) {
        radioSend = new DiagramData(
            new AveragedTimeLine("send (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "send (avg)"}, "time", "s",
                "radio send packets", "n/a");
        addData(radioSend, DataManager.LEVEL_ALL);
      }
      return radioSend;
    }

    protected DiagramDataHist getRadioSendHist() {
      // Radio send counter
      if (null == radioSendHist ) {
        radioSendHist = new DiagramDataHist(
            new String[] {category, "Radio", "send (hist)"},
            "addr", "ip", "occurence", "count");
        addData(radioSendHist, DataManager.LEVEL_BASIC);
      }
      return radioSendHist;
    }

    protected DiagramData getRadioReceive() {
      // Radio receive counter
      if (null == radioReceive) {
        radioReceive = new DiagramData(
            new AveragedTimeLine("receive (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "receive (avg)"}, "time", "s",
                "radio recv packets", "n/a");
        addData(radioReceive, DataManager.LEVEL_ADDITIONAL);
      }
      return radioReceive;
    }

    protected DiagramDataHist getRadioReceiveHist() {
      // Radio receive counter
      if (null == radioReceiveHist ) {
        radioReceiveHist = new DiagramDataHist(
            new String[] {category, "Radio", "receive (hist)"},
            "addr", "ip", "occurence", "count");
        addData(radioReceiveHist, DataManager.LEVEL_BASIC);
      }
      return radioReceiveHist;
    }

    protected DiagramDataHist getRadioUtilization() {
      if (null == radioUtilizationMax) {
        radioUtilizationMax = new DiagramDataHist(
            new String[] {category, "Radio", "utilization (max)"},
                "radio", "id", "medium utilization", "fraction airtime",
                DiagramDataHist.MODE_MAX);
        addData(radioUtilizationMax, DataManager.LEVEL_IMPORTANT);
      }

      return radioUtilizationMax;
    }
  }

  private static final String ID = "StatsRadioHandler";

  private NodeData[] nodeData;

  private GlobalData globalData;

  private double sampleLen;

  public StatsRadioHandler(double sampleLen) {
    this.sampleLen = sampleLen;
    this.nodeData = new NodeData[20];
    this.globalData = new GlobalData();
  }

  /*
   * (non-Javadoc)
   * @see brn.sim.DataManager.DataContributor#getId()
   */
  public String getId() {
    return ID;
  }

  protected NodeData getData(int id) {
    try {
      if (null == nodeData[id])
        nodeData[id] = new NodeData(id);
    }
    catch (ArrayIndexOutOfBoundsException e) {
      if (id > 50000)
        throw new RuntimeException("id to large");
      NodeData[] tmp = new NodeData[id + 100];
      System.arraycopy(nodeData, 0, tmp, 0, nodeData.length);
      nodeData = tmp;
      return getData(id);
    }
    return nodeData[id];
  }

  public void registerHandlers() {
    super.registerHandlers();

    // Node Handlers

    // Compute radio utilization
    Event.addHandler(AbstractMac.RadioModeChanged.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.RadioModeChanged ev = (AbstractMac.RadioModeChanged) event;
        getData(ev.nodeId).getRadioUtilization().addNextTimePoint(ev.time,
            Constants.RADIO_MODE_IDLE == ev.newMode ? .0 : 1.);
        // TODO
//        globalData.getRadioUtilization().addNextValue(ev.nodeId, value)
      }
    });

    Event.addHandler(AbstractRadio.RadioSendEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRadio.RadioSendEvent ev = (AbstractRadio.RadioSendEvent) event;
        getData(ev.nodeId).getRadioSend().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioSend().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioSendHist().addNextValue(ev.radioInfo.getId());
      }
    });

    Event.addHandler(AbstractRadio.RadioReceiveEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRadio.RadioReceiveEvent ev = (AbstractRadio.RadioReceiveEvent) event;
        getData(ev.nodeId).getRadioReceive().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioReceive().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioReceiveHist().addNextValue(ev.radioInfo.getId());
      }
    });

    Event.addHandler(AbstractRadio.RadioDropEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRadio.RadioDropEvent ev = (AbstractRadio.RadioDropEvent) event;
        getData(ev.nodeId).getRadioDrop().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioDrop().addNextTimePoint(ev.time, ev.radioInfo.getId());
        globalData.getRadioDropHist().addNextValue(ev.radioInfo.getId());
      }
    });

//    // Global Handlers
//    Event.addHandler(AbstractRadio.RadioSendEvent.class, new Event.Handler() {
//      public void handle(Event event) {
//        AbstractRadio.RadioSendEvent ev = (AbstractRadio.RadioSendEvent) event;
//      }
//    });
//
//    Event.addHandler(AbstractRadio.RadioReceiveEvent.class, new Event.Handler() {
//      public void handle(Event event) {
//        AbstractRadio.RadioReceiveEvent ev = (AbstractRadio.RadioReceiveEvent) event;
//      }
//    });
//
//    Event.addHandler(AbstractRadio.RadioDropEvent.class, new Event.Handler() {
//      public void handle(Event event) {
//        AbstractRadio.RadioDropEvent ev = (AbstractRadio.RadioDropEvent) event;
//      }
//    });
  }

}
