package jist.swans.rate;

import java.util.Arrays;

import org.apache.log4j.Logger;

import jist.swans.Constants;
import jist.swans.mac.MacInfo;
import jist.swans.mac.MacTimes;

public class SampleRateState {

	/** SampleRate logger. */
	public static final Logger log = Logger.getLogger(SampleRateState.class);

	/** List of bit-rate statistics: for each size bin and each bit-rate */
	public RateStats stats[][];

	public SampleRateState(MacInfo macInfo, int[] packetSizes, int[] bitrates) {
		int count = packetSizes.length;
		this.stats = new RateStats[count][bitrates.length];
		for (int i = 0; i < count; i++)
			for (int j = 0; j < bitrates.length; j++)
				stats[i][j] = new RateStats(macInfo, packetSizes[i],
						bitrates[j]);

		lastSampleRateIdx = new int[count];
		Arrays.fill(lastSampleRateIdx, -1);
		currentSampleIdx = new int[count];
		Arrays.fill(currentSampleIdx, -1);
		currentRateIdx = new int[count];
		Arrays.fill(currentRateIdx, -1);
		packetsSent = new int[count];
		Arrays.fill(packetsSent, 0);
		packetsSinceSwitch = new int[count];
		Arrays.fill(packetsSinceSwitch, 0);
		timeSinceSwitch = new long[count];
		Arrays.fill(timeSinceSwitch, 0);
		packetsSinceSample = new int[count];
		Arrays.fill(packetsSinceSample, 0);
		sampleTxTime = new long[count];
		Arrays.fill(sampleTxTime, 0);
	}

	/** Last bit-rate (idx) that was sampled; one for each size bin. */
	int lastSampleRateIdx[];

	/** Bit-rate (idx) that is currently sampled; one for each size bin. */
	int currentSampleIdx[];

	/** Currently used bit-rate (idx); one per size bin. */
	int currentRateIdx[];

	/** Number of packets sent; count per size bin. */
	int packetsSent[];

	/** Number of packets sent since rate switch; per size bin. */
	int packetsSinceSwitch[];

	/** Time passed since rate switch; per size bin. */
	long timeSinceSwitch[];

	/** Number of packets sent since last sampling; per size bin. */
	int packetsSinceSample[];

	/** Last sample tx time; per size bin. */
	long sampleTxTime[];

	/** Bit-rate statistics class */
	public class RateStats {
		int successiveFailures = 0;

		int tries = 0;

		int totalPackets = 0;

		int packetsAcked = 0;

		long perfectTxTime; /* tx time for 0 retries */

		long avgTxTime;

		long lastTx = 0;

		// XXX: controlRate in Atheros chipsets is always the lowest!
		// see madwifi code/Bicket's paper
		public RateStats(MacInfo macInfo, int length, int dataRate) {
			boolean useRtsCts = length > macInfo.getThresholdRts();
			perfectTxTime = MacTimes.avgTransmitTimeUnicastPacket(macInfo,
					length, dataRate, macInfo.getHighestBasicRate(dataRate)
					/*getBasicRateSet()[0]*/
					, useRtsCts, false, useRtsCts ? 1 : 0, 1, true);
			avgTxTime = perfectTxTime;
		}

		public String toString() {
			StringBuffer sb = new StringBuffer();
			sb.append('|').append(successiveFailures).append('|').append(tries)
					.append('|').append(totalPackets).append('|').append(
							packetsAcked).append('|').append(
							perfectTxTime / Constants.MICRO_SECOND).append('|')
					.append(avgTxTime / Constants.MICRO_SECOND).append('|')
					.append(lastTx).append('|');
			formatSb(sb, "|");
			return sb.toString();
		}
		
		private void formatSb(StringBuffer sb, String sep) {
			String s = "            "; // 12 spaces
			int i1 = sb.indexOf(sep, 1);
			int i2 = sb.indexOf(sep, i1 + 1);
			int i = 0;
			while (i1 != -1 && i2 != -1 && i < 10) {
				sb.insert(i1 + 1, s.substring(0, (i2 - i1 > 6 ? 13 : 6) - (i2 - i1)));
				i1 = sb.indexOf(sep, i2);
				i2 = sb.indexOf(sep, i1 + 1);
				i++;
			}
		}
	}

	public String toString() {
		StringBuffer sb = new StringBuffer();
		for (int i = 0; i < stats.length; i++) {
			sb.append(" [").append(i).append(':').append(currentRateIdx[i])
					.append('|').append(currentSampleIdx[i]).append('|')
					.append(lastSampleRateIdx[i]).append('|').append(
							packetsSent[i]).append('|').append(
							packetsSinceSample[i]).append('|').append(
							packetsSinceSwitch[i]).append('|').append(
							sampleTxTime[i]).append('|').append(
							timeSinceSwitch[i]).append(']');
		}
		sb.append('\n');
		for (int j = 0; j < stats[0].length; j++) {
			sb.append(j).append(':');
//			for (int i = 0; i < stats.length; i++) {
			for (int i = 1; i < stats.length-1; i++) {
				sb.append('\t').append(stats[i][j].toString()).append('\t');
			}
			sb.setCharAt(sb.length() - 1, '\n');
		}
		sb.deleteCharAt(sb.length() - 1); // delete last line break
		return sb.toString();
	}

	public static String logFormat() {
		StringBuffer sb = new StringBuffer();
		sb.append("[currentRateIdx|").append("currentSampleIdx|").append(
				"lastSampleRateIdx|").append("packetsSent|").append(
				"packetsSinceSample|").append("packetsSinceSwitch|").append(
				"sampleTxTime|").append("timeSinceSwitch]\n");
		sb.append("|successiveFailures|").append("tries|").append("totalPackets|")
				.append("packetsAcked|").append(
						"perfectTxTime|").append(
						"avgTxTime|").append("lastTx|");
		return sb.toString();
	}
}
