package brn.sim.builder;

import java.io.IOException;

import jist.swans.Node;
import jist.swans.field.Fading;


/**
 * Abstract base class for all fading builders.
 *
 * @author kurth
 */
public abstract class FadingBuilder extends Builder {

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

  public static class NoneParams extends FadingParams {
    private static final long serialVersionUID = 1L;
  }

  public static class RayleighParams extends FadingParams {
    private static final long serialVersionUID = 1L;
  }

  public static class RicianParams extends FadingParams {
    private static final long serialVersionUID = 1L;

    /**
     * In indoor channels with an unobstructed line-of-sight between transmit and
     * receive antenna the K-factor is between, say, 4 and 12 dB
     *
     * TODO absolute or dB?
     */
    public double K = 5;

    public double getK() {
      return K;
    }
    public void setK(double k) {
      K = k;
    }
  }

  public static class PunnooseRicianParams extends FadingParams {
    private static final long serialVersionUID = 1L;

    /** Maximum velocity of vehicle/objects in environment.
     * Used for computing doppler */
    public double maxVelocity = 2.5;

    /**
     * Ricean K factor (dB!)
     * -Inf dB  = Rayleigh
     * typical values for LOS are 3 - 6 dB
     */
    public double K = 6;

    /** File containing the rician trace */
    public String traceFile = "res/fading/rice_table.txt";

    /** whether to use the same value for forward and backward link */
    public boolean symmetric = true;

    public double getMaxVelocity() {
      return maxVelocity;
    }
    public void setMaxVelocity(double maxVelocity) {
      this.maxVelocity = maxVelocity;
    }
    public double getK() {
      return K;
    }
    public void setK(double k) {
      K = k;
    }
    public String getTraceFile() {
      return traceFile;
    }
    public void setTraceFile(String traceFile) {
      this.traceFile = traceFile;
    }
    public boolean isSymmetric() {
      return symmetric;
    }
    public void setSymmetric(boolean symmetric) {
      this.symmetric = symmetric;
    }
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      long temp;
      temp = Double.doubleToLongBits(K);
      result = prime * result + (int) (temp ^ (temp >>> 32));
      temp = Double.doubleToLongBits(maxVelocity);
      result = prime * result + (int) (temp ^ (temp >>> 32));
      result = prime * result + (symmetric ? 1231 : 1237);
      result = prime * result
          + ((traceFile == null) ? 0 : traceFile.hashCode());
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final PunnooseRicianParams other = (PunnooseRicianParams) obj;
      if (Double.doubleToLongBits(K) != Double.doubleToLongBits(other.K))
        return false;
      if (Double.doubleToLongBits(maxVelocity) != Double
          .doubleToLongBits(other.maxVelocity))
        return false;
      if (symmetric != other.symmetric)
        return false;
      if (traceFile == null) {
        if (other.traceFile != null)
          return false;
      } else if (!traceFile.equals(other.traceFile))
        return false;
      return true;
    }
  }

  public static class JakesParams extends FadingParams {
    private static final long serialVersionUID = 1L;

    /**
     * JakesPropagationNumberOfRaysPerPath - The number of rays to use for
     * compute the fading coeficent for a given path (default is 1)
     */
    public int rays = 1;

    /**
     * JakesPropagationNumberOfOscillatorsPerRay - The number of oscillators to
     * use for compute the coeficent for a given ray of a given path (default is 4)
     */
    public int oscillators = 4;

    /**
     * JakesPropagationDopplerFreq - The doppler frequency in Hz
     * (f_d = v / lambda = v * f / c, the defualt is 0)
     */
    public double dopplerFreq = 0;

    public int getRays() {
      return rays;
    }
    public void setRays(int rays) {
      this.rays = rays;
    }
    public int getOscillators() {
      return oscillators;
    }
    public void setOscillators(int oscillators) {
      this.oscillators = oscillators;
    }
    public double getDopplerFreq() {
      return dopplerFreq;
    }
    public void setDopplerFreq(double dopplerFreq) {
      this.dopplerFreq = dopplerFreq;
    }
    @Override
    public int hashCode() {
      final int prime = 31;
      int result = 1;
      long temp;
      temp = Double.doubleToLongBits(dopplerFreq);
      result = prime * result + (int) (temp ^ (temp >>> 32));
      result = prime * result + oscillators;
      result = prime * result + rays;
      return result;
    }
    @Override
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final JakesParams other = (JakesParams) obj;
      if (Double.doubleToLongBits(dopplerFreq) != Double
          .doubleToLongBits(other.dopplerFreq))
        return false;
      if (oscillators != other.oscillators)
        return false;
      if (rays != other.rays)
        return false;
      return true;
    }
  }

  /**
   * No Fading
   *
   * @author kurth
   */
  public static class None extends FadingBuilder {
    private static final long serialVersionUID = 1L;

    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return NoneParams.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 Fading.None();
    }

    /*
     * (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 {
      // pass..
    }
  }

  /**
   * Rayleigh Fading
   *
   * @author kurth
   */
  public static class Rayleigh extends FadingBuilder {
    private static final long serialVersionUID = 1L;

    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return RayleighParams.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 Fading.Rayleigh();
    }

    /*
     * (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 {
      // pass..
    }
  }

  /**
   * Rician Fading
   *
   * @author kurth
   */
  public static class Rician extends FadingBuilder {
    private static final long serialVersionUID = 1L;

    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return RicianParams.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 {
      RicianParams opts = (RicianParams) params;
      return new Fading.Rician(opts.K);
    }

    /*
     * (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 {
      // pass..
    }
  }

  /**
   * PunnooseRician Fading
   *
   * @author kurth
   */
  public static class PunnooseRician extends FadingBuilder {
    private static final long serialVersionUID = 1L;

    /*
     * (non-Javadoc)
     * @see brn.sim.builder.Builder#getParamClass()
     */
    public Class getParamClass() {
      return PunnooseRicianParams.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 {
      PunnooseRicianParams opts = (PunnooseRicianParams) params;
      try {
        return new Fading.PunnooseRician(opts.maxVelocity,
            opts.K, opts.traceFile, opts.symmetric);
      } catch (IOException e) {
        throw new BuilderException(e);
      }
    }

    /*
     * (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 {
      // pass..
    }
  }


  /**
   * Propagation Model from http://www.dei.unipd.it/wdyn/?IDsezione=5529
   *
   * @author kurth
   */
  public static class Jakes extends FadingBuilder {

    @Override
    public Class getParamClass() {
      return JakesParams.class;
    }

    @Override
    public Object build(Params opts, Node node) throws BuilderException {
      JakesParams params = (JakesParams) opts;
      return new Fading.Jakes(params.rays, params.oscillators, params.dopplerFreq);
    }

    @Override
    public void hookUp(Params params, Node node, Object entity)
        throws BuilderException {
      // pass...
    }

  }

}
