package test.sim.scenario;

import java.util.ArrayList;
import java.util.List;

import jist.runtime.JistAPI;
import jist.runtime.Util;
import jist.swans.Constants;
import jist.swans.Node;
import jist.swans.mac.Mac802_11;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacDcf;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.misc.MessageBytes;
import jist.swans.net.NetAddress;
import jist.swans.net.NetMessage;
import jist.swans.trans.TransUdp;

import brn.sim.builder.FieldBuilder;
import brn.sim.builder.MacBuilder;
import brn.sim.builder.MetricBuilder;
import brn.sim.builder.PathLossBuilder;
import brn.sim.builder.RadioBuilder;
import brn.sim.builder.RateBuilder;
import brn.sim.builder.RouteBuilder;
import brn.sim.scenario.txdor.TxDOrParams;
import brn.sim.scenario.txdor.TxDOrSim;
import brn.swans.mac.MacTxDOR;
import brn.swans.mac.MacTxDORInterface;
import brn.swans.net.NetTxDOR;
import brn.swans.route.RouteDsrBrnMsg;

public class TestSchedule {

  /**
   * @param args
   */
  public static void main(String[] args) {
    int idx = Integer.valueOf(args[0]).intValue();

    switch (idx) {
    case 0:
      testSchedule();
      break;
    case 1:
      testSyncMac();
      break;
    case 2:
      testSyncNet();
      break;
    default:
      throw new RuntimeException("Unknown idx " + idx);
    }
  }

  private static void testSyncNet() {
    TxDOrParams params = TxDOrParams.defaultParams(new TxDOrParams());
    {
      params.seed = 2;
      params.nodes = 3;

      params.handlerRadioTimeBar = true;
      params.handlerMacTimeBar = true;
      params.handlerNetTimeBar = true;
      params.handlerRadio = true;

      FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) params.field;
      field.fieldX = 20;
      field.fieldY = 20;
      field.pathloss = new PathLossBuilder.FreeSpaceParams();

      RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) params.node.radio;
      radio.fieldX = field.fieldX;
      radio.fieldY = field.fieldY;
      radio.radioType = Constants.MAC_802_11bg;
      radio.placement = Constants.PLACEMENT_RANDOM;

      MacBuilder.M802_11TxDivParams mac = (MacBuilder.M802_11TxDivParams) params.node.mac;
      mac.thresholdRts = 600;
      RateBuilder.ConstantParams rateSelection = new RateBuilder.ConstantParams();
      rateSelection.dataBitrate = Constants.BANDWIDTH_1Mbps;
      rateSelection.controlBitrate = Constants.BANDWIDTH_1Mbps;
      mac.rateSelection = rateSelection;
//      mac.macType = Constants.MAC_802_11bg;

      RouteBuilder.TxDORParams route = (RouteBuilder.TxDORParams) params.node.route;
      route.candidateSetSize = 3;
//      route.globalForwarderTable = false;
      route.candidateSelection = brn.swans.Constants.TXDOR_CSSEL_LESSCONS;

      route.metric = new MetricBuilder.HopCountParams();

