package test.brn.scenario.handoff;

import jist.swans.Node;
import jist.swans.field.Field;
import jist.swans.misc.Util;
import jist.swans.net.NetAddress;
import jist.swans.trans.AbstractTrans;
import jist.swans.trans.TcpSocket;
import test.TestDriver;
import brn.sim.AbstractParams;
import brn.sim.builder.Builder;
import brn.sim.builder.BuilderException;
import brn.swans.app.TcpApplication;
import brn.swans.app.rtp.RtpApplication;
import brn.swans.app.rtp.RtpProfile;
import click.runtime.ClickException;
import click.swans.net.ClickRouter;

/**
 * Scenario with a gateway and randomized placed mesh nodes. the clients
 * communicate with the gateway.
 *
 * @author kurth
 *
 */
public class HandoffTest extends TestDriver {

  /*
   * (non-Javadoc)
   * @see brn.sim.AbstractDriver#buildAdditionNodes(brn.sim.AbstractParams, int, jist.swans.field.Field)
   */
  protected void buildAdditionNodes(AbstractParams params, int nextNodeId,
      Field field) throws BuilderException {
    HandoffParams opts = (HandoffParams) params;

    for (int i = 0; i < opts.clients; i++) {
      int nodeId = i + nextNodeId;
      Node node = buildNode(opts.client, nodeId, field);
      nodes.add(node);

      // trans + app
      setupApplication(opts, i, node);
    }
  }

  protected void postHookUp(AbstractParams params) throws BuilderException {
    super.postHookUp(params);

    HandoffParams opts = (HandoffParams) params;

    for (int i = 1; i <= params.nodes; i++) {
      Node node = (Node) nodes.get(i);

      // set optimize
      ClickRouter click = (ClickRouter) node.getNet();
      try {
//          click.writeHandler("MeshNode/brn_iapp/optimize", "optimize",
//              ((HandoffParams)params).optimize ? "true" : "false");
      click.writeHandler("meshNode/brn_iapp/optimize", "optimize",
          ((HandoffParams)params).optimize ? "true" : "false");

        click.writeHandler("meshNode/access_point/assoc_resp", "response_delay",
            Integer.toString(opts.reassocDelay_ms));
      } catch (ClickException e) {
        throw new BuilderException(e);
      }
    }
  }

  /*
   * (non-Javadoc)
   * @see brn.sim.AbstractDriver#setupApplication(brn.sim.AbstractParams, int, jist.swans.Node)
   */
  protected void setupApplication(AbstractParams params, int nodeId, Node node)
      throws BuilderException {
    HandoffParams opts = (HandoffParams) params;
//    AbstractNet net = node.getNet();

    boolean isClient = nodeId > params.nodes;
    boolean isServer = nodeId <= opts.clients;

    if (isClient) {
      opts.tcpParams.clientAddr = new NetAddress(nodeId);
      opts.tcpParams.serverAddr = new NetAddress(nodeId - opts.nodes);
    }
    else if (isServer) {
      opts.tcpParams.clientAddr = new NetAddress(nodeId + opts.nodes);
      opts.tcpParams.serverAddr = new NetAddress(nodeId);
    }

    // transport
    Builder builder = builderProvider.getBuilder(opts.transParams);
    AbstractTrans trans = (AbstractTrans) builder.build(opts, node);
    builderProvider.addHookUp(builder, opts.transParams, node, trans);
    node.addTransport(trans);

    // rtp app
    if (opts.rtpParams != null) {
      RtpProfile.setAutoOutputFileNames(opts.rtpParams.autoOutputFileNames);

      builder = builderProvider.getBuilder(opts.rtpParams);
      RtpApplication app = (RtpApplication) builder.build(opts.rtpParams, node);
      if (null != app) {
        builderProvider.addHookUp(builder, opts.rtpParams, node, app);
        node.addApplication(app);
      }
    }

    // tcp app
    if (null != opts.tcpParams) {
      builder = builderProvider.getBuilder(opts.tcpParams);
      TcpApplication app = (TcpApplication) builder.build(opts.tcpParams, node);
      if (null != app) {
        builderProvider.addHookUp(builder, opts.tcpParams, node, app);
        node.addApplication(app);
      }
    }
  }

  public static void main(String[] args) {
    HandoffTest test = new HandoffTest();
    if (args.length < 1) {
      test.showUsage();
      return;
    }

    try {
      final HandoffParams options = (HandoffParams) (Util.readObject(args[0]));

      test.run(options);
    } catch (Exception e) {
      throw new RuntimeException(e);
    }
  }

}
