package brn.sim.builder;

import test.sim.scenario.mac.NetIpMacTest;
import jist.swans.Constants;
import jist.swans.Node;
import jist.swans.misc.Mapper;
import jist.swans.misc.Util;
import jist.swans.net.AbstractNet;
import jist.swans.net.NetAddress;
import jist.swans.net.NetIp;
import jist.swans.net.PacketLoss;
import brn.swans.net.NetIpNotify;
import brn.swans.net.NetQoS;
import brn.swans.net.NetTxDOR;

public abstract class NetBuilder extends Builder {

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

    /** Whether to be in promisc mode */
    public boolean netPromisc = false;
    /** protocols registered at the mapper */
    public int[] protocolMapper = new int[] { Constants.NET_PROTOCOL_UDP, Constants.NET_PROTOCOL_TCP };

    /** Packet loss model. */
    public int loss = Constants.NET_LOSS_NONE;
    /** Packet loss options. */
    public String lossOpts = "";

    /** options for bit error model */
	public String bitErrorOpts = "";

    public int getLoss() {
      return loss;
    }
    public void setLoss(int loss) {
      this.loss = loss;
    }
    public String getLossOpts() {
      return lossOpts;
    }
    public void setLossOpts(String lossOpts) {
      this.lossOpts = lossOpts;
    }
	public void setBitErrorOpts(String bitErrorOpts) {
		this.bitErrorOpts = bitErrorOpts;
	}
	public String getBitErrorOpts() {
		return bitErrorOpts;
	}
	public boolean isNetPromisc() {
      return netPromisc;
    }
    public void setNetPromisc(boolean netPromisc) {
      this.netPromisc = netPromisc;
    }
    public int[] getProtocolMapper() {
      return protocolMapper;
    }
    public void setProtocolMapper(int[] protocolMapper) {
      this.protocolMapper = protocolMapper;
    }
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder.Params#clone()
     */
    public Object clone() throws CloneNotSupportedException {
      IpParams ret = (IpParams) super.clone();
      if (null != protocolMapper) ret.protocolMapper = (int[]) protocolMapper.clone();
      return ret;
    }
  }

  public static class IpNotifyParams extends IpParams {
    private static final long serialVersionUID = 1L;
  }

  public static class TxDORParams extends IpNotifyParams {
    private static final long serialVersionUID = 1L;
  }
  
  public static class QoSParams extends IpParams {
    private static final long serialVersionUID = 1L;
  }

  public static class IpMacTestParams extends IpParams {
    private static final long serialVersionUID = 1L;
  }


  protected PacketLoss createLoss(IpParams opts) {
    PacketLoss inLoss = null;
    switch (opts.loss) {
    case Constants.NET_LOSS_NONE:
      inLoss = new PacketLoss.Zero();
      break;
    case Constants.NET_LOSS_UNIFORM:
      inLoss = new PacketLoss.Uniform(Double.parseDouble(opts.lossOpts));
      break;
    default:
      throw new RuntimeException("unknown packet loss model");
    }
    return inLoss;
  }

  /**
   * Ip network object builder;
   *
   * @author kurth
   */
  public static class Ip extends NetBuilder {

    public Class getParamClass() {
      return IpParams.class;
    }

    public Object build(Params params, Node node) throws BuilderException {
      IpParams opts = (IpParams) params;

      // initialize shared protocol mapper
      Mapper protMap = new Mapper(opts.protocolMapper);

      // initialize packet loss models
      PacketLoss outLoss = new PacketLoss.Zero();
      PacketLoss inLoss = createLoss(opts);

      NetAddress address = new NetAddress(node.getNodeId());
      AbstractNet net = new NetIp(address, protMap, inLoss, outLoss);
      // TODO
      // net.setPromiscuous(opts.netPromisc);
      Util.assertion(false == opts.netPromisc);

      return net;
    }

    public void hookUp(Params params, Node node, Object entity) throws BuilderException {
    }
  }
  
  /**
   * Ip network object builder;
   *
   * @author kurth
   */
  public static class QoS extends NetBuilder {

    public Class getParamClass() {
      return QoSParams.class;
    }

    public Object build(Params params, Node node) throws BuilderException {
      QoSParams opts = (QoSParams) params;

      // initialize shared protocol mapper
      Mapper protMap = new Mapper(opts.protocolMapper);

      // initialize packet loss models
      PacketLoss outLoss = new PacketLoss.Zero();
      PacketLoss inLoss = createLoss(opts);

      NetAddress address = new NetAddress(node.getNodeId());
      AbstractNet net = new NetQoS(address, protMap, inLoss, outLoss);
      // TODO
      // net.setPromiscuous(opts.netPromisc);
      Util.assertion(false == opts.netPromisc);

      return net;
    }

    public void hookUp(Params params, Node node, Object entity) throws BuilderException {
    }
  }

  public static class IpNotify extends NetBuilder {

    public Class getParamClass() {
      return IpNotifyParams.class;
    }

    public Object build(Params params, Node node) throws BuilderException {
      IpParams opts = (IpParams) params;

      // initialize shared protocol mapper
      Mapper protMap = new Mapper(opts.protocolMapper);

      // initialize packet loss models
      PacketLoss outLoss = new PacketLoss.Zero();
      PacketLoss inLoss = createLoss(opts);

      NetAddress address = new NetAddress(node.getNodeId());
      AbstractNet net = new NetIpNotify(address, protMap, inLoss, outLoss);
      // TODO
      // net.setPromiscuous(opts.netPromisc);
      Util.assertion(false == opts.netPromisc);

      return net;
    }

    public void hookUp(Params params, Node node, Object entity) throws BuilderException {
    }
  }

  public static class TxDOR extends IpNotify {

    public Class getParamClass() {
      return TxDORParams.class;
    }

    public Object build(Params params, Node node) throws BuilderException {
      TxDORParams opts = (TxDORParams) params;

      // initialize shared protocol mapper
      Mapper protMap = new Mapper(opts.protocolMapper);

      // initialize packet loss models
      PacketLoss outLoss = new PacketLoss.Zero();
      PacketLoss inLoss = createLoss(opts);

      NetAddress address = new NetAddress(node.getNodeId());
      AbstractNet net = new NetTxDOR(address, protMap, inLoss, outLoss);
      // TODO
      // net.setPromiscuous(opts.netPromisc);
      Util.assertion(false == opts.netPromisc);

      return net;
    }

    public void hookUp(Params params, Node node, Object entity) throws BuilderException {
    }
  }

  
  /**
   * Ip network object builder for MAC test;
   *
   * @author ofriedri
   */
  public static class IpMacTest extends NetBuilder {

    public Class getParamClass() {
      return IpMacTestParams.class;
    }

    public Object build(Params params, Node node) throws BuilderException {
      IpParams opts = (IpParams) params;

      NetAddress address = new NetAddress(node.getNodeId());
      AbstractNet net = new NetIpMacTest(address);
      // TODO
      // net.setPromiscuous(opts.netPromisc);
      Util.assertion(false == opts.netPromisc);

      return net;
    }

    public void hookUp(Params params, Node node, Object entity) throws BuilderException {
    }
  }
}
