package brn.sim.scenario.rca;

import java.util.LinkedList;
import java.util.List;

import org.apache.commons.math.stat.descriptive.DescriptiveStatistics;


import brn.distsim.ormapper.util.DBSaver;
import brn.distsim.ormapper.util.DbBinaryLoader;
import brn.sim.builder.MacBuilder;
import brn.sim.builder.RateBuilder;
import brn.sim.builder.FieldBuilder.FieldParams;
import brn.sim.builder.PathLossBuilder.DistShadowingParams;
import brn.sim.data.DiagramData;
import brn.sim.data.DiagramDataHist;
import brn.sim.data.FlowStats;
import brn.sim.data.MacStats;
import brn.sim.data.PropertiesData;

import test.sim.scenario.mac.MacTestParams;

public class RcaSimEval {
	
	private RcaSim sim;
	
	public RcaSimEval(RcaSim sim) {
		this.sim = sim;
	}

	public void evalSimulationResults(String dbUrl, String dbUser,
			String dbPasswd, String driver, String version, String dbResUrl,
			String dbResUser, String dbResPasswd) throws Throwable {
		DbBinaryLoader loader = new DbBinaryLoader(dbUrl, dbUser, dbPasswd);
		List lstIds = loader.loadSimulationIds(driver, version);
		DBSaver saver = new DBSaver(dbResUrl, dbResUser, dbResPasswd, dbResUrl,
				dbResUser, dbResPasswd);

		List results = new LinkedList();
		if (null == lstIds || lstIds.size() <= 0)
			throw new Error("no simulations found for driver " + driver
					+ " and version " + version);

		for (int i = 0; i < lstIds.size(); i++) {
			SimulationResult result = new SimulationResult();
			result.simulationId = ((Integer) lstIds.get(i)).intValue();
			result.driver = driver;
			result.version = version;

			try {
				{ // read configuration
					MacTestParams params = (MacTestParams) sim.load(loader,
							result.simulationId, "Global, Config");
					result.seed = params.seed;
					result.nodes = params.nodes;
					result.shadowStdDev = ((DistShadowingParams) ((FieldParams) params.field).pathloss).stdDeviation;
					result.csSize = 1;
					// result.globFwdTable = 0;
					// result.metricDelta = 0;
					MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) params.node.mac;
					result.useRts = mac.thresholdRts < 1000 ? 1 : 0;
					result.dataRate = ((RateBuilder.ConstantParams) mac.rateSelection).dataBitrate;
					result.controlRate = ((RateBuilder.ConstantParams) mac.rateSelection).controlBitrate;
					// result.dataTxDiv = false ? 1 : 0;
					// result.ignoreRtsCtsNav = false ? 1 : 0;
					// result.useAckCancelation = false ? 1 : 0;
					// result.promiscStorePackets = false ? 1 : 0;
				}

				{ // flow parameters
					PropertiesData data = (PropertiesData) sim.load(loader,
							result.simulationId, "All flows, Flow stats");
					FlowStats stats = (FlowStats) data.getProperties();
					result.avgThroughput = stats.getAvgThroughput();
					result.avgDelay = stats.getAvgDelay();
					result.sendPackets = stats.getSendPackets();
					result.recvPackets = stats.getRecvPackets();
					result.flowEnd = stats.getEnd();
					result.avgHopCount = stats.getAvgHopCount();
				}

				{ // mac stats
					PropertiesData data = (PropertiesData) sim.load(loader,
							result.simulationId, "Global, Mac, Stats", true);
					result.macStats = (MacStats) data.getProperties();
				}

				{ // mac drops
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Mac, discard vs time", false);
					result.macDrops = null == data ? 0 : data.getY().length;
				}

				{ // mac dups
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Mac, duplicate vs time", false);
					result.macDuplicates = null == data ? 0
							: data.getY().length;
				}

				{ // mac backoff
					DiagramDataHist data = (DiagramDataHist) sim.load(loader,
							result.simulationId, "Global, Mac, backoff (cuml)");
					double[] bos = data.getY();
					double bo = .0;
					for (int j = 0; j < bos.length; j++)
						bo += bos[j];
					result.macBackoffCuml = bo;
				}

				{ // mac short retries
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Mac, short retries vs time", false);
					result.macShortRetries = data == null ? 0
							: data.getY().length;
				}

				{ // mac long retries
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Mac, long retries vs time", false);
					result.macLongRetries = data == null ? 0
							: data.getY().length;
				}

				{ // mac ack cancelation
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Mac, ack cancelation", false);
					result.macCanceledAcks = data == null ? 0
							: data.getY().length;
				}

				{ // mac send immediate
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId, "Global, Mac, send immediate",
							false);
					result.macSendImmediate = data == null ? 0
							: data.getY().length;
				}

				{ //
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Routing, duplicate vs time", false);
					result.rtgDuplicate = data == null ? 0 : data.getY().length;
				}

				{ //
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId, "Global, Routing, ", false);
					result.rtgDrops = data == null ? 0 : data.getY().length;
				}

				{ // radio txdiv delay spread
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Radio, txdiv delay spread", false);
					double[] spreads = (data == null ? null : data.getY());
					result.radAvgDelaySpread = 0;
					if (null != spreads && spreads.length > 0) {
						double spread = .0;
						for (int j = 0; j < spreads.length; j++)
							spread += spreads[j];
						result.radAvgDelaySpread = spread / spreads.length;
					}
				}

				{ // radio txdiv delay spread
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Global, Radio, txdiv power delta", false);
					double[] deltas = (data == null ? null : data.getY());
					result.radAvgPowerDelta = 0;
					result.radTxDivCount = 0;
					if (null != deltas && deltas.length > 0) {
						double delta = .0;
						for (int j = 0; j < deltas.length; j++)
							delta += deltas[j];
						result.radAvgPowerDelta = delta / deltas.length;
						result.radTxDivCount = deltas.length;
					}
				}

