package brn.sim.scenario.rca;

import java.io.IOException;

import jist.runtime.JistAPI;
import jist.swans.Constants;
import jist.swans.misc.Util;
import brn.sim.AbstractParams;
import brn.sim.builder.FlowBuilder;
import brn.sim.builder.FadingBuilder;
import brn.sim.builder.FieldBuilder;
import brn.sim.builder.MacBuilder;
import brn.sim.builder.NetBuilder;
import brn.sim.builder.RadioBuilder;
import brn.sim.builder.RateBuilder;
import brn.sim.builder.RouteBuilder;
import brn.sim.builder.TrafficBuilder;
import brn.sim.builder.TransBuilder;
import brn.sim.builder.FadingBuilder.PunnooseRicianParams;
import brn.sim.builder.PathLossBuilder.LogDistanceParams;
import brn.sim.builder.PathLossBuilder.ShadowingParams;
import brn.sim.builder.TrafficBuilder.TrafficParams;
import brn.sim.builder.TransBuilder.TransParams;

public class RcaParams extends AbstractParams {
	private static final long serialVersionUID = 1L;

	public TrafficBuilder.TrafficParams trafficParams;

	public TransBuilder.TransParams transParams;

	public RcaParams() {
		/* global */
		this.endTime = 100; // in seconds!
		this.assertion = true;
		this.seed = 2;
		this.nodes = 2;

		/* handler */
		this.handlerRadio = false;
		this.handlerMac = true;
		this.handlerRate = true;
		this.handlerNet = true;
		this.handlerRoute = false;
		this.handlerFlow = true;
		this.handlerForwardGraph = true;
		this.handlerLinkQuality = true;
		this.handlerLinkTable = true;
		this.handlerRadioTimeBar = false;
		this.handlerMacTimeBar = true;
		this.handlerNetTimeBar = false;
		this.handlerTrans = false;

    LogDistanceParams lossExp = new LogDistanceParams();
    lossExp.exponent = 3.5;

    ShadowingParams pathLoss = new ShadowingParams();
    pathLoss.pathloss = lossExp;
    pathLoss.stdDeviation = 0.;
    pathLoss.exponential = false; // JistAPI.END !!
    pathLoss.coherenceTime = JistAPI.END;// (long)(2.5 * Constants.SECOND);

		// FadingBuilder.PunnooseRicianParams fading = new
		// FadingBuilder.PunnooseRicianParams();
		// fading.maxVelocity = 1.5;
		// fading.K = 0;
		FadingBuilder.NoneParams fading = new FadingBuilder.NoneParams();

		FieldBuilder.FieldParams field = new FieldBuilder.FieldParams();
		field.fieldX = 100;
		field.fieldY = 1;
		field.spatial_mode = Constants.SPATIAL_LINEAR;
		field.pathloss = pathLoss;
		field.fading = fading;
		this.field = field;

		RadioBuilder.NoiseParams radio = new RadioBuilder.NoiseAdditiveBerParams();/* Ber */
		radio.fieldX = field.fieldX;
		radio.fieldY = field.fieldY;
		radio.placement = Constants.PLACEMENT_GRID;
		radio.placementOpts = "2x1";
		radio.useAnnos = true; //false;
		radio.radioType = Constants.MAC_802_11g_PURE;// MAC_802_11bg;
		this.node.radio = radio;

		MacBuilder.M802_11Params mac = new MacBuilder.M802_11Params();
		mac.useAnnos = radio.useAnnos;
		mac.useBitRateAnnos = false;
//		mac.macType = (short) radio.radioType;
		RateBuilder.ConstantParams rate = new RateBuilder.ConstantParams();
		rate.controlBitrate = Constants.BANDWIDTH_1Mbps;
		rate.dataBitrate = Constants.BANDWIDTH_1Mbps;
		mac.rateSelection = rate;
		this.node.mac = mac;

		NetBuilder.IpParams net = new NetBuilder.IpMacTestParams();
		net.protocolMapper = new int[] { Constants.NET_PROTOCOL_DUMB };
		this.node.net = net;

		// DumbParams route = new DumbParams();
		// route.protocol = Constants.NET_PROTOCOL_DUMB;
		// RouteBuilder.AodvParams route = new RouteBuilder.AodvParams();
		this.node.setRoute(/* route */null);

		this.transParams = new TransBuilder.UdpParams();

		// Watch the right order
		TrafficBuilder.HorizontalParams trafficParams = new TrafficBuilder.HorizontalParams();
		FlowBuilder.CaUdpParams udpCa = new FlowBuilder.CaUdpParams();
		udpCa.start = 10000;
		udpCa.end = 300000;
		udpCa.maxOutstandingPackets = 180;
		trafficParams.flowParams = udpCa;
		trafficParams.flows = 1;
		this.trafficParams = trafficParams;
		// this.trafficParams.flowParams = udpCa;
	}

	/**
	 * Parameters for use of AARF rate control algorithm
	 */
	public static class AarfParams extends RcaParams {
		private static final long serialVersionUID = 4230757956188337932L;

		//
		public AarfParams() {
			super();
			MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) this.node.mac;
			RateBuilder.AarfParams rate = new RateBuilder.AarfParams();
//			rate.macType = mac.macType;
			mac.rateSelection = rate;
			this.node.mac = mac;
		}
	}

	//
	/**
	 * Parameters for use of ARF rate control algorithm
	 */
	public static class ArfParams extends RcaParams {
		private static final long serialVersionUID = 9089896517736735806L;

		//
		public ArfParams() {
			super();
			MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) this.node.mac;
			RateBuilder.ArfParams rate = new RateBuilder.ArfParams();
//			rate.macType = mac.macType;
			mac.rateSelection = rate;
			this.node.mac = mac;
		}
	}

	/**
	 * Parameters for use of ARF rate control algorithm
	 */
	public static class AmrrParams extends RcaParams {
		private static final long serialVersionUID = 9089896517736735806L;

		//
		public AmrrParams() {
			super();
			MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) this.node.mac;
			RateBuilder.AmrrParams rate = new RateBuilder.AmrrParams();
//			rate.macType = mac.macType;
			mac.rateSelection = rate;
			this.node.mac = mac;
		}
	}

	//
	/**
	 * Parameters for use of ConstantRate rate control algorithm
	 */
	public static class ConstParams extends RcaParams {
		private static final long serialVersionUID = 8604206368367872661L;

		//
		public ConstParams() {
			this(Constants.BANDWIDTH_1Mbps, Constants.BANDWIDTH_1Mbps);
		}

		//
		public ConstParams(int dataRate, int controlRate) {
			MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) this.node.mac;
			RateBuilder.ConstantParams rate = new RateBuilder.ConstantParams();
			rate.setControlBitrate(controlRate);
			rate.setDataBitrate(dataRate);
			mac.rateSelection = rate;
			this.node.mac = mac;
		}
	}

	//
	/**
	 * Parameters for use of SampleRate rate control algorithm
	 */
	public static class SampleParams extends RcaParams {
		private static final long serialVersionUID = -8658329656480908706L;

		//
		public SampleParams() {
			MacBuilder.M802_11Params mac = (MacBuilder.M802_11Params) this.node.mac;
			RateBuilder.SampleParams rate = new RateBuilder.SampleParams();
//			rate.macType = mac.macType;
			mac.rateSelection = rate;
			this.node.mac = mac;
		}
	}
	
	public RcaParams setCustomRadioParams() {
	  RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) this.node.radio;
	  radio.radioDesc = "mac=802.11g\r\nbitrates=6,12\r\nbasicRates=6";
	  setAppUdp(2500);
	  return this;
	}
  
  public RcaParams setMac80211eParams() {
    RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) this.node.radio;
    MacBuilder.M802_11eParams mac = new MacBuilder.M802_11eParams();
    mac.useAnnos = radio.useAnnos;
    mac.useBitRateAnnos = false;
    mac.constBitRate = Constants.BANDWIDTH_6Mbps;
    RateBuilder.ConstantParams rate = new RateBuilder.ConstantParams();
    rate.controlBitrate = Constants.BANDWIDTH_6Mbps;
    rate.dataBitrate = Constants.BANDWIDTH_6Mbps;
    //mac.rateSelection = rate;
    this.node.mac = mac;
    return this;
  }

	public RcaParams setUdpDsrParams() {
		NetBuilder.IpParams net = new NetBuilder.IpParams();
		net.protocolMapper = new int[] { Constants.NET_PROTOCOL_UDP,
				Constants.NET_PROTOCOL_DSR };
		this.node.net = net;
		this.node.route = new RouteBuilder.DsrParams();
		return this;
	}

	public RcaParams setProtocolsUdpAodv() {
		NetBuilder.IpParams net = new NetBuilder.IpParams();
		net.protocolMapper = new int[] { Constants.NET_PROTOCOL_UDP,
				Constants.NET_PROTOCOL_AODV };
		this.node.net = net;
		RouteBuilder.AodvParams aodv = new RouteBuilder.AodvParams();
		aodv.aodvStats = true;
		aodv.aodvHelloInterval = 5;
		this.node.route = aodv;
		return this;
	}

	public RcaParams setMovingAway() {
		return this.setMovingAway(1);
	}

	public RcaParams setMovingAway(int initDist) {
		return this.setMovingAway(1, 1);
	}

	public RcaParams setMovingAway(int initialDistance, double speed) {
		return this.setMovingAway(initialDistance, speed, 1);
	}

	/**
	 * @param initialDistance
	 *          Initial distance in meters
	 * @return
	 */
	public RcaParams setMovingAway(int initialDistance, double speed,
			double stepTime) {
		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) this.field;
		field.fieldX = 400;
		field.fieldY = 100;
		field.mobility = Constants.MOBILITY_BOUNDLESS_SIM_AREA;
		field.mobilityOpts = /* min */speed + ":" + /* max */speed + ":0:0:"
				+ stepTime + ":0:" + /* start */speed + ":0";

		RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) this.node.radio;
		radio.fieldX = field.fieldX;
		radio.fieldY = field.fieldY;
		radio.placement = Constants.PLACEMENT_MANUAL;
		radio.placementOpts = "0x50:" + initialDistance + "x50";
		radio.startMobility = "-1:10"; // don't move node 1, start moving node 2 at t=10s
		// 2 at
		// 10s

		this.field = field;
		this.node.radio = node.radio;
		return this;
	}

	public RcaParams setMobility() {
		((FieldBuilder.FieldParams) this.field).mobility = Constants.MOBILITY_WAYPOINT;
		((RadioBuilder.NoiseParams) this.node.radio).startMobility = "10:0";
		return this;
	}

	/**
	 * @param packetRate
	 *          Packet sending rate per second
	 * @return
	 */
	public RcaParams setAppUdp(int packetRate) {
		FlowBuilder.CbrUdpParams udp = new FlowBuilder.CbrUdpParams();
		udp.start = 10000;
		udp.end = 300000;
		udp.udpTxRate = packetRate;
		this.trafficParams.flowParams = udp;
		return this;
	}

	public RcaParams setShadowSim1(double exp, double stdDev, long cohTime,
			int seed) {
		this.setAppUdp(2500); // really stuff the channel from the
		// beg.
		this.setMovingAway(20, 2);
		this.setProtocolsUdpAodv();
		this.endTime = 130 /* s */;
		this.nodes = 2;
		this.seed = seed; // different seed every time
		this.setParamsBatch();
		this.handlerNet = true;
		this.handlerForwardGraph = false;
		this.handlerRate = false;
		this.handlerMac = true;
		this.handlerField = true; // node distance
		this.handlerRadio = false;

    LogDistanceParams lossExp = new LogDistanceParams();
    ShadowingParams pathLoss = new ShadowingParams();

		lossExp.exponent = exp;
		// lossExp.refDist = refDist;
		pathLoss.pathloss = lossExp;
		pathLoss.stdDeviation = stdDev;
		pathLoss.coherenceTime = cohTime;

		pathLoss.exponential = true; // distShad;
		((FieldBuilder.FieldParams) this.field).pathloss = pathLoss;
		return this;
	}

	public RcaParams setShadowSim2(double exp, double stdDev, long cohTime,
			int seed) {
		this.setAppUdp(2600); // really stuff the channel from the beginning
		this.setMovingAway(20, 4, 0.5);
		this.setProtocolsUdpAodv();
		this.endTime = 70 /* s */;
		this.nodes = 2;
		this.seed = seed; // different seed every time
		this.setParamsBatch();
		this.handlerNet = true;
		this.handlerForwardGraph = false;
		this.handlerRate = false;
		this.handlerMac = true;
		this.handlerField = true; // node distance
		this.handlerRadio = false;

		LogDistanceParams lossExp = new LogDistanceParams();
    ShadowingParams pathLoss = new ShadowingParams();
		lossExp.exponent = exp;
		// lossExp.refDist = refDist;
		pathLoss.pathloss = lossExp;
		pathLoss.stdDeviation = stdDev;
		pathLoss.coherenceTime = cohTime;

		pathLoss.exponential = true; // distShad;
		((FieldBuilder.FieldParams) this.field).pathloss = pathLoss;
		return this;
	}

	public RcaParams setPathlossSim(double exp, int seed) {
		this.setAppUdp(2600); // really stuff the channel from the beginning
		this.setMovingAway(20, 4, 0.5);
		this.setProtocolsUdpAodv();
		this.endTime = 80 /* s */;
		this.nodes = 2;
		this.seed = seed; // different seed every time
		this.setParamsBatch();
		this.handlerNet = true;
		this.handlerForwardGraph = false;
		this.handlerRate = true;
		this.handlerMac = true;
		this.handlerField = true; // node distance
		this.handlerRadio = false;

		// Pathloss without shadowing
    LogDistanceParams lossExp = new LogDistanceParams();
    //ShadowingParams pathLoss = new ShadowingParams();
		//DistExponentialParams pathLoss = new DistExponentialParams();
		//pathLoss.exponent = exp;
    lossExp.exponent = exp;
		((FieldBuilder.FieldParams) this.field).pathloss = lossExp;
		return this;
	}
	
	public RcaParams setPathlossSim2(double exp, int seed) {
		this.setAppUdp(2600); // really stuff the channel from the beginning
		this.setMovingAway(258, 4, 0.5);
		this.setProtocolsUdpAodv();
		this.endTime = 21 /* s */;
		this.nodes = 2;
		this.seed = seed; // different seed every time
		this.setParamsBatch();
		this.handlerNet = true;
		this.handlerForwardGraph = false;
		this.handlerRate = true;
		this.handlerMac = true;
		this.handlerField = true; // node distance
		this.handlerRadio = false;

		// Pathloss without shadowing
    LogDistanceParams lossExp = new LogDistanceParams();
    lossExp.exponent = exp;
		((FieldBuilder.FieldParams) this.field).pathloss = lossExp;
		return this;
	}

	public RcaParams setPathlossFadingSim1(int seed) {
		return setPathlossFadingSim1(seed, 6);
	}
	
	public RcaParams setPathlossFadingSim1(int seed, int fadingK) {
		this.setPathlossSim(2.7, seed);
		this.handlerRate = true;
		
		FadingBuilder.PunnooseRicianParams fading = new FadingBuilder.PunnooseRicianParams();
		fading.maxVelocity = 1.5;
		fading.K = fadingK;

		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) this.field;
		field.fading = fading;
		
		return this;
	}
	
	public RcaParams setPathlossShadowingSim(int seed, int distInM) {
		this.setAppUdp(2600); // really stuff the channel from the beginning
		this.setProtocolsUdpAodv();
		this.endTime = 60 /* s */;
		this.nodes = 2;
		this.seed = seed; // different seed every time
		this.setParamsBatch();
		this.handlerNet = true;
		this.handlerForwardGraph = false;
		this.handlerRate = true;
		this.handlerMac = true;
		this.handlerField = false; // node distance
		this.handlerRadio = false;

		FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) this.field;
		field.fieldX = 200;
		field.fieldY = 100;
		
		RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) this.node.radio;
		radio.fieldX = field.fieldX;
		radio.fieldY = field.fieldY;
		radio.placement = Constants.PLACEMENT_MANUAL;
		radio.placementOpts = "0x50:" + distInM + "x50";
		
		// Pathloss with shadowing
    LogDistanceParams lossExp = new LogDistanceParams();
    lossExp.exponent = 2.7;
    ShadowingParams pathLoss = new ShadowingParams();
    pathLoss.coherenceTime = 10 * Constants.MILLI_SECOND;
    pathLoss.exponential = false;
		field.pathloss = lossExp;
		
		this.field = field;
		this.node.radio = node.radio;
		
		return this;
	}
	
	public RcaParams setPathlossShadowingMovingSim(int seed, int distInM) {
		this.setPathlossShadowingSim(seed, distInM);
		this.setMovingAway(distInM, 4, .5);
		this.endTime = (250 /* m */ - distInM) / 4;
		return this;
	}

	/**
	 * Set parameters for batch execution with distsim.
	 * 
	 * @return the parameters to adopt
	 */
	public RcaParams setParamsBatch() {
		RcaParams params = this;

		params.handlerRadio = false; // too big
		params.handlerRadioEx = false; // too big, to slow
		params.handlerMac = true;
		params.handlerRate = true;
		params.handlerNet = false;
		params.handlerRoute = false;
		params.handlerFlow = false;
		params.handlerForwardGraph = true;
		params.handlerLinkQuality = false; // too big
		params.handlerLinkTable = false;
		params.handlerRadioTimeBar = false; // too big
		params.handlerMacTimeBar = false; // too big
		params.handlerNetTimeBar = false; // too big

		params.dumpRadio = null;// "txdor.tr";
		params.dumpMac = false;
		params.dumpNet = false;
		params.dumpFieldNam = null;// "txdor-field.nam";
		params.dumpRadioNam = null;// "txdor-radio.nam";
		params.dumpMacNam = null;// "txdor-mac.nam";
		params.dumpNetNam = null;// "txdor-net.nam";

		return params;
	}

	/**
	 * Set parameters for local test execution.
	 * 
	 * @return the parameters to adopt
	 */
	public RcaParams setParamsTest() {
		RcaParams params = this;

		params.db = true;
		params.dbRes = "jdbc:mysql://localhost:3306/simulation";
		params.dbDef = "jdbc:mysql://localhost:3306/simulation";
		params.file = false;
		params.directory = "/tmp/sim";

		params.handlerRadio = false;
		params.handlerRadioEx = false;
		params.handlerMac = true;
		params.handlerNet = true;
		params.handlerRoute = true;
		params.handlerFlow = true;
		params.handlerForwardGraph = true;
		params.handlerLinkQuality = false;
		params.handlerLinkTable = true;
		params.handlerRadioTimeBar = false; // too big
		params.handlerMacTimeBar = false; // too big
		params.handlerNetTimeBar = false; // too big

		params.dumpRadio = null;// "txdor.tr";
		params.dumpMac = false;
		params.dumpNet = false;
		params.dumpFieldNam = null;// "txdor-field.nam";
		params.dumpRadioNam = null;// "txdor-radio.nam";
		params.dumpMacNam = null; // "txdor-mac.nam";
		params.dumpNetNam = null; // "txdor-net.nam";

		return params;
	}

	public static void main(String[] args) throws IOException {
		RcaParams params = new RcaParams().setProtocolsUdpAodv();

		// and out
		Util.writeObject("res/config/RcaParams.xml", params);
	}

	public TrafficBuilder.TrafficParams getTrafficParams() {
		return trafficParams;
	}

	public void setTrafficParams(TrafficBuilder.TrafficParams trafficParams) {
		this.trafficParams = trafficParams;
	}

	public TransBuilder.TransParams getTransParams() {
		return transParams;
	}

	public void setTransParams(TransBuilder.TransParams transParams) {
		this.transParams = transParams;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see brn.sim.AbstractParams#clone()
	 */
	public Object clone() throws CloneNotSupportedException {
		RcaParams ret = (RcaParams) super.clone();
		if (null != trafficParams)
			ret.trafficParams = (TrafficParams) trafficParams.clone();
		if (null != transParams)
			ret.transParams = (TransParams) transParams.clone();
		return ret;
	}

}
