package test.click;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

import jist.swans.Constants;

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

import brn.distsim.ormapper.util.DBSaver;
import brn.distsim.ormapper.util.DbBinaryLoader;
import brn.sim.AbstractParams;
import brn.sim.BasicDriver;
import brn.sim.DataManager;
import brn.sim.builder.FieldBuilder;
import brn.sim.builder.MacBuilder;
import brn.sim.builder.NodeBuilder;
import brn.sim.builder.RadioBuilder;
import brn.sim.builder.RateBuilder;
import brn.sim.builder.TrafficBuilder;
import brn.sim.builder.TransBuilder;
import brn.sim.builder.FadingBuilder.PunnooseRicianParams;
import brn.sim.builder.FadingBuilder.RayleighParams;
import brn.sim.builder.FadingBuilder.RicianParams;
import brn.sim.builder.FlowBuilder.CbrUdpParams;
import brn.sim.builder.NodeBuilder.TrafficParams;
import brn.sim.builder.PathLossBuilder.LogDistanceParams;
import brn.sim.builder.RadioBuilder.NoiseAdditiveBerParams;
import brn.sim.builder.TrafficBuilder.HorizontalParams;
import brn.sim.builder.TransBuilder.UdpParams;
import brn.sim.data.DiagramData;
import brn.sim.data.DiagramDataHist;
import brn.sim.data.FlowStats;
import brn.sim.data.ForwardGraphData;
import brn.sim.data.MacStats;
import brn.sim.data.PropertiesData;
import brn.sim.data.ForwardGraphData.FwdLink;
import brn.sim.data.ForwardGraphData.Node;
import brn.sim.handler.StatsClickHandler;
import click.sim.builder.ClickBuilder;
import click.sim.builder.ClickBuilder.NetParams;

/**
 * Scenario with a few netcoding nodes, one traffic source and one sink, all
 * randomly placed
 * 
 * @author hermann
 * 
 */
public class NetcodingTest extends BasicDriver {

	public static class FlowSorter implements Comparator {

		List fwdLinks = new LinkedList();

		int source;

		int dest;

		public FlowSorter(int source, int dest) {
			this.source = source;
			this.dest = dest;
		}

		public void addLink(FwdLink link) {
			fwdLinks.add(link);
		}

		private boolean searchForward(Node n1, Node n2) {
			Iterator i = fwdLinks.iterator();
			while (i.hasNext()) {
				FwdLink link = (FwdLink) i.next();
				if (link.from == n1 && link.to == n2)
					return true;
			}
			i = fwdLinks.iterator();
			while (i.hasNext()) {
				FwdLink link = (FwdLink) i.next();
				if (link.from == n1 && searchForward(link.to, n2))
					return true;
			}
			return false;
		}

		private boolean searchBackward(Node n1, Node n2) {
			Iterator i = fwdLinks.iterator();
			while (i.hasNext()) {
				FwdLink link = (FwdLink) i.next();
				if (link.from == n1 && link.to == n2)
					return true;
			}
			i = fwdLinks.iterator();
			while (i.hasNext()) {
				FwdLink link = (FwdLink) i.next();
				if (link.to == n2 && searchBackward(n1, link.from))
					return true;
			}
			return false;
		}

		private int search(Node n1, Node n2) {

			if (searchForward(n1, n2)) {
				return -1;
			} else if (searchBackward(n1, n2)) {
				return 1;
			} else if (searchForward(n2, n1)) {
				return 1;
			} else if (searchBackward(n2, n1)) {
				return -1;
			} else {
				System.err.println("links not properly ordered");
				return 0;
			}
		}

		public int compare(Object o1, Object o2) {
			Node n1 = (Node) o1;
			Node n2 = (Node) o2;
			if (n1 == n2)
				return 0;
			if (n1.nodeId == source || n2.nodeId == dest)
				return -1;
			else if (n1.nodeId == dest || n2.nodeId == source)
				return 1;
			else {
				return search(n1, n2);
			}
		}

	}

	public static class ExtraFlowStats {
		private static final long serialVersionUID = 118417276468557685L;

		Node[] route;

		int flowId;

		int numHops;

		int receivedFragments;

		int receivedPackets;

		int duplicateFragments;

		int duplicatePackets;

		int opportunisticFragments;

		int opportunisticPackets;
	}

	public static class SimulationResult {
		public String driver;

		public String version;

		public int simulationId;

		public int fragmentsInPacket;

		public int fragmentsOutPacket;

		public int fragmentsInBatch;

		public boolean opportunistic;

		public String protocol;

		public String fadingModel;

		public double fadingK;

		public double fadingVelocity;

		public String bitErrorModel;

		public boolean recvCorruptPackets;

		public int seed;

		public int numNodes;

		public Node[] nodes;

		public FwdLink[] links;

		public int dataRate;

		public int controlRate;

		public int flows;

		public double avgThroughput;

		public double avgDelay;

		public int sendPackets;

		public int recvPackets;

		public List /* FlowResult */flowStats;

		public List extraFlowStats;

		public int macDrops;

		public int macDuplicates;

		public double macBackoffCuml;

		public int macShortRetries;

		public MacStats macStats;

		public int rtgDuplicate;
	}

	protected Object load(DbBinaryLoader loader, int simulationId,
			String configPath, boolean error) throws Exception {
		Object ret = loader.load(simulationId, configPath);
		if (null == ret && error) {
			throw new Error("Object with path " + configPath
					+ " not found for id " + simulationId);
		}
		return ret;
	}

	protected Object load(DbBinaryLoader loader, int simulationId,
			String configPath) throws Exception {
		Object ret = loader.load(simulationId, configPath);
		if (null == ret) {
			throw new Error("Object with path " + configPath
					+ " not found for id " + simulationId);
		}
		return ret;
	}

