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

import jist.swans.Node;
import jist.swans.mac.AbstractMac;
import jist.swans.net.AbstractNet;
import jist.swans.radio.AbstractRadio;
import jist.swans.route.AbstractRoute;
import jist.swans.trans.AbstractTrans;

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

  /**
   * TODO rename to BaseParams
   * @author kurth
   *
   */
  public static class NodeParams extends Builder.Params {
    private static final long serialVersionUID = 1L;

    /** radio parameter block */
    public Builder.Params radio = new RadioBuilder.NoiseAdditiveParams();
    /** mac parameter block */
    public Builder.Params mac = new MacBuilder.M802_11Params();
    /** net parameter block */
    public Builder.Params net = new NetBuilder.IpParams();
    /** route parameter block */
    public Builder.Params route = new RouteBuilder.AodvParams();

    public Builder.Params getMac() {
      return mac;
    }
    public void setMac(Builder.Params mac) {
      this.mac = mac;
    }
    public Builder.Params getNet() {
      return net;
    }
    public void setNet(Builder.Params net) {
      this.net = net;
    }
    public Builder.Params getRadio() {
      return radio;
    }
    public void setRadio(Builder.Params radio) {
      this.radio = radio;
    }
    public Builder.Params getRoute() {
      return route;
    }
    public void setRoute(Builder.Params route) {
      this.route = route;
    }
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder.Params#clone()
     */
    public Object clone() throws CloneNotSupportedException {
      NodeParams ret = (NodeParams) super.clone();
      if (null != radio) ret.radio = (Params) radio.clone();
      if (null != mac) ret.mac = (Params) mac.clone();
      if (null != net) ret.net = (Params) net.clone();
      if (null != route) ret.route = (Params) route.clone();
      return ret;
    }
  }


  public static class TrafficParams extends NodeParams {
    private static final long serialVersionUID = 1L;

    /** trans part */
    public Builder.Params trans = new TransBuilder.TcpParams();
    /** app part */
    public Builder.Params traffic = new TrafficBuilder.HorizontalParams();

    public Builder.Params getTrans() {
      return trans;
    }
    public void setTrans(Builder.Params trans) {
      this.trans = trans;
    }
    public Builder.Params getTraffic() {
      return traffic;
    }
    public void setTraffic(Builder.Params traffic) {
      this.traffic = traffic;
    }
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder.Params#clone()
     */
    public Object clone() throws CloneNotSupportedException {
      TrafficParams ret = (TrafficParams) super.clone();
      if (null != trans) ret.trans = (Params) trans.clone();
      if (null != traffic) ret.traffic = (Params) traffic.clone();
      return ret;
    }
  }

  public static class FlowParams extends NodeParams {
    private static final long serialVersionUID = 1L;

    /** trans part */
    public Builder.Params trans = new TransBuilder.UdpParams();
    /** app part */
    public Builder.Params[] flows = null;

    public Builder.Params getTrans() {
      return trans;
    }
    public void setTrans(Builder.Params trans) {
      this.trans = trans;
    }
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder.Params#clone()
     */
    public Object clone() throws CloneNotSupportedException {
      FlowParams ret = (FlowParams) super.clone();
      if (null != trans) ret.trans = (Params) trans.clone();
      if (null != flows) {
        ret.flows = (Params[]) flows.clone();
        for (int i =0; i <flows.length; i++)
          ret.flows[i] = (flows[i] == null ? null : (Params) flows[i].clone());
      }
      return ret;
    }
    public Builder.Params[] getFlows() {
      return flows;
    }
    public void setFlows(Builder.Params[] flows) {
      this.flows = flows;
    }
  }

  public static class AppParams extends NodeParams {
    private static final long serialVersionUID = 1L;

    /** trans part */
    public Builder.Params trans = new TransBuilder.UdpParams();
    /** app part */
    public Builder.Params app = new AppBuilder.UdpNotifyParams();

    public Builder.Params getTrans() {
      return trans;
    }
    public void setTrans(Builder.Params trans) {
      this.trans = trans;
    }
    public Builder.Params getApp() {
      return app;
    }
    public void setApp(Builder.Params app) {
      this.app = app;
    }
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder.Params#clone()
     */
    public Object clone() throws CloneNotSupportedException {
      AppParams ret = (AppParams) super.clone();
      if (null != trans) ret.trans = (Params) trans.clone();
      if (null != app) ret.app = (Params) app.clone();
      return ret;
    }
  }
  
  public static class ExtensibleParams 
      extends NodeBuilder.NodeParams implements Builder.ParamsWithBuilder {
    private static final long serialVersionUID = -540330037197475336L;
    public Class builderClass() {
      return Extensible.class;  
    }

    /** params for link probing */
    public Builder.Params[] extension = null;
    
    
    @Override
    public Object clone() throws CloneNotSupportedException {
      ExtensibleParams ret = (ExtensibleParams) super.clone();
      if (ret.extension != null) {
        ret.extension = extension.clone();
        for (int i = 0; i < ret.extension.length; i++)
          ret.extension[i] = (Params) ret.extension[i].clone();
      }
      return ret;
    }

    public Builder.Params[] getExtension() {
      return extension;
    }
    public void setExtension(Builder.Params[] extension) {
      this.extension = extension;
    }
  }
  
  public static class Base extends NodeBuilder {
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return NodeParams.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 {
      NodeParams opts = (NodeParams) params;

      // radio
      if (opts.radio != null) {
        RadioBuilder radioBuilder = (RadioBuilder) getProvider().getBuilder(opts.radio);
        AbstractRadio radio = (AbstractRadio) radioBuilder.build(opts.radio, node);
        getProvider().addHookUp(radioBuilder, opts.radio, node, radio);
        node.addRadio(radio);
      }

      // mac
      if (opts.mac != null) {
        MacBuilder macBuilder = (MacBuilder) getProvider().getBuilder(opts.mac);
        AbstractMac mac = (AbstractMac) macBuilder.build(opts.mac, node);
        getProvider().addHookUp(macBuilder, opts.mac, node, mac);
        node.addMac(mac);
      }

      // network
      if (opts.net != null) {
        NetBuilder netBuilder = (NetBuilder) getProvider().getBuilder(opts.net);
        AbstractNet net = (AbstractNet) netBuilder.build(opts.net, node);
        getProvider().addHookUp(netBuilder, opts.net, node, net);
        node.setNet(net);
      }

      // routing
      if (opts.route != null) {
        RouteBuilder routeBuilder = (RouteBuilder) getProvider().getBuilder(opts.route);
        AbstractRoute route = (AbstractRoute) routeBuilder.build(opts.route, node);
        getProvider().addHookUp(routeBuilder, opts.route, node, route);
        node.setRoute(route);
      }

      return node;
    }

    /* (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 {
      // TODO Auto-generated method stub

    }
  }

  public static class Traffic extends Base {
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return TrafficParams.class;
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    public Object build(Params opts, Node node) throws BuilderException {
      TrafficParams params = (TrafficParams) opts;
      super.build(params, node);

      //    transport
      if (null != params.trans) {
        Builder builder = getProvider().getBuilder(params.trans);
        AbstractTrans trans = (AbstractTrans) builder.build(params.trans, node);
        getProvider().addHookUp(builder, params.trans, node, trans);
        node.addTransport(trans);
      }

      // app
      if (null != params.traffic) {
        Builder builder = getProvider().getBuilder(params.traffic);
        Object ret = builder.build(params.traffic, node);
        getProvider().addHookUp(builder, params.traffic, node, ret);
      }

      return node;
    }

    /* (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 {
      // TODO Auto-generated method stub

    }
  }

  public static class Flow extends Base {
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return FlowParams.class;
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    public Object build(Params opts, Node node) throws BuilderException {
      FlowParams params = (FlowParams) opts;
      super.build(params, node);

      //    transport
      if (null != params.trans) {
        Builder builder = getProvider().getBuilder(params.trans);
        AbstractTrans trans = (AbstractTrans) builder.build(params.trans, node);
        getProvider().addHookUp(builder, params.trans, node, trans);
        node.addTransport(trans);
      }

      // app
      for (int i = 0; params.flows != null && i < params.flows.length; i++) {
        Builder builder = getProvider().getBuilder(params.flows[i]);
        Object ret = builder.build(params.flows[i], node);
        getProvider().addHookUp(builder, params.flows[i], node, ret);
      }

      return node;
    }

    /* (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 {
      // TODO Auto-generated method stub

    }
  }

  public static class App extends Base {
    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return AppParams.class;
    }

    /* (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    public Object build(Params opts, Node node) throws BuilderException {
      AppParams params = (AppParams) opts;
      super.build(params, node);

      //    transport
      if (null != params.trans) {
        Builder builder = getProvider().getBuilder(params.trans);
        AbstractTrans trans = (AbstractTrans) builder.build(params.trans, node);
        getProvider().addHookUp(builder, params.trans, node, trans);
        node.addTransport(trans);
      }

      // app
      if (null != params.app) {
        Builder builder = getProvider().getBuilder(params.app);
        Object ret = builder.build(params.app, node);
        getProvider().addHookUp(builder, params.app, node, ret);
      }

      return node;
    }

    /* (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 {
      // TODO Auto-generated method stub

    }
  }
  
  public static class Extensible extends NodeBuilder.Base {
    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    @Override
    public Class getParamClass() {
      return ExtensibleParams.class;
    }

    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#build(brn.sim.builder.Builder.Params, jist.swans.Node)
     */
    @Override
    public Object build(Params params, Node node) throws BuilderException {
      Object obj = super.build(params, node);

      ExtensibleParams opts = (ExtensibleParams) params;

      for (int i = 0; i < opts.extension.length; i++) {
        Builder builder = getProvider().getBuilder(opts.extension[i]);
        Object ext = builder.build(opts.extension[i], node);
        getProvider().addHookUp(builder, opts.extension[i], node, ext);
      }
      return obj;
    }

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