package brn.sim.handler;

import jist.swans.Constants;
import jist.swans.misc.Event;
import jist.swans.misc.Util;
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 StatsRadioDivHandler extends DataContributor {

  public class NodeData {
    protected DiagramData delaySpread = null;
    protected DiagramData powerDelta = null;

    protected int id;
    private String category;
    private DiagramData  txDivOccurance  =null;
    private DiagramData powerDeltaAvg = null;
    private DiagramData delaySpreadAvg = null;

    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 getDelaySpread() {
      if (null == delaySpread) {
        delaySpread = new DiagramData(new String[] {category, "Radio",
            "txdiv delay spread"}, "time", "s", "delay spread", "us",
            AbstractDiagramData.MARK);
        addData(delaySpread, DataManager.LEVEL_ALL);
      }
      return delaySpread;
    }
    
    protected DiagramData getDelaySpreadAvg() {
      if (null == delaySpreadAvg) {
        delaySpreadAvg = new DiagramData(
            new AveragedTimeLine("txdiv delay spread (avg)", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category, "Radio", "txdiv delay spread (avg)"}, 
            "time", "s", "delay spread", "us");
        addData(delaySpreadAvg, DataManager.LEVEL_BASIC);
      }
      return delaySpreadAvg;
    }

    protected DiagramData getPowerDelta() {
      if (null == powerDelta) {
        powerDelta = new DiagramData(new String[] {category, "Radio",
            "txdiv power delta"}, "time", "s", "rx power delta", "dBm",
            AbstractDiagramData.MARK);
        addData(powerDelta, DataManager.LEVEL_ALL);
      }
      return powerDelta;
    }

    protected DiagramData getPowerDeltaAvg() {
      if (null == powerDeltaAvg ) {
        powerDeltaAvg = new DiagramData(
            new AveragedTimeLine("txdiv power delta (avg)", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category, "Radio", "txdiv power delta (avg)"},
            "time", "s", "rx power delta", "dBm");
        addData(powerDeltaAvg, DataManager.LEVEL_BASIC);
      }
      return powerDeltaAvg;
    }

    protected DiagramData getTxDivOccurance() {
      if (null == txDivOccurance  ) {
        txDivOccurance = new DiagramData(
            new AveragedTimeLine("txdiv occurance (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "txdiv occurance (avg)"},
            "time", "s", "occurances", "per sec");
        addData(txDivOccurance, DataManager.LEVEL_BASIC);
      }
      return txDivOccurance;
    }
  }

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

    protected DiagramData delaySpread = null;
    protected DiagramData powerDelta = null;
    protected DiagramDataHist txDivRecvDrop = null;
    protected DiagramData txDivDrop = null;
    private DiagramData delaySpreadAvg;
    private DiagramData powerDeltaAvg;
    private DiagramData txDivOccurance;

    private DiagramDataHist txDivDropHist; 

    protected DiagramData getDelaySpread() {
      if (null == delaySpread) {
        delaySpread = new DiagramData(new String[] {category, "Radio",
            "txdiv delay spread"}, "time", "s", "delay spread", "us",
            AbstractDiagramData.MARK);
        addData(delaySpread, DataManager.LEVEL_ALL);
      }
      return delaySpread;
    }
    
    protected DiagramData getDelaySpreadAvg() {
      if (null == delaySpreadAvg) {
        delaySpreadAvg = new DiagramData(
            new AveragedTimeLine("txdiv delay spread (avg)", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category, "Radio", "txdiv delay spread (avg)"}, 
            "time", "s", "delay spread", "us");
        addData(delaySpreadAvg, DataManager.LEVEL_IMPORTANT);
      }
      return delaySpreadAvg;
    }

    protected DiagramData getPowerDelta() {
      if (null == powerDelta) {
        powerDelta = new DiagramData(new String[] {category, "Radio",
            "txdiv power delta"}, "time", "s", "rx power delta", "dBm",
            AbstractDiagramData.MARK);
        addData(powerDelta, DataManager.LEVEL_ALL);
      }
      return powerDelta;
    }

    protected DiagramData getPowerDeltaAvg() {
      if (null == powerDeltaAvg ) {
        powerDeltaAvg = new DiagramData(
            new AveragedTimeLine("txdiv power delta (avg)", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category, "Radio", "txdiv power delta (avg)"},
            "time", "s", "rx power delta", "dBm");
        addData(powerDeltaAvg, DataManager.LEVEL_IMPORTANT);
      }
      return powerDeltaAvg;
    }

    protected DiagramData getTxDivOccurance() {
      if (null == txDivOccurance  ) {
        txDivOccurance = new DiagramData(
            new AveragedTimeLine("txdiv occurance (avg)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category, "Radio", "txdiv occurance (avg)"},
            "time", "s", "occurances", "per sec");
        addData(txDivOccurance, DataManager.LEVEL_IMPORTANT);
      }
      return txDivOccurance;
    }

    public DiagramDataHist getTxDivRecvDrop() {
      if (null == txDivRecvDrop) {
        txDivRecvDrop = new DiagramDataHist(
            new String[] {category, "Radio", "txdiv caused recv and drop (hist)"},
            "node", "id", "recv (positive) and drop (negative)", "count",
            DiagramDataHist.MODE_CUML);
        addData(txDivRecvDrop, DataManager.LEVEL_IMPORTANT);
      }
      return txDivRecvDrop;
    }

    public DiagramData getTxDivDrop() {
      // Radio drop counter
      if (null == txDivDrop) {
        txDivDrop = new DiagramData(
            new String[] {category, "Radio", "txdiv drop vs time"}, "time", "s",
            "radio drop packets", "nodeId", AbstractDiagramData.MARK);
        addData(txDivDrop, DataManager.LEVEL_ALL);
      }
      return txDivDrop;
    }

    public DiagramDataHist getTxDivDropHist() {
      // Radio drop counter
      if (null == txDivDropHist) {
        txDivDropHist = new DiagramDataHist(
            new String[] {category, "Radio", "txdiv drop (hist)"},
            "node", "id", "occurence", "count");
        addData(txDivDropHist, DataManager.LEVEL_IMPORTANT);
      }
      return txDivDropHist;
    }

  }

  private static final String ID = "StatsRadioHandler";

  private NodeData[] nodeData;

  private GlobalData globalData;

  private double sampleLen;

  public StatsRadioDivHandler(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

    Event.addHandler(AbstractRadio.RadioRxDiversityEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRadio.RadioRxDiversityEvent ev = (AbstractRadio.RadioRxDiversityEvent) event;
        double powerDiff = Util.toDB(ev.powerCombined_mW) - Util.toDB(ev.powerOld_mW);
        double delaySpread_us = ev.delaySpread / (double)Constants.MICRO_SECOND;
        
        NodeData dataNode = getData(ev.nodeId);
        dataNode.getPowerDelta().addNextTimePoint(ev.time, powerDiff);
        dataNode.getPowerDeltaAvg().addNextTimePoint(ev.time, powerDiff);
        dataNode.getTxDivOccurance().addNextTimePoint(ev.time, 0);
        dataNode.getDelaySpread().addNextTimePoint(ev.time, delaySpread_us);
        dataNode.getDelaySpreadAvg().addNextTimePoint(ev.time, delaySpread_us);
        
        
        globalData.getDelaySpread().addNextTimePoint(ev.time, delaySpread_us);
        globalData.getDelaySpreadAvg().addNextTimePoint(ev.time, delaySpread_us);
        globalData.getPowerDelta().addNextTimePoint(ev.time, powerDiff);
        globalData.getPowerDeltaAvg().addNextTimePoint(ev.time, powerDiff);
        globalData.getTxDivOccurance().addNextTimePoint(ev.time, powerDiff);

        if (ev.causedRecv)
          globalData.getTxDivRecvDrop().addNextValue(ev.nodeId, 1.);
        if (ev.causedDrop)
          globalData.getTxDivRecvDrop().addNextValue(ev.nodeId + .01, -1.);
      }
    });

    // Global Handlers

    Event.addHandler(AbstractRadio.RadioDropEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRadio.RadioDropEvent ev = (AbstractRadio.RadioDropEvent) event;
        if (ev.reason.equals("multipath")) {
          globalData.getTxDivDrop().addNextTimePoint(ev.time, ev.radioInfo.getId());
          globalData.getTxDivDropHist().addNextValue(ev.radioInfo.getId());
        }
      }
    });
  }

}