	/**
	 * this is an ugly hack. Don't use it
	 * 
	 * @param simulationId
	 * @return
	 */
	private String getStudyName(int simulationId, String dbUrl, String dbUser,
			String dbPasswd) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection connection = DriverManager.getConnection(dbUrl, dbUser,
					dbPasswd);
			ResultSet res = connection
					.createStatement()
					.executeQuery(
							"select studies.name from studies join groups on studies.id = groups.studyId join simulations on groups.id = simulations.groupId where simulations.id ="
									+ simulationId);
			res.next();
			String ret = res.getString(1);
			res.close();
			connection.close();
			return ret;
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}

	/**
	 * this is an ugly hack. Don't use it
	 * 
	 * @param simulationId
	 * @return
	 */
	private String getStudyVersion(int simulationId, String dbUrl,
			String dbUser, String dbPasswd) {
		try {
			Class.forName("com.mysql.jdbc.Driver");
			Connection connection = DriverManager.getConnection(dbUrl, dbUser,
					dbPasswd);
			ResultSet res = connection.createStatement().executeQuery(
					"select distinct simulationId from java_lang_Object where simulationId ="
							+ simulationId + ";");
			if (res.next())
				throw new Error("simulation already evaluated!");
			res.close();
			res = connection
					.createStatement()
					.executeQuery(
							"select studies.version from studies join groups on studies.id = groups.studyId join simulations on groups.id = simulations.groupId where simulations.id ="
									+ simulationId);
			res.next();
			String ret = res.getString(1);
			res.close();
			connection.close();
			return ret;
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return "";
	}

	public void reEvalFlows(String dbUrl, String dbUser, String dbPasswd,
			int minId, int maxId, String dbResUrl, String dbResUser,
			String dbResPasswd) throws Exception {
		DbBinaryLoader loader = new DbBinaryLoader(dbUrl, dbUser, dbPasswd);
		Class.forName("com.mysql.jdbc.Driver");
		Connection connection = DriverManager.getConnection(dbResUrl,
				dbResUser, dbResPasswd);
		PreparedStatement stmt = connection
				.prepareStatement("update test_click_NetcodingTest$ExtraFlowStats join test_click_NetcodingTest$SimulationResult$$extraFlowStats on elm=test_click_NetcodingTest$ExtraFlowStats.primary_key join test_click_NetcodingTest$SimulationResult on uid=test_click_NetcodingTest$SimulationResult.primary_key set opportunisticFragments=?, opportunisticPackets=? where simulationId=? and i=?;");
		PreparedStatement updateAggregation = connection.prepareStatement("update aggregation set opportunisticPackets=? where id=?;");
		
		List lstIds = loader.loadSimulationIds();
//		List results = new LinkedList();
		if (null == lstIds || lstIds.size() <= 0)
			throw new Error("no simulations found between in DB");
		for (int i = 0; i < lstIds.size(); i++) {
			int id = ((Integer) lstIds.get(i)).intValue();
			if (id < minId)
				continue;
			if (id > maxId)
				continue;
			SimulationResult result = new SimulationResult();
			result.simulationId = id;

			System.out.println("evaluating simulation " + result.simulationId);
			try {
				loadConfiguration(loader, result);
			} catch (Throwable e) {
				continue;
			}
			if (!result.opportunistic)
				continue;
			loadFlowStats(loader, result);
			loadForwardGraph(loader, result);
//			ExtraFlowStats allFlows = (ExtraFlowStats) result.extraFlowStats
//					.get(0);
			for (int flowNum = 0; flowNum < result.extraFlowStats.size(); ++flowNum) {
				ExtraFlowStats flow = (ExtraFlowStats) result.extraFlowStats
						.get(flowNum);
				stmt.setInt(1, flow.opportunisticFragments);
				stmt.setInt(2, flow.opportunisticPackets);
				stmt.setInt(3, result.simulationId);
				stmt.setInt(4, flowNum);
				System.out.println(stmt);
				stmt.execute();
				if (flowNum == 0) {
					updateAggregation.setInt(1, flow.opportunisticPackets);
					updateAggregation.setInt(2, result.simulationId);
					System.out.println(updateAggregation);
					updateAggregation.execute();
				}
			}
		}

	}

	private void loadForwardGraph(DbBinaryLoader loader, SimulationResult result)
			throws Exception {

		ForwardGraphData fwGraph = (ForwardGraphData) load(loader,
				result.simulationId, "Global, Forward Graph");
		Collection nodes = fwGraph.nodes.values();
		Collection links = fwGraph.fwdLinks.values();
		FlowSorter sorters[] = new FlowSorter[result.flows];
		Set nodeSets[] = new Set[result.flows];
		for (int flowNum = 0; flowNum < result.flows; flowNum++) {
			FlowStats stats = (FlowStats) result.flowStats.get(flowNum + 1);
			sorters[flowNum] = new FlowSorter(stats.getSrc().getId(), stats
					.getDst().getId());
			nodeSets[flowNum] = new HashSet();
		}
		result.nodes = new Node[nodes.size()];
		result.links = new FwdLink[links.size()];

		Iterator j = nodes.iterator();
		int num = 0;
		while (j.hasNext()) {
			Node node = (Node) j.next();
			result.nodes[node.nodeId - 1] = node;
		}
		j = links.iterator();
		num = 0;
		while (j.hasNext()) {
			FwdLink link = (FwdLink) j.next();
			if (link.flowId < result.flows) {
				ExtraFlowStats stats = (ExtraFlowStats) result.extraFlowStats
						.get(link.flowId + 1);
				stats.receivedFragments += link.noPackets;
				// stats.receivedFragments += link.noDuplicates;
				stats.duplicateFragments += link.noDuplicates;

				nodeSets[link.flowId].add(link.from);
				nodeSets[link.flowId].add(link.to);
				sorters[link.flowId].addLink(link);
				result.links[num++] = link;
			}

		}

		ExtraFlowStats allFlows = (ExtraFlowStats) result.extraFlowStats.get(0);

		for (num = 0; num < result.flows; ++num) {
			ExtraFlowStats stats = (ExtraFlowStats) result.extraFlowStats
					.get(num + 1);
			stats.flowId = num;
			stats.route = new Node[nodeSets[num].size()];
			j = nodeSets[num].iterator();
			int numInArray = 0;
			while (j.hasNext()) {
				stats.route[numInArray++] = (Node) j.next();
			}
			if (!result.protocol.equals("DSR"))
				Arrays.sort(stats.route, sorters[num]);
			stats.numHops = stats.route.length;
			if (!result.protocol.equals("DSR")) {
				for (int pos = 0; pos < stats.numHops - 1; ++pos) {
					Node src = stats.route[pos];
					Node next = stats.route[pos + 1];
					j = links.iterator();
					while (j.hasNext()) {
						FwdLink link = (FwdLink) j.next();
						if (link.from == src && link.to != next && link.flowId == num) {
							stats.opportunisticFragments += link.noPackets;
							// stats.opportunisticFragments +=
							// link.noDuplicates;
						}
					}
				}
			}
			stats.opportunisticPackets = stats.opportunisticFragments
					/ result.fragmentsInPacket;
			stats.duplicatePackets = stats.duplicateFragments
					/ result.fragmentsInPacket;
			stats.receivedPackets = stats.receivedFragments
					/ result.fragmentsInPacket;

			allFlows.duplicateFragments += stats.duplicateFragments;
			allFlows.receivedFragments += stats.receivedFragments;
			allFlows.duplicatePackets += stats.duplicatePackets;
			allFlows.receivedPackets += stats.receivedPackets;
			allFlows.opportunisticFragments += stats.opportunisticFragments;
			allFlows.opportunisticPackets += stats.opportunisticPackets;
			allFlows.numHops += stats.numHops;

		}
		allFlows.numHops /= result.flows;
		allFlows.flowId = -1;

	}

	private void loadConfiguration(DbBinaryLoader loader,
			SimulationResult result) throws Exception {

		NetcodingParams params = (NetcodingParams) load(loader,
				result.simulationId, "Global, Config");

		ClickBuilder.NetParams click = (ClickBuilder.NetParams) params.nodeParams[0].net;
		String clickFile = click.clickfile;

		if (clickFile.contains("dsr")) {
			result.protocol = "DSR";
			result.fragmentsInBatch = 1;
			result.fragmentsInPacket = 1;
			result.fragmentsOutPacket = 1;
			result.opportunistic = false;
		} else if (!clickFile.equals("res/click/meshnode_netcoding.click")) {
			result.protocol = "Netcoding";
			String libParams[] = clickFile.replace(".click", "").split("_");
			result.fragmentsInBatch = Integer.parseInt(libParams[2]);
			result.fragmentsInPacket = Integer.parseInt(libParams[3]);
			/* all packets have equal numbers of fragments in new form */
			result.fragmentsOutPacket = result.fragmentsInPacket;
			if (libParams.length > 4 && libParams[4].equals("noopp"))
				result.opportunistic = false;
			else
				result.opportunistic = true;
		} else {
			/*
			 * old form, parameters encoded in click library file name packets
			 * can have different numbers of fragments
			 */
			result.protocol = "Netcoding";
			String clickLib = click.clickLibrary;
			String libParams[] = clickLib.split("_");
			int packetsInBatch = Integer.parseInt(libParams[2]);
			result.fragmentsInPacket = Integer.parseInt(libParams[3]);
			result.fragmentsInBatch = result.fragmentsInPacket * packetsInBatch;
			result.fragmentsOutPacket = Integer.parseInt(libParams[4]);
			result.opportunistic = true;
		}
		result.seed = params.seed;
		result.numNodes = params.nodeNumber[0];

		MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) params.nodeParams[0].mac;

		result.dataRate = ((RateBuilder.ConstantParams) mac.rateSelection).dataBitrate;
		result.controlRate = ((RateBuilder.ConstantParams) mac.rateSelection).controlBitrate;

    TrafficParams nodeParams = (TrafficParams) params.nodeParams[0];
		result.flows = ((TrafficBuilder.TrafficParams)nodeParams.traffic).flows;

		RadioBuilder.NoiseAdditiveBerParams radio = (RadioBuilder.NoiseAdditiveBerParams) params.nodeParams[0].radio;
		switch (radio.bitErrorModel) {
		case brn.swans.Constants.BITERRORS_NONE:
			result.bitErrorModel = "None";
			break;
		case brn.swans.Constants.BITERRORS_UNIFORM:
			result.bitErrorModel = "Uniform";
			break;
		case brn.swans.Constants.BITERRORS_ACCUMULATED:
			result.bitErrorModel = "Accumulated";
			break;
		case brn.swans.Constants.BITERRORS_MASKED:
			result.bitErrorModel = "MaskedPackets";
			break;
		default:
			throw new Error("unrecognized bit error model");
		}

		result.recvCorruptPackets = radio.recvCorruptPackets;

		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) params.field;
		if (field.fading instanceof RicianParams) {
			result.fadingModel = "Rician";
			result.fadingK = ((RicianParams) field.fading).K;
		} else if (field.fading instanceof RayleighParams)
			result.fadingModel = "Rayleigh";
		else if (field.fading instanceof PunnooseRicianParams) {
			result.fadingModel = "PunnooseRician";
			result.fadingK = ((PunnooseRicianParams) field.fading).K;
			result.fadingVelocity = ((PunnooseRicianParams) field.fading).maxVelocity;
		}
	}

	private void loadFlowStats(DbBinaryLoader loader, SimulationResult result)
			throws Exception {
		// flow parameters
		result.flowStats = new ArrayList();
		result.extraFlowStats = new ArrayList();
		int lostFlows = 0;
		for (int flow = -1; flow < result.flows; flow++) {
			String flowName = (-1 == flow ? "All Flows, " : "Flow " + flow
					+ ", ");
			flowName += "Application, ";

			{
				PropertiesData data = (PropertiesData) load(loader,
						result.simulationId, flowName + "Flow stats");
				FlowStats stats = (FlowStats) data.getProperties();

				if (0 > flow) {
					result.avgThroughput = stats.getAvgThroughput();
					result.avgDelay = stats.getAvgDelay();
					result.sendPackets = stats.getSendPackets();
					result.recvPackets = stats.getRecvPackets();
				} else if (stats.getRecvPackets() == 0) {
					lostFlows++;
					continue;
				}
				result.flowStats.add(stats);
				result.extraFlowStats.add(new ExtraFlowStats());
			}

			{
				DescriptiveStatistics stats = DescriptiveStatistics
						.newInstance();
				DiagramData data = (DiagramData) load(loader,
						result.simulationId, flowName + "packet delay", false);
				if (data != null) {
					double[] y = data.getY();
					for (int j = 1; j < y.length; j++)
						stats.addValue(y[j] - y[j - 1]);
				}

			}

			{
				DescriptiveStatistics stats = DescriptiveStatistics
						.newInstance();
				DiagramData data = (DiagramData) load(loader,
						result.simulationId, flowName
								+ "packet arrival vs time", false);
				if (data != null) {
					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]);
				}

			}
		}
		result.flows -= lostFlows;
	}

	/**
	 * This method is a somewhat hacked up version of the usual evaluation
	 * method. Take care as some of the parameters have a different meaning than
	 * usual. It evaluates a number of simulations with sequential IDs instead
	 * of a specific study and it makes assumptions about the results database.
	 * 
	 * @param dbUrl
	 *            database from where to read the binary objects
	 * @param dbUser
	 * @param dbPasswd
	 * @param minIdStr
	 *            this is NOT the driver/name of the study to be evaluated, but
	 *            the minimum ID of the simulations to be evaluated, in string
	 *            form
	 * @param maxIdStr
	 *            this is NOT the version of the study to be evaluated, but the
	 *            maximum ID of the simulations to be evaluated, in string form.
	 * @param dbResUrl
	 *            database where the evaluated results shall be written. It is
	 *            assumed that this DB contains the *definitions*, too so that
	 *            we can find the driver and version from the simulationId.
	 * @return
	 */
	public void evalSimulationResults(String dbUrl, String dbUser,
			String dbPasswd, String minIdStr, String maxIdStr, String dbResUrl,
			String dbResUser, String dbResPasswd) throws Throwable {
		// driver = "DSR - Interference";
		// version = "080607-2";
		DbBinaryLoader loader = new DbBinaryLoader(dbUrl, dbUser, dbPasswd);
		List lstIds = loader.loadSimulationIds(); // driver, version,
													// "simulation");
		// TODO of course these are ALL ids. Solution was to either create a
		// custom
		// method which takes driver, version, and db-name or adjust the
		// "continue" clause
		// below each time.

		List results = new LinkedList();
		if (null == lstIds || lstIds.size() <= 0)
			throw new Error("no simulations found between in DB");
		int minId = Integer.parseInt(minIdStr);
		int maxId = Integer.parseInt(maxIdStr);
		for (int i = 0; i < lstIds.size(); i++) {
			int id = ((Integer) lstIds.get(i)).intValue();
			if (id < minId)
				continue;
			if (id > maxId)
				continue;
			SimulationResult result = new SimulationResult();
			result.simulationId = id;
			// if (result.simulationId < 1107 /*|| result.simulationId > 1129*/)
			// if (id != 1151 && id != 1177 && id != 1183 && id != 1187 && id !=
			// 1188
			// && id != 1192 && id != 1200)
			// continue;
			System.out.println("evaluating simulation " + result.simulationId);

			try {
				result.driver = getStudyName(id, dbResUrl, dbResUser,
						dbResPasswd);
				result.version = getStudyVersion(id, dbResUrl, dbResUser,
						dbResPasswd);
				// read configuration
				loadConfiguration(loader, result);

				loadFlowStats(loader, result);

				loadForwardGraph(loader, result);

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

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

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

					{ // mac backoff
						DiagramDataHist data = (DiagramDataHist) 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) load(loader,
								result.simulationId,
								"Global, Mac, short retries vs time", false);
						result.macShortRetries = data == null ? 0
								: data.getY().length;
					}
				}

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

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

			results.add(result);
		}

		// 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++) {
			SimulationResult result = (SimulationResult) results.get(i);
			DBSaver saver = new DBSaver(dbResUrl, dbResUser, dbResPasswd,
					result.simulationId);
			saver.save(result, result.driver + "-" + result.version, false);
			saver.finalize();
		}
	}

	public List getSimulationSuite(String version) {
		List simulations = new LinkedList();
		NetcodingParams params = new NetcodingParams();

		// TODO: have the DataManager save everything (including TcpTrace)
		// somewhere
		int level = DataManager.LEVEL_ALL;
		int timebarLevel = DataManager.LEVEL_OFF;
		params.db = true;
		params.dbUser = "distsim";
		params.dbPassword = "distsim";
		params.dbDef = "jdbc:mysql://gorillaz:3306/simulation";
		params.dbRes = "jdbc:mysql://gorillaz:3306/simulation";
		params.handlerFieldLevel = level;
		params.handlerRadioLevel = level;
		params.handlerRadioDivLevel = level;
		params.handlerMacLevel = level;
		params.handlerRateLevel = level;
		params.handlerNetLevel = level;
		params.handlerRouteLevel = level;
		params.handlerTransLevel = level;
		params.handlerFlowLevel = level;

		params.handlerRadioTimeBarLevel = timebarLevel;
		params.handlerMacTimeBarLevel = timebarLevel;
		params.handlerNetTimeBarLevel = timebarLevel;

		params.handlerForwardGraphLevel = level;
		params.handlerLinkTableLevel = level;
		params.handlerLinkQualityLevel = level;

		// set parameters
		params.nodeNumber[0] = 25;
		params.endTime = 90;

		LogDistanceParams pathloss = new LogDistanceParams();

		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) params.field;
		field.fieldX = 150;
		field.fieldY = 150;
		field.pathloss = pathloss;
		field.spatial_mode = Constants.SPATIAL_LINEAR;
		RadioBuilder.NoiseAdditiveBerParams radio = new RadioBuilder.NoiseAdditiveBerParams();
		radio.placement = Constants.PLACEMENT_GRID;

		radio.connectivityBitRate = Constants.BANDWIDTH_54Mbps;
		radio.placementOpts = "5,5";
		radio.fieldX = field.fieldX;
		radio.fieldY = field.fieldY;
		radio.useAnnos = true;
		params.nodeParams[0].radio = radio;
		ClickBuilder.MacParams mac = new ClickBuilder.MacParams();
		RateBuilder.ConstantParams rate = new RateBuilder.ConstantParams();
		rate.setControlBitrate(Constants.BANDWIDTH_6Mbps);
		rate.setDataBitrate(Constants.BANDWIDTH_54Mbps);
		mac.setRateSelection(rate);
		mac.macPromisc = true;
		mac.useAnnos = radio.useAnnos;
		mac.useBitRateAnnos = true;
		params.nodeParams[0].mac = mac;
		ClickBuilder.NetParams net = new ClickBuilder.NetParams();
		net.protocolMapper = new int[] { Constants.NET_PROTOCOL_UDP,
				Constants.NET_PROTOCOL_LINK_PROBE, Constants.NET_PROTOCOL_TCP };
		params.nodeParams[0].net = net;
		params.nodeParams[0].route = null;
		UdpParams transParams = new TransBuilder.UdpParams();
		
		TrafficParams nodeParams = (TrafficParams) params.nodeParams[0];
		nodeParams.trans = transParams;
		HorizontalParams traffic = new HorizontalParams();
		CbrUdpParams flowParams = new CbrUdpParams();
		// flowParams.queueLess = true;
		// flowParams.maxOutstandingPackets = 200;
		flowParams.maxNumberOfPackets = Integer.MAX_VALUE;
		flowParams.udpTxRate = 3000;
		traffic.flowParams = flowParams;
		traffic.startOffset = 0;
		nodeParams.traffic = traffic;

		PunnooseRicianParams[] fading = { /*
										 * new RayleighParams(), new
										 * RicianParams(),
										 */new PunnooseRicianParams() };
		fading[0].maxVelocity = 16;
		int bitErrors[] = { brn.swans.Constants.BITERRORS_NONE,
		// brn.swans.Constants.BITERRORS_UNIFORM,
		// brn.swans.Constants.BITERRORS_ACCUMULATED
		};
		String clickFiles[] = {
		// "res/click/meshnode_dsr.click",
		// "res/click/meshnode_netcoding_1_1.click",
		// "res/click/meshnode_netcoding_1_1_noopp.click",
		"res/click/meshnode_netcoding_1_1_noopp_volroute.click",
		// "res/click/meshnode_netcoding_32_2.click",
		// "res/click/meshnode_netcoding_128_8.click",
		// "res/click/meshnode_netcoding_32_2_noopp.click",
		};
		int numFlows[] = { 1, 3 };
		for (int f = 0; f < fading.length; ++f) {
			field = (FieldBuilder.FieldParams) params.field;
			field.fading = fading[f];

			for (int b = 0; b < bitErrors.length; ++b) {
				radio = (NoiseAdditiveBerParams) params.nodeParams[0].radio;
				radio.setBitErrorModel(bitErrors[b]);
				if (radio.bitErrorModel == brn.swans.Constants.BITERRORS_NONE)
					radio.recvCorruptPackets = false;
				else
					radio.recvCorruptPackets = true;

				/*
				 * switch (f) { case 0: radio.min_connectivity_betweenNodes = 2;
				 * case 1:
				 */
				// radio.min_connectivity_betweenNodes = 7;
				/*
				 * case 2: radio.min_connectivity_betweenNodes = 3; }
				 */

				for (int c = 0; c < clickFiles.length; ++c) {
					net = (NetParams) params.nodeParams[0].net;

					net.clickfile = clickFiles[c];

					net.clickLibrary = "jistclick";

					for (int n = 0; n < numFlows.length; ++n) {
					  nodeParams = (TrafficParams) params.nodeParams[0];
						traffic = (HorizontalParams) nodeParams.traffic;
						traffic.flows = numFlows[n];
						for (int seed = 1000; seed < 1020; ++seed) {
							params.seed = seed;
							// ((HorizontalParams)params.trafficParams).seed =
							// seed;
							simulations.add(params);
							try {
								params = (NetcodingParams) params.clone();
							} catch (CloneNotSupportedException e) {
								e.printStackTrace();
							}
						}
					}
				}

			}
		}
		return simulations;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see brn.sim.AbstractDriver#installHandlers(brn.sim.AbstractParams)
	 */
	protected void installHandlers(AbstractParams options) throws Exception {
		super.installHandlers(options);

		addHandler(new StatsClickHandler(options.sampleLen),
				DataManager.LEVEL_ALL);

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see brn.sim.AbstractDriver#installBuilders(brn.sim.AbstractParams)
	 */
	protected void installBuilders(AbstractParams options) {
		super.installBuilders(options);

		builderProvider.addBuilder(new ClickBuilder.Net());
		builderProvider.addBuilder(new ClickBuilder.Mac());
	}

	public static void mainLocal() throws CloneNotSupportedException {
		NetcodingParams params = new NetcodingParams();
		NetcodingTest test = new NetcodingTest();

		// TODO: have the DataManager save everything (including TcpTrace)
		// somewhere
		int level = DataManager.LEVEL_ALL;
		int timebarLevel = DataManager.LEVEL_OFF;
		params.db = true;
		params.dbUser = "distsim";
		params.dbPassword = "distsim";
		params.handlerFieldLevel = level;
		params.handlerRadioLevel = level;
		params.handlerRadioDivLevel = level;
		params.handlerMacLevel = level;
		params.handlerRateLevel = level;
		params.handlerNetLevel = level;
		params.handlerRouteLevel = level;
		params.handlerTransLevel = level;
		params.handlerFlowLevel = level;

		params.handlerRadioTimeBarLevel = level;
		params.handlerMacTimeBarLevel = timebarLevel;
		params.handlerNetTimeBarLevel = timebarLevel;

		params.handlerForwardGraphLevel = level;
		params.handlerLinkTableLevel = level;
		params.handlerLinkQualityLevel = level;

		// set parameters
		params.seed = 4;
		params.nodeNumber[0] = 25;
		params.nodeParams[0] = new NodeBuilder.TrafficParams();
		params.endTime = 90;
    TrafficParams nodeParams = (TrafficParams) params.nodeParams[0];

		LogDistanceParams pathloss = new LogDistanceParams();
		PunnooseRicianParams fading = new PunnooseRicianParams();
		fading.maxVelocity = 16;

		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) params.field;
		field.fading = fading;
		field.fieldX = 150;
		field.fieldY = 150;
		field.pathloss = pathloss;
		field.spatial_mode = Constants.SPATIAL_LINEAR;
		RadioBuilder.NoiseAdditiveBerParams radio = new RadioBuilder.NoiseAdditiveBerParams();
		radio.placement = Constants.PLACEMENT_GRID;

		radio.connectivityBitRate = Constants.BANDWIDTH_54Mbps;
		radio.placementOpts = "5,5";
		radio.fieldX = field.fieldX;
		radio.fieldY = field.fieldY;
		radio.useAnnos = true;
		//radio.recvCorruptPackets = false;
		radio.setBitErrorModel(brn.swans.Constants.BITERRORS_NONE);
		params.nodeParams[0].radio = radio;

		ClickBuilder.MacParams mac = new ClickBuilder.MacParams();
		RateBuilder.ConstantParams rate = new RateBuilder.ConstantParams();
		rate.setControlBitrate(Constants.BANDWIDTH_6Mbps);
		rate.setDataBitrate(Constants.BANDWIDTH_54Mbps);
		mac.setRateSelection(rate);
		mac.macPromisc = true;
		mac.useAnnos = radio.useAnnos;
		mac.useBitRateAnnos = true;
		params.nodeParams[0].mac = mac;

		ClickBuilder.NetParams net = new ClickBuilder.NetParams();
		net.clickfile = "res/click/meshnode_netcoding_1_1.click";
		net.clickLibrary = "jistclick";
		net.protocolMapper = new int[] { Constants.NET_PROTOCOL_UDP,
				Constants.NET_PROTOCOL_LINK_PROBE, Constants.NET_PROTOCOL_TCP };

		params.nodeParams[0].net = net;
		params.nodeParams[0].route = null;

		nodeParams.trans = new TransBuilder.UdpParams();

		HorizontalParams traffic = new HorizontalParams();
		CbrUdpParams flowParams = new CbrUdpParams();
		flowParams.maxNumberOfPackets = Integer.MAX_VALUE;
		flowParams.udpTxRate = 3000;

		traffic.flowParams = flowParams;
		traffic.flows = 1;
		traffic.startOffset = 0;

		nodeParams.traffic = traffic;

		try {
			test.run(params);
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}

	}

	public static void main(String[] params) throws Throwable {
		if (params.length == 0) {
			//NetcodingTest test = new NetcodingTest();
			//test.reEvalFlows("jdbc:mysql://gorillaz/simulation_results",
			//		"distsim", "distsim", 1106, 2868,
			//		"jdbc:mysql://gorillaz/simulation", "distsim", "distsim");
			mainLocal();
		} else {
			NetcodingTest test = new NetcodingTest();
			test.run(params);
		}

	}

}