				// {
				// // TODO hugo
				// DescriptiveStatistics stats =
				// DescriptiveStatistics.newInstance();
				// DiagramData data = (DiagramData)
				// sim.load(loader, result.simulationId, "Flow 0, packet delay",
				// true);
				// double[] y = data.getY();
				// for (int j = 1; j < y.length; j++)
				// stats.addValue(y[j] - y[j-1]);
				//
				// DelayStats delayStats = new DelayStats(result.simulationId,
				// stats.getMin(),
				// stats.getPercentile(5.),
				// stats.getMean(),
				// stats.getPercentile(95.),
				// stats.getMax(),
				// stats.getStandardDeviation());
				// saver.save(delayStats, driver + "-" + version, false);
				// }

				{
					// TODO hugo
					DescriptiveStatistics stats = DescriptiveStatistics
							.newInstance();
					DiagramData data = (DiagramData) sim.load(loader,
							result.simulationId,
							"Flow 0, packet arrival vs time", true);
					double[] y = data.getY();
					for (int j = 0; j < y.length; j++)
						stats.addValue(y[j]);
					double[] sortedY = stats.getSortedValues();
					stats.clear();
					for (int j = 0; j < y.length; j++)
						stats.addValue(y[j] - sortedY[j]);

				}

				/*
				 * TODO radio utilization routing number of hops, routing drops
				 */

			} catch (Error e) {
				System.err.println(e.getMessage());
				continue;
			} catch (Throwable e) {
				e.printStackTrace();
				continue;
			}

			results.add(result);
		}

		for (int i = 0; i < results.size(); i++) {
			SimulationResult res = (SimulationResult) results.get(i);
			if (res.csSize != 1) {
				res.avgThroughputRatio = -1.;
				res.avgNonRtsThroughputRatio = -1.;
				res.avgDelayRatio = -1.;
				res.avgNonRtsDelayRatio = -1.;

				for (int j = 0; j < results.size(); j++) {
					SimulationResult other = (SimulationResult) results.get(j);
					if (other.seed == res.seed && other.nodes == res.nodes
							&& other.shadowStdDev == res.shadowStdDev
							&& other.useRts == res.useRts && other.csSize == 1
							&& other.dataRate == res.dataRate
							&& other.controlRate == res.controlRate) {
						res.avgThroughputRatio = res.avgThroughput
								/ other.avgThroughput;
						if (Double.isNaN(res.avgThroughputRatio))
							res.avgThroughputRatio = .0;
						other.avgThroughputRatio = 1.;
						res.avgDelayRatio = res.avgDelay / other.avgDelay;
						if (Double.isNaN(res.avgDelayRatio))
							res.avgDelayRatio = .0;
						other.avgDelayRatio = 1.;
						break;
					}
				}

				for (int j = 0; j < results.size(); j++) {
					SimulationResult other = (SimulationResult) results.get(j);
					if (other.seed == res.seed && other.nodes == res.nodes
							&& other.shadowStdDev == res.shadowStdDev
							&& other.useRts == 0 && other.csSize == 1
							&& other.dataRate == res.dataRate
							&& other.controlRate == res.controlRate) {
						res.avgNonRtsThroughputRatio = res.avgThroughput
								/ other.avgThroughput;
						if (Double.isNaN(res.avgNonRtsThroughputRatio))
							res.avgNonRtsThroughputRatio = .0;
						other.avgNonRtsThroughputRatio = 1.;
						res.avgNonRtsDelayRatio = res.avgDelay / other.avgDelay;
						if (Double.isNaN(res.avgNonRtsDelayRatio))
							res.avgNonRtsDelayRatio = .0;
						other.avgNonRtsDelayRatio = 1.;
						break;
					}
				}
			}
		}

		// System.out.println(SimulationResult.getHeader());
		// for (int i = 0; i < results.size(); i++) {
		// System.out.println(((SimulationResult)results.get(i)).toString());
		// }

		for (int i = 0; i < results.size(); i++)
			saver.save(results.get(i), driver + "-" + version, false);
		saver.finalize();
	}

	public static class SimulationResult {
		public String driver;

		public String version;

		public int simulationId;

		public int seed;

		public int nodes;

		public double shadowStdDev;

		public int csSize;

		// public int globFwdTable;
		public int useRts;

		// public int dataTxDiv;
		// public int ignoreRtsCtsNav;
		// public int useAckCancelation;
		// public int metricDelta;
		public int dataRate;

		public int controlRate;

		public String metric;

		// public int promiscStorePackets;
		// public int noCandSets;
		// public int scaleRemainingHops;

		public double avgThroughput;

		/** ratio relative to the corresponding traditional transmission run */
		public double avgThroughputRatio;

		/**
		 * ratio relative to the corresponding traditional transmission run w/o
		 * rts
		 */
		public double avgNonRtsThroughputRatio;

		public double avgDelay;

		public double avgDelayRatio;

		public double avgNonRtsDelayRatio;

		public int sendPackets;

		public int recvPackets;

		public double flowEnd;

		public double avgHopCount;

		public double radAvgDelaySpread;

		public double radAvgPowerDelta;

		public int radTxDivCount;

		public int macDrops;

		public int macDuplicates;

		public double macBackoffCuml;

		public int macShortRetries;

		public int macLongRetries;

		public int macCanceledAcks;

		public int macSendImmediate;

		public MacStats macStats;

		public int rtgDuplicate;

		public int rtgDrops;
	}

}