package brn.sim.handler;

import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;

import jist.swans.Constants;
import jist.swans.mac.AbstractMac;
import jist.swans.misc.Event;
import jist.swans.misc.MessageAnno;
import jist.swans.net.AbstractNet;
import jist.swans.net.NetAddress;
import jist.swans.net.NetMessage;
import jist.swans.net.AbstractNet.RecvFromTransEvent;
import jist.swans.net.AbstractNet.SendToTransEvent;
import jist.swans.route.AbstractRoute;
import jist.swans.trans.AbstractTrans;
import brn.sim.DataManager;
import brn.sim.DriverRemote;
import brn.sim.DataManager.DataContribution;
import brn.sim.DataManager.DataContributor;
import brn.sim.data.AbstractDiagramData;
import brn.sim.data.AveragedTimeLine;
import brn.sim.data.DiagramData;
import brn.sim.data.FlowStats;
import brn.sim.data.PropertiesData;
import brn.sim.data.XplotData;
import brn.sim.handler.TcpTrace.PLOTTER;
import brn.sim.handler.TcpTrace.TcpTraceEvents;
import jist.swans.app.AbstractApplication;

/**
 * Event processor containing collectors on a per node basis.
 *
 * @author kurth
 */
public class StatsFlowHandlers extends DataContributor {

  public class Data {
    protected DiagramData throughput = null;
    protected DiagramData packetArrival = null;
    protected DiagramData packetSent = null;
    protected DiagramData offeredThroughput = null;
    protected DiagramData delay = null;
    protected DiagramData forwardTime = null;
    protected DiagramData forward = null;
    protected DiagramData discard = null;
    protected DiagramData packetHopCount = null;
    protected DiagramData timeHopCount = null;
    protected FlowStats stats = null;
    private String category1;
    private String category2;
    protected TcpTrace tcpTraceSrc;
    protected TcpTrace tcpTraceDst;
    private int aNode = -1;
    private DiagramData  delayAvg;
    private DiagramData arrivalTimeNode;
    private DiagramData forwardTimeNode;
    private DiagramData forwardedTimeNode;

    public Data(String category1, String category2) {
      this.category1 = category1;
      this.category2 = category2;
    }

    public DiagramData getPacketArrival() {
      if (null == packetArrival) {
        // Packet arrival
        packetArrival = new DiagramData(
            new String[] {category1, category2, "packet arrival vs time"}, "time", "sec",
            "id", "n/a", AbstractDiagramData.MARK);
        addData(packetArrival, DataManager.LEVEL_ALL);

        // TODO replace both with AverageTimeline
        DiagramData packetArrivalRate = new DiagramData(
            new AveragedTimeLine("packet arrival vs time (avg'd)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category1, category2, "packet arrival vs time (avg'd)"}, "time", "sec",
            "arrival rate", "packets/sec");
        packetArrival.addChain(packetArrivalRate);
        addData(packetArrivalRate, DataManager.LEVEL_BASIC);

        DiagramData packetArrivalCuml = new DiagramData(
            new AveragedTimeLine("packet arrival vs time (cuml'd)", sampleLen, AveragedTimeLine.MODE_CN, 1.),
            new String[] {category1, category2, "packet arrival vs time (cuml'd)"},
            "time", "sec", "arrived packets", "no.");
        packetArrivalRate.addChain(packetArrivalCuml);
        addData(packetArrivalCuml, DataManager.LEVEL_BASIC);
      }

      return packetArrival;
    }

    public DiagramData getThroughput() {
      if (null == throughput) {
        // Throughput
        throughput = new DiagramData(
            new AveragedTimeLine("throughput (avg'd)", sampleLen, AveragedTimeLine.MODE_WA2, 1.),
            new String[] {category1, category2, "throughput (avg'd)"}, "time", "sec",
            "avg. throughput", "kbit/sec");
        addData(throughput, DataManager.LEVEL_IMPORTANT);
      }

      return throughput;
    }

    public DiagramData getPacketSent() {
      if (null == packetSent) {
        // Packet arrival
        packetSent = new DiagramData(
            new String[] {category1, category2, "packet transmission vs time"},
            "time", "sec", "id", "n/a", AbstractDiagramData.MARK);
        addData(packetSent, DataManager.LEVEL_ALL);

        DiagramData packetSentAvg = new DiagramData(
            new AveragedTimeLine("packet transmission vs time (avg'd)", sampleLen, AveragedTimeLine.MODE_R, 1.),
            new String[] {category1, category2, "packet transmission vs time (avg'd)"},
            "time", "sec", "tx rate", "packets/sec");
        packetSent.addChain(packetSentAvg);
        addData(packetSentAvg, DataManager.LEVEL_BASIC);

        DiagramData packetSentCuml = new DiagramData(
            new AveragedTimeLine("packet transmission vs time (cuml'd)", sampleLen, AveragedTimeLine.MODE_CN, 1.),
            new String[] {category1, category2, "packet transmission vs time (cuml'd)"},
            "time", "sec", "transmitted packets", "no.");
        packetSentAvg.addChain(packetSentCuml);
        addData(packetSentCuml, DataManager.LEVEL_BASIC);
      }

      return packetSent;
    }

    public DiagramData getOfferedThroughput() {
      if (null == offeredThroughput) {
        // Throughput
        offeredThroughput = new DiagramData(
            new AveragedTimeLine("offered throughput (avg'd)", sampleLen, AveragedTimeLine.MODE_WA2, 1.),
            new String[] {category1, category2, "offered throughput (avg'd)"}, "time", "sec",
            "avg. throughput", "kbit/sec");
        addData(offeredThroughput, DataManager.LEVEL_BASIC);
      }

      return offeredThroughput;
    }

    public DiagramData getDelay() {
      if (null == delay) {
        // Throughput
        delay = new DiagramData(
            new String[] {category1, category2, "packet delay"},
            "packet", "id", "delay", "msec", AbstractDiagramData.MARK);
        addData(delay, DataManager.LEVEL_ALL);
      }

      return delay;
    }

    public DiagramData getDelayAvg() {
      if (null == delayAvg) {
        delayAvg = new DiagramData(
            new AveragedTimeLine("packet delay (avg'd)", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category1, category2, "packet delay (avg'd)"},
            "time", "sec", "avg. delay", "msec");
        addData(delayAvg, DataManager.LEVEL_BASIC);
      }

      return delayAvg;
    }

    public DiagramData getForwardTime() {
      if (null == forwardTime) {
        forwardTime = new DiagramData(
            new String[] {category1, category2, "mac forward vs time"},
            "time", "s", "packet", "id", AbstractDiagramData.MARK);
        addData(forwardTime, DataManager.LEVEL_ALL);
      }
      return forwardTime;
    }

    public DiagramData getArrivalTimeNode() {
      if (null == arrivalTimeNode) {
        arrivalTimeNode = new DiagramData(
            new String[] {category1, category2, "net arrival time vs node"},
            "time", "s", "node", "id", AbstractDiagramData.MARK);
        addData(arrivalTimeNode, DataManager.LEVEL_ALL);
      }
      return arrivalTimeNode;
    }

    public DiagramData getForwardTimeNode() {
      if (null == forwardTimeNode) {
        forwardTimeNode = new DiagramData(
            new String[] {category1, category2, "net forward time vs node"},
            "time", "s", "node", "id", AbstractDiagramData.MARK);
        addData(forwardTimeNode, DataManager.LEVEL_ALL);
      }
      return forwardTimeNode;
    }

    public DiagramData getForwardedTimeNode() {
      if (null == forwardedTimeNode) {
        forwardedTimeNode = new DiagramData(
            new String[] {category1, category2, "net forwarded time vs node"},
            "time", "s", "node", "id", AbstractDiagramData.MARK);
        addData(forwardedTimeNode, DataManager.LEVEL_ALL);
      }
      return forwardedTimeNode;
    }

    public DiagramData getForward() {
      if (null == forward) {
        forward = new DiagramData(
            new String[] {category1, category2, "mac forward"},
            "packet", "id", "forwarder", "id", AbstractDiagramData.MARK);
        addData(forward, DataManager.LEVEL_ALL);
      }
      return forward;
    }

    public DiagramData getDiscard() {
      if (null == discard) {
        discard = new DiagramData(
            new String[] {category1, category2, "mac discard"},
            "packet", "id", "node", "id", AbstractDiagramData.MARK);
        addData(discard, DataManager.LEVEL_ADDITIONAL);
      }
      return discard;
    }

    // TODO
    public DiagramData getPacketHopCount() {
      if (null == packetHopCount) {
        packetHopCount = new DiagramData(new String[] {category1, category2, "hop count"},
            "packet", "id", "hop count", "hops", AbstractDiagramData.MARK);
        addData(packetHopCount, DataManager.LEVEL_ALL);
      }

      return packetHopCount;
    }

    // TODO
    public DiagramData getTimeHopCount() {
      if (null == timeHopCount) {
        timeHopCount = new DiagramData(
            new AveragedTimeLine("", sampleLen, AveragedTimeLine.MODE_A, 1.),
            new String[] {category1, category2, "hop count vs. time (avg'd)"},
            "time", "s", "avg. hop count", "hops");
        addData(timeHopCount, DataManager.LEVEL_BASIC);
      }

      return timeHopCount;
    }

    public FlowStats getStats(NetAddress src, int srcPort, NetAddress dst,
        int dstPort) {
      if (null == stats) {
        stats = new FlowStats(src, srcPort, dst, dstPort);

        String[] path = new String[] {category1, category2, "Flow stats" };
        DataContribution contrib = new PropertiesData(stats, path);
        addData(contrib, DataManager.LEVEL_IMPORTANT);
      }

      return stats;
    }

    public TcpTrace getTcpTrace(final int currNode) {
      boolean source = (this.aNode == currNode || this.aNode == -1);
      if (source) {
        if (null == tcpTraceSrc) {
          aNode = currNode;
          tcpTraceSrc = new TcpTrace();
          tcpTraceSrc.addEventListener(new TcpTraceEvents() {
            public void plotterCreated(PLOTTER plotter) {
              StringTokenizer tok = new StringTokenizer(plotter.title, "()");
              String[] path = new String[] { category1, "TcpTrace", "Node "+currNode,
                  tok.nextToken().trim(), tok.nextToken().trim() };
              int id = addData(path, DriverRemote.ITEM_TYPE_XPLOT, DataManager.LEVEL_ADDITIONAL);
              if (0 <= id)
                mapPlotters.put(new Integer(id), plotter);
            }
          });
        }
        return tcpTraceSrc;
      } else {
        if (null == tcpTraceDst) {
          tcpTraceDst = new TcpTrace();
          tcpTraceDst.addEventListener(new TcpTraceEvents() {
            public void plotterCreated(PLOTTER plotter) {
              StringTokenizer tok = new StringTokenizer(plotter.title, "()");
              String[] path = new String[] { category1, "TcpTrace", "Node "+currNode,
                  tok.nextToken().trim(), tok.nextToken().trim() };
              int id = addData(path, DriverRemote.ITEM_TYPE_XPLOT, DataManager.LEVEL_ADDITIONAL);
              if (0 <= id)
                mapPlotters.put(new Integer(id), plotter);
            }
          });
        }
        return tcpTraceDst;
      }
    }
  }

  private static final String ID = "FlowHandlers";

  protected double sampleLen;

  protected Data[] transData;
  protected Data[] appData;

  protected Data globalAppData;
  protected Data globalTransData;

  int flowOffset = 10000;

  protected Map mapPlotters;

  public StatsFlowHandlers(double sampleLen) {
    this.sampleLen = sampleLen;
    this.transData = new Data[20];
    this.appData = new Data[20];
    this.mapPlotters = new HashMap();
  }

  public void registerAppHandlers() {
    // Add app handlers

    Event.addHandler(AbstractApplication.PacketArrivedEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractApplication.PacketArrivedEvent ev = (AbstractApplication.PacketArrivedEvent) event;

        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == flowId)
          return;

        updateFlow(ev, getData(flowId, false));
        updateFlow(ev, getData(-1, false));

//        if (ev.data instanceof AbstractApplication.AnnotatedMessage) {
//          AbstractApplication.AnnotatedMessage msg = (AbstractApplication.AnnotatedMessage) ev.data;
//          updateFlow(ev, getData(msg.flowId, false));
//          updateFlow(ev, getData(-1, false));
//        }
      }

      private void updateFlow(AbstractApplication.PacketArrivedEvent ev, Data flowData) {
        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

//        AbstractApplication.AnnotatedMessage msg = (AbstractApplication.AnnotatedMessage) ev.data;
        double bandWidth = ev.data.getSize() * 8.0 / 1024.0;

        flowData.getThroughput().addNextTimePoint(ev.time, bandWidth);
        flowData.getPacketArrival().addNextTimePoint(ev.time, flowOffset * flowId + packetId);
        flowData.getDelay().addNextPoint(flowOffset * flowId + packetId,
            ev.delay/(double)Constants.MILLI_SECOND);
        flowData.getDelayAvg().addNextTimePoint(ev.time,
            ev.delay/(double)Constants.MILLI_SECOND);

        int hopCount = 0;
        if (null != ev.anno) {
            Integer i = ((Integer) ev.anno.get(MessageAnno.ANNO_RTG_HOPCOUNT));
          if (null != i) {
            hopCount = i.intValue();
            flowData.getPacketHopCount().addNextPoint(flowOffset * flowId + packetId, hopCount);
            flowData.getTimeHopCount().addNextTimePoint(ev.time, hopCount);
          }
        }

        FlowStats stats = flowData.getStats(ev.src, ev.srcPort, ev.dst, ev.dstPort);
        stats.packetRecv(ev.time, ev.data.getSize() * 8, ev.delay, hopCount);
      }
    });

    Event.addHandler(AbstractApplication.PacketSentEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractApplication.PacketSentEvent ev = (AbstractApplication.PacketSentEvent) event;

        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == flowId)
          return;

        updateFlow(ev, getData(flowId, false));
        updateFlow(ev, getData(-1, false));

//        AbstractApplication.AnnotatedMessage msg = (AbstractApplication.AnnotatedMessage) ev.data;
//        updateFlow(ev, getData(msg.flowId, false));
//        updateFlow(ev, getData(-1, false));
      }

      private void updateFlow(AbstractApplication.PacketSentEvent ev, Data flowData) {
        double bandWidth = ev.data.getSize() * 8.0 / 1024.0;
//        AbstractApplication.AnnotatedMessage msg = (AbstractApplication.AnnotatedMessage) ev.data;
        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

        flowData.getOfferedThroughput().addNextTimePoint(ev.time, bandWidth);
        flowData.getPacketSent().addNextTimePoint(ev.time, flowOffset * flowId + packetId);
        flowData.getArrivalTimeNode().addNextTimePoint(ev.time, ev.nodeId);

        FlowStats stats = flowData.getStats(ev.src, ev.srcPort, ev.dst, ev.dstPort);
        stats.packetSend(ev.time, ev.data.getSize() * 8);
      }
    });

    Event.addHandler(AbstractMac.SendEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.SendEvent ev = (AbstractMac.SendEvent) event;
        if (null == ev.anno)
          return;

        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

        Data data = getData(flowId.intValue(), false);
        data.getForward().addNextPoint(packetId.intValue(), ev.nodeId);
        data.getForwardTime().addNextTimePoint(ev.time, packetId.intValue());
        getData(-1, false).getForwardTime().addNextTimePoint(ev.time, flowOffset * flowId.intValue() + packetId.intValue());
      }
    });

    Event.addHandler(AbstractNet.SendToMacEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractNet.SendToMacEvent ev = (AbstractNet.SendToMacEvent) event;
        if (null == ev.anno)
          return;

        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

        Data data = getData(flowId.intValue(), false);
        data.getForwardTimeNode().addNextTimePoint(ev.time, ev.nodeId);
      }
    });

    Event.addHandler(AbstractRoute.PacketForwardedEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractRoute.PacketForwardedEvent ev = (AbstractRoute.PacketForwardedEvent) event;
        if (null == ev.anno)
          return;

        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

        Data data = getData(flowId.intValue(), false);
        data.getForwardedTimeNode().addNextTimePoint(ev.time, ev.lastIp.getId());
        data.getArrivalTimeNode().addNextTimePoint(ev.time, ev.nodeId);
      }
    });

    // discarded packets on mac layer
    Event.addHandler(AbstractMac.DiscardEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractMac.DiscardEvent ev = (AbstractMac.DiscardEvent) event;
        if (null == ev.anno)
          return;

        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        if (null == packetId || null == flowId)
          return;

        getData(flowId.intValue(), false).getDiscard().addNextPoint(packetId.intValue(), ev.nodeId);
      }
    });

    Event.addHandler(AbstractTrans.SegmentArrivedEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractTrans.SegmentArrivedEvent ev = (AbstractTrans.SegmentArrivedEvent) event;

        int flow = -1;
        int packet = -1;
        if (null != ev.anno) {
          Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
          Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
          if (null != flowId && null != packetId) {
            flow = flowId.intValue();
            packet = packetId.intValue();
          }
        }
//        if (-1 == flow || -1 == packet) {
//          if (ev.data instanceof AnnotatedMessage) {
//            AnnotatedMessage msg = (AnnotatedMessage) ev.data;
//            flow = msg.flowId;
//            packet = msg.packetId;
//          }
//        }
        if (-1 == flow || -1 == packet)
          return;

        updateFlow(ev, getData(flow, true), flow, packet);
        updateFlow(ev, getData(-1, true), flow, packet);
      }

      private void updateFlow(AbstractTrans.SegmentArrivedEvent ev, Data flowData,
          int flowId, int packetId) {
        double bandWidth = ev.data.getSize() * 8.0 / 1024.0;
        long delay = 0;

        if (null != ev.anno) {
          Long txTime = (Long) ev.anno.get(MessageAnno.ANNO_TRANS_TXTIME);
          if (null != txTime)
        	  delay = ev.time - txTime;
        }

        flowData.getThroughput().addNextTimePoint(ev.time, bandWidth);
        flowData.getPacketArrival().addNextTimePoint(ev.time, flowOffset * flowId + packetId);
        flowData.getDelay().addNextPoint(flowOffset * flowId + packetId,
            delay/(double)Constants.MILLI_SECOND);
        flowData.getDelayAvg().addNextTimePoint(ev.time,
            delay/(double)Constants.MILLI_SECOND);

        int hopCount = 0;
        if (null != ev.anno) {
            Integer i = ((Integer) ev.anno.get(MessageAnno.ANNO_RTG_HOPCOUNT));
          if (null != i) {
            hopCount = i.intValue();
            flowData.getPacketHopCount().addNextPoint(flowOffset * flowId + packetId, hopCount);
            flowData.getTimeHopCount().addNextTimePoint(ev.time, hopCount);
          }
        }

        FlowStats stats = flowData.getStats(ev.src, ev.srcPort, ev.dst, ev.dstPort);
        stats.packetRecv(ev.time, ev.data.getSize() * 8, delay, hopCount);
      }
    });

    Event.addHandler(AbstractTrans.SegmentSentEvent.class, new Event.Handler() {
      public void handle(Event event) {
        AbstractTrans.SegmentSentEvent ev = (AbstractTrans.SegmentSentEvent) event;

        if (null == ev.anno)
          return;

        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        Integer packetId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_PACKETID);
        if (null == flowId || null == packetId)
          return;

        updateFlow(ev, getData(flowId.intValue(), true), flowId.intValue(), packetId.intValue());
        updateFlow(ev, getData(-1, true), flowId.intValue(), packetId.intValue());
      }

      private void updateFlow(AbstractTrans.SegmentSentEvent ev, Data flowData,
          int flowId, int packetId) {
        double bandWidth = ev.data.getSize() * 8.0 / 1024.0;
        flowData.getOfferedThroughput().addNextTimePoint(ev.time, bandWidth);
        flowData.getPacketSent().addNextTimePoint(ev.time, flowOffset * flowId + packetId);
        flowData.getArrivalTimeNode().addNextTimePoint(ev.time, ev.nodeId);

        FlowStats stats = flowData.getStats(ev.src, ev.srcPort, ev.dst, ev.dstPort);
        stats.packetSend(ev.time, ev.data.getSize() * 8);
      }
    });

    Event.addHandler(AbstractNet.SendToTransEvent.class, new Event.Handler() {
      public void handle(Event event) {
        SendToTransEvent ev = (SendToTransEvent) event;
        NetMessage.Ip data = (NetMessage.Ip) ev.data;

        if (data.getProtocol() != Constants.NET_PROTOCOL_TCP
            || null == ev.anno)
          return;

        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        getData(flowId.intValue(), false).getTcpTrace(ev.nodeId).dotrace(data, ev.time);
      }
    });
    Event.addHandler(AbstractNet.RecvFromTransEvent.class, new Event.Handler() {
      public void handle(Event event) {
        RecvFromTransEvent ev = (RecvFromTransEvent) event;
        NetMessage.Ip data = (NetMessage.Ip) ev.data;

        if (data.getProtocol() != Constants.NET_PROTOCOL_TCP
            || null == ev.anno)
          return;

        Integer flowId = (Integer) ev.anno.get(MessageAnno.ANNO_RTG_FLOWID);
        getData(flowId.intValue(), false).getTcpTrace(ev.nodeId).dotrace(data, ev.time);
      }
    });
  }

  protected Data getData(int id, boolean trans) {
    if (0 > id) {
      if (trans && globalTransData == null)
        globalTransData = new Data("All flows", "Transport");
      if (!trans && globalAppData == null)
        globalAppData = new Data("All flows", "Application");

      return (trans ? globalTransData : globalAppData);
    }

    try {
      if (trans && null == transData[id]) {
        String category1 = StatsFlowHandlers.getCategory(String.valueOf(id));
        String category2 = (trans ? "Transport" : "Application");
        transData[id] = new Data(category1, category2);
      }
      else if (!trans && null == appData[id]) {
        String category1 = StatsFlowHandlers.getCategory(String.valueOf(id));
        String category2 = (trans ? "Transport" : "Application");
        appData[id] = new Data(category1, category2);
      }
    }
    catch (ArrayIndexOutOfBoundsException e) {
      if (id > 50000)
          throw new RuntimeException("id to big");
        Data[] tmp = new Data[id + 100];
        if (trans) {
          System.arraycopy(transData, 0, tmp, 0, transData.length);
          transData = tmp;
        } else {
          System.arraycopy(appData, 0, tmp, 0, appData.length);
          appData = tmp;
        }
        return getData(id, trans);
      }
      return (trans ? transData[id] : appData[id]);
  }

  public static String getCategory(String id) {
    return "Flow " + id;
  }

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

  /*
   * (non-Javadoc)
   * @see brn.sim.DataManager.DataContributor#registerHandlers()
   */
  public void registerHandlers() {
    super.registerHandlers();

    registerAppHandlers();
  }

  /*
   * (non-Javadoc)
   * @see brn.sim.DataManager.DataContributor#getContribution(int)
   */
  public DataContribution getContribution(int id, String[] path) {
    PLOTTER plotter = (PLOTTER) mapPlotters.get(new Integer(id));
    if (null == plotter)
      return super.getContribution(id, path);

    return new XplotData(path, plotter.getContent());
  }

//  /*
//   * (non-Javadoc)
//   * @see brn.sim.DataManager.DataContributor#getFileName(int, brn.sim.DataManager.DataContribution)
//   */
//  public String getFileName(int id, String[] path, DataContribution contribution) {
//    return DataManager.getPath(path) + ".dot";
//  }

}
