/**
 *
 */
package brn.sim.builder;

import jist.swans.Constants;
import jist.swans.Node;
import jist.swans.misc.Util;
import jist.swans.trans.TcpSocket;
import jist.swans.trans.TransTcp;
import jist.swans.trans.TransUdp;

/**
 * @author kurth
 *
 */
public abstract class TransBuilder extends Builder {

  public abstract static class TransParams extends Builder.Params {
    private static final long serialVersionUID = 1L;
  }

  public static class UdpParams extends TransParams {
    private static final long serialVersionUID = 1L;
  }

  public static class TcpParams extends TransParams {
    private static final long serialVersionUID = 1L;

    /** Maximum Segment Size. Number of bytes to transfer in one packet. */
    public int mss = 1460;

    /** Maximum Segment Lifetime. */
    public int msl = 30;

    /** Number of milliseconds to wait before sending an ACK. */
    public long delayedAckTime = 200 * Constants.MILLI_SECOND;

    /** Number of seconds to wait for first retransmission timer. */
    public long retransmitTimeout = 1500 * Constants.MILLI_SECOND;

    /** Number of seconds to wait for second retransmission timer. */
    public long retransmitTimeoutFinal = 64 * Constants.SECOND;

    /**  Number of seconds to wait before sending probe packets. This
     * happens when receiver is advertising zero window. */
    public long persistTime = 60 * Constants.SECOND;

    /** Initial receiver window size. 4096 */
    public short initWindowSize = 32767;

    public int getMss() {
      return mss;
    }
    public void setMss(int mss) {
      this.mss = mss;
    }
    public int getMsl() {
      return msl;
    }
    public void setMsl(int msl) {
      this.msl = msl;
    }
    public long getDelayedAckTime() {
      return delayedAckTime;
    }
    public void setDelayedAckTime(long delayedAckTime) {
      this.delayedAckTime = delayedAckTime;
    }
    public long getRetransmitTimeout() {
      return retransmitTimeout;
    }
    public void setRetransmitTimeout(long retransmitTimeout) {
      this.retransmitTimeout = retransmitTimeout;
    }
    public long getRetransmitTimeoutFinal() {
      return retransmitTimeoutFinal;
    }
    public void setRetransmitTimeoutFinal(long retransmitTimeoutFinal) {
      this.retransmitTimeoutFinal = retransmitTimeoutFinal;
    }
    public long getPersistTime() {
      return persistTime;
    }
    public void setPersistTime(long persistTime) {
      this.persistTime = persistTime;
    }
    public short getInitWindowSize() {
      return initWindowSize;
    }
    public void setInitWindowSize(short initWindowSize) {
      this.initWindowSize = initWindowSize;
    }
  }

  public static class Udp extends TransBuilder {

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return UdpParams.class;
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    public Object build(Params params, Node node) throws BuilderException {
      return new TransUdp();
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#hookUp(brn.sim.builder.Builder.Params, jist.swans.Node, java.lang.Object)
     */
    public void hookUp(Params params, Node node, Object entity)
        throws BuilderException {
      TransUdp udp = (TransUdp) entity;

      // node entity hookup
      node.getNet().setProtocolHandler(Constants.NET_PROTOCOL_UDP, udp.getProxy());
      udp.setNetEntity(node.getNet().getProxy());
    }
  }

  public static class Tcp extends TransBuilder {

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return TcpParams.class;
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    public Object build(Params params, Node node) throws BuilderException {
      return new TransTcp();
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#hookUp(brn.sim.builder.Builder.Params, jist.swans.Node, java.lang.Object)
     */
    public void hookUp(Params opts, Node node, Object entity)
        throws BuilderException {
      TransTcp tcp = (TransTcp) entity;
      TcpParams params = (TcpParams)opts;

      // node entity hookup
      node.getNet().setProtocolHandler(Constants.NET_PROTOCOL_TCP, tcp.getProxy());
      tcp.setNetEntity(node.getNet().getProxy());

      // TODO use a socket factory in order to prevent static initialization

      // Setup tcp, window scaling not implemented
      Util.assertion(params.initWindowSize <= 0xFFFF);
      TcpSocket.MSS = params.mss;
      TcpSocket.MSL = params.msl;
      TcpSocket.DELAYED_ACK_TIME = params.delayedAckTime;
      TcpSocket.RETRANSMIT_TIMEOUT= params.retransmitTimeout;
      TcpSocket.RETRANSMIT_TIMEOUT_FINAL =  params.retransmitTimeoutFinal;
      TcpSocket.PERSIST_TIMER =  params.persistTime;
      TcpSocket.INIT_WINDOW_SIZE = params.initWindowSize;
    }
  }

}
