/**
 * 
 */
package brn.sim.handler;

import jist.swans.Constants;
import jist.swans.mac.AbstractMac;
import jist.swans.misc.Event;
import jist.swans.misc.Util;
import brn.sim.DataManager;
import brn.sim.DataManager.DataContributor;
import brn.sim.data.AbstractDiagramData;
import brn.sim.data.DiagramData;

/**
 * @author ofriedri
 */
public class StatsRateHandler extends DataContributor {

	private static final String ID = "StatsRateHandler";

	/** Time interval over which to calculate averages */
//	private double sampleLen;

	/** Data captured per node */
	private NodeData[] nodeData;

	public StatsRateHandler(double sampleLen) {
//		this.sampleLen = sampleLen;
		this.nodeData = new NodeData[20];
	}

	/*
	 * (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();

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

				getNodeData(ev.nodeId).getSendBitrate().addNextTimePoint(ev.time,
						((double)ev.phyBitRate) / Constants.BANDWIDTH_1Mbps);

				getNodeData(ev.nodeId).getPacketsPerBitrate().addNextPoint(
						((double)ev.phyBitRate) / Constants.BANDWIDTH_1Mbps,
						getNodeData(ev.nodeId).incBitrateSentCount(
								((double)ev.phyBitRate) / Constants.BANDWIDTH_1Mbps));
			}
		});
	}

	protected NodeData getNodeData(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 getNodeData(id);
		}
		return nodeData[id];
	}

	/**
	 * Class encapsulating simulation data on a per node basis
	 */
	public class NodeData {
		protected DiagramData packetsPerBitrate = null;

		protected DiagramData sendBitrate = null;

		protected DiagramData recvBitrate = null;

		protected int id;

		private String category;

		/** Counts the packets sent at a certain bitrate. */
		private double[][] /* bitrate, count */bitrateSentCount;

		public NodeData(int id) {
			this.id = id;
			this.category = "Node " + id;
			this.bitrateSentCount = new double[2][Constants.BITRATES_ALL.length];
			for (int i = 0; i < Constants.BITRATES_ALL.length; i++) {
				bitrateSentCount[0][i] = ((double) Constants.BITRATES_ALL[i])
						/ Constants.BANDWIDTH_1Mbps;
				bitrateSentCount[1][i] = 0; /* initial count = 0 */
			}
		}

		public DiagramData getSendBitrate() {
			if (sendBitrate == null) {
				sendBitrate = new DiagramData(new String[] { category, "Rate",
						"bit-rate vs. time (at sender)" }, "time", "ms", "bitrate", "Mbit/s",
						AbstractDiagramData.MARK);
				addData(sendBitrate, DataManager.LEVEL_ALL);
			}
			return sendBitrate;
		}

		public DiagramData getPacketsPerBitrate() {
			if (packetsPerBitrate == null) {
				packetsPerBitrate = new DiagramData(
				    new String[] { category,
								"Rate", "packets per bit-rate" }, "bitrate", "Mbit/s",
						"packets", "#", AbstractDiagramData.MARK);
				addData(packetsPerBitrate, DataManager.LEVEL_ALL);
			}
			return packetsPerBitrate;
		}

		/**
		 * @param bitrate bit-rate at which a packet was sent (in Mbit/s)
		 * @return The total count of packets sent at that bit-rate.
		 */
		public double incBitrateSentCount(double bitrate) {
			int idx = Util.isInArray(bitrateSentCount[0], bitrate);
			// TODO what about -1???
			return ++bitrateSentCount[1][idx];
		}
	}
}