      params.trafficParams = null;
    }

    try {
      TxDOrSim test = new TxDOrSim();
//      test.run(TxDOrParams.setParamsTest(params));
      test.run(params);

      // prepare linktable

      Message udpMsg = new TransUdp.UdpMessage(12, 14, new MessageBytes(null, 0, 1460));
      // prepare message
      RouteDsrBrnMsg brnMsg = new RouteDsrBrnMsg(udpMsg, 0, 0, null);
      brnMsg.setNextHeaderType(Constants.NET_PROTOCOL_UDP);
      NetMessage.Ip msg = new NetMessage.Ip(brnMsg, NetAddress.NULL,
          new NetAddress(3), Constants.NET_PROTOCOL_MCEXOR, (byte)2, (byte)10);
      MessageAnno anno = new MessageAnno();
      List candidates = new ArrayList();
      candidates.add(new MacAddress(3));
      anno.put(MessageAnno.ANNO_MAC_CANDIDATES, candidates);
      anno.put(MessageAnno.ANNO_MAC_FINALDST_IDX, new Byte((byte)-1));

      // give message to nodes
      JistAPI.sleep(10 * Constants.SECOND);
      List nodes = test.getNodes();
      Node node0 = (Node) nodes.get(0);
      Node node1 = (Node) nodes.get(1);
      Node node2 = (Node) nodes.get(2);
      MacTxDOR mac0 = (MacTxDOR) node0.getMac(0);
      MacTxDOR mac1 = (MacTxDOR) node1.getMac(0);
      MacTxDOR mac2 = (MacTxDOR) node2.getMac(0);

      MacAddress nextHop = MacTxDOR.ADDR_ANYCAST;
      ((NetTxDOR)node1.getNet()).testEnqueuePacket(
          (byte)Constants.NET_INTERFACE_DEFAULT, msg, nextHop, anno);
      node0.getNet().getProxy().send(msg, Constants.NET_INTERFACE_DEFAULT, nextHop, anno);

      JistAPI.sleepBlock(10000030L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(MacDcf.MAC_MODE_DIFS == mac0.getMode());
      Util.assertion(MacDcf.MAC_MODE_SIDLE == mac1.getMode());
      Util.assertion(MacDcf.MAC_MODE_SIDLE == mac2.getMode());

      JistAPI.sleepBlock(10000300L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_XRTS == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
      Util.assertion(MacDcf.MAC_MODE_SIDLE == mac2.getMode());

      JistAPI.sleepBlock(10000500L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_SWFCTS == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
      Util.assertion(Mac802_11.MAC_MODE_XCTS == mac2.getMode());

      JistAPI.sleepBlock(10001000L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_XUNICAST == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_XUNICAST == mac1.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SWFDATA == mac2.getMode());

      JistAPI.sleepBlock(10013500L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_SWFACK == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
      Util.assertion(Mac802_11.MAC_MODE_XACK == mac2.getMode());

      JistAPI.sleepBlock(10013775L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac1.getMode());
      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac2.getMode());

      JistAPI.sleepBlock(10013825L * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(Mac802_11.MAC_MODE_SBO == mac0.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac2.getMode());

    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  private static void testSyncMac() {
    TxDOrParams params = TxDOrParams.defaultParams(new TxDOrParams());
    // TODO test with BER
    params = TxDOrParams.radioBerParams(params);
    {
      params.seed = 2;
      params.nodes = 3;

      params.handlerRadioTimeBar = true;
      params.handlerMacTimeBar = true;
      params.handlerNetTimeBar = true;

      FieldBuilder.FieldParams field = (FieldBuilder.FieldParams) params.field;
      field.fieldX = 20;
      field.fieldY = 20;
      field.pathloss = new PathLossBuilder.FreeSpaceParams();

      RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) params.node.radio;
      radio.fieldX = field.fieldX;
      radio.fieldY = field.fieldY;
//      radio.radioType = Constants.MAC_802_11bg;
      radio.placement = Constants.PLACEMENT_RANDOM;

      MacBuilder.M802_11TxDivParams mac = (MacBuilder.M802_11TxDivParams) params.node.mac;
      mac.thresholdRts = 600;
      RateBuilder.ConstantParams rateSelection = new RateBuilder.ConstantParams();
      rateSelection.dataBitrate = Constants.BANDWIDTH_6Mbps;
      rateSelection.controlBitrate = Constants.BANDWIDTH_6Mbps;
      mac.rateSelection = rateSelection;
//      mac.macType = Constants.MAC_802_11;

      RouteBuilder.TxDORParams route = (RouteBuilder.TxDORParams) params.node.route;
      route.candidateSetSize = 3;
//      route.globalForwarderTable = false;
      route.candidateSelection = brn.swans.Constants.TXDOR_CSSEL_LESSCONS;

      route.metric = new MetricBuilder.HopCountParams();

      params.trafficParams = null;
    }

    try {
      TxDOrSim test = new TxDOrSim();
      test.run(params);

      // prepare linktable

      Message udpMsg = new TransUdp.UdpMessage(12, 14, new MessageBytes(null, 0, 1460));
      // prepare message
      RouteDsrBrnMsg brnMsg = new RouteDsrBrnMsg(udpMsg, 0, 0, null);
      brnMsg.setNextHeaderType(Constants.NET_PROTOCOL_UDP);
      NetMessage.Ip msg = new NetMessage.Ip(brnMsg, NetAddress.NULL,
          new NetAddress(3), Constants.NET_PROTOCOL_MCEXOR, (byte)2, (byte)10);
      MessageAnno anno = new MessageAnno();
      List candidates = new ArrayList();
      candidates.add(new MacAddress(3));
      anno.put(MessageAnno.ANNO_MAC_CANDIDATES, candidates);
      anno.put(MessageAnno.ANNO_MAC_FINALDST_IDX, new Byte((byte)-1));

      // give message to nodes
      JistAPI.sleep(10 * Constants.SECOND);
      List nodes = test.getNodes();
      Node node0 = (Node) nodes.get(0);
      Node node1 = (Node) nodes.get(1);
      Node node2 = (Node) nodes.get(2);
      MacTxDOR mac0 = (MacTxDOR) node0.getMac(0);
      MacTxDOR mac1 = (MacTxDOR) node1.getMac(0);
      MacTxDOR mac2 = (MacTxDOR) node2.getMac(0);

      MacAddress nextHop = MacTxDOR.ADDR_ANYCAST;//new MacAddress(3);
      node0.getNet().getProxy().send(msg, Constants.NET_INTERFACE_DEFAULT, nextHop, anno);
      JistAPI.sleep(20 * Constants.MICRO_SECOND);
      node1.getNet().getProxy().send(msg, Constants.NET_INTERFACE_DEFAULT, nextHop, anno);

//      JistAPI.sleepBlock(10000030L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(MacDcf.MAC_MODE_DIFS == mac0.getMode());
//      Util.assertion(MacDcf.MAC_MODE_DIFS == mac1.getMode());
//
//      JistAPI.sleepBlock(10000300L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_XRTS == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
//
//      JistAPI.sleepBlock(10000500L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_SWFCTS == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_XCTS == mac2.getMode());
//
//      JistAPI.sleepBlock(10001000L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_XUNICAST == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_XUNICAST == mac1.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SWFDATA == mac2.getMode());
//
//      JistAPI.sleepBlock(10013500L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_SWFACK == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_XACK == mac2.getMode());
//
//      JistAPI.sleepBlock(10013775L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac1.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_DIFS == mac2.getMode());
//
//      JistAPI.sleepBlock(10013825L * Constants.MICRO_SECOND - JistAPI.getTime());
//      Util.assertion(Mac802_11.MAC_MODE_SBO == mac0.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac1.getMode());
//      Util.assertion(Mac802_11.MAC_MODE_SIDLE == mac2.getMode());
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

  /**
   *
   */
  private static void testSchedule() {
    TxDOrParams params = TxDOrParams.defaultParams(new TxDOrParams());
    {
      params.seed = 2;
      params.gui = true;
      params.nodes = 1;

      params.handlerRadioTimeBar = true;
      params.handlerMacTimeBar = true;
      params.handlerNetTimeBar = true;

      RadioBuilder.NoiseParams radio = (RadioBuilder.NoiseParams) params.node.radio;
      radio.radioType = Constants.MAC_802_11bg;

      MacBuilder.M802_11TxDivParams mac = (MacBuilder.M802_11TxDivParams) params.node.mac;
      mac.thresholdRts = 600;
      RateBuilder.ConstantParams rateSelection = new RateBuilder.ConstantParams();
      rateSelection.dataBitrate = Constants.BANDWIDTH_1Mbps;
      rateSelection.controlBitrate = Constants.BANDWIDTH_1Mbps;
      mac.rateSelection = rateSelection;
//      mac.macType = Constants.MAC_802_11bg;

      RouteBuilder.TxDORParams route = (RouteBuilder.TxDORParams) params.node.route;
      route.metric = new MetricBuilder.HopCountParams();
      route.candidateSetSize = 3;
//      route.globalForwarderTable = false;
      route.candidateSelection = brn.swans.Constants.TXDOR_CSSEL_LESSCONS;

      params.transParams   = null;
      params.trafficParams = null;
    }

    try {
      TxDOrSim test = new TxDOrSim();
//      test.run(TxDOrParams.setParamsTest(params));
      test.run(params);
      List nodes = test.getNodes();
      Node node = (Node) nodes.get(0);
      MacTxDOR mac = (MacTxDOR) node.getMac(0);
      RouteDsrBrnMsg brnMsg = new RouteDsrBrnMsg(new MessageBytes(null, 0, 1460), 0, 0, null);
      NetMessage.Ip msg = new NetMessage.Ip(brnMsg,
          NetAddress.NULL, NetAddress.ANY, (short)0, (byte)2, (byte)10);
      msg.freeze();
      MessageAnno anno = new MessageAnno();
      List candidates = new ArrayList();
      candidates.add(new MacAddress(1));
      candidates.add(new MacAddress(2));
      candidates.add(new MacAddress(3));
      short seqNo = 3;
      boolean retry = false;
      MacAddress src = new MacAddress(6);
      ((MacTxDORInterface)mac.getProxy()).sendWithTxDiv(msg, anno, candidates,
          2 * Constants.SECOND, src, seqNo, retry, (byte)-1);

      JistAPI.sleepBlock(2 * Constants.SECOND - JistAPI.getTime());
      Util.assertion(MacDcf.MAC_MODE_XUNICAST == mac.getMode());

      JistAPI.sleepBlock(2012350 * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(MacDcf.MAC_MODE_XUNICAST == mac.getMode());

      JistAPI.sleepBlock(2012480 * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(MacDcf.MAC_MODE_DIFS == mac.getMode());

      JistAPI.sleepBlock(2012520 * Constants.MICRO_SECOND - JistAPI.getTime());
      Util.assertion(MacDcf.MAC_MODE_SIDLE == mac.getMode());
    } catch (Throwable e) {
      e.printStackTrace();
      System.exit(1);
    }
  }

}
