package jist.swans.phy;

import jist.swans.Constants;

/**
 * The abstract base class for the different PLCP headers (values dependent on
 * the PHY specification used).
 * 
 * @author Oliver
 * 
 */
public abstract class PlcpHeader
{
//  /** Representation of the PHY layer with radio information and timing logic. */
//  protected Phy802_11 phy;

  /** Transmission rate of the plcp header */
  protected int       plcpRate;

  /** Transmission rate of the payload (units: Mbit/s). */
  protected int       payloadRate;
  protected Integer   payloadRateObj;

  /** Number of octets of the payload. */
  protected int       payloadLength;

  /** Duration of PLCP header (depends on PHY and rate). */
  protected long      headerDuration;

  /** Duration of PLCP payload (depends on PHY and rate). */
  protected long      payloadDuration;

  /** Duration of PLCP preamble (depends on PHY and rate). */
  protected long      preambleDuration;
  
  /**
   * Base constructor for a PLCP header object.
   * 
   * @param phy
   *          The PHY layer with radio information and timing logic.
   * @param payloadRate
   *          The bitrate for the PSDU transmission (in bits/s).
   * @param dataLength
   *          The number of octets in the PSDU.
   */
  private PlcpHeader(Phy802_11 phy, int payloadRate, int dataLength)
  {
//    this.phy = phy;
    this.payloadRate = payloadRate;
    this.payloadRateObj = Integer.valueOf(payloadRate);
    this.payloadLength = dataLength;
    this.plcpRate = phy.getPlcpHeaderRate(payloadRate);
  }

  /** @return Transmission rate of the payload (units: Mbit/s). */
  public int getPlcpRate()
  {
    return plcpRate;
  }

  /** @return Transmission rate of the payload (units: Mbit/s). */
  public int getDataRate()
  {
    return payloadRate;
  }

  /** @return Number of octets of the payload. */
  public int getDataLength()
  {
    return payloadLength;
  }

  /** @return Duration of PLCP header in us (depends on PHY and rate). */
  public long getHeaderDuration()
  {
    return headerDuration;
  }

  /** 
   * @return Duration of gross PLCP payload in us (depends on PHY and rate). 
   * This includes the various non-data parts of the payload. 
   */
  public long getPayloadDuration()
  {
    return payloadDuration;
  }
  
  /**
   * "virtual" time needed to transmit the actual data in the payload.
   * This doesn't include service bits, tail and padding
   */
  public long getDataDuration() {
    return payloadLength * 8 * Constants.SECOND / payloadRate;
  }
  
  /**
   * "virtual" time needed to transmit the service bits
   * @return dead time at the front of the payload
   */
  public abstract long getServiceBitsDuration();
  
  /**
   * "virtual" time needed for the "tail" bits at the end of the payload
   */
  public abstract long getTailDuration();
  
  /**
   * "virtual" time needed for the padding at the end of the payload
   */
  public long getPadDuration() {
    return payloadDuration - getTailDuration() - getServiceBitsDuration() - getDataDuration();
  }
  
 

  /** @return Duration of PLCP preamble in us (depends on PHY and rate). */
  public long getPreambleDuration()
  {
    return preambleDuration;
  }
  
  /**
   * @return The symbol transmission time for this message.
   */
  public abstract long getSymbolDuration();

  public abstract long getCyclicPrefix();

  public int hashCode() {
    final int PRIME = 31;
    int result = 1;
    result = PRIME * result + payloadLength;
    result = PRIME * result + payloadRate;
    result = PRIME * result + (int) (headerDuration ^ (headerDuration >>> 32));
    result = PRIME * result + (int) (payloadDuration ^ (payloadDuration >>> 32));
//    result = PRIME * result + (pbcc ? 1231 : 1237);
    result = PRIME * result + (int) (preambleDuration ^ (preambleDuration >>> 32));
    return result;
  }

  public boolean equals(Object obj) {
    if (this == obj)
      return true;
    if (obj == null)
      return false;
    if (getClass() != obj.getClass())
      return false;
    final PlcpHeader other = (PlcpHeader) obj;
    if (payloadLength != other.payloadLength)
      return false;
    if (payloadRate != other.payloadRate)
      return false;
    if (headerDuration != other.headerDuration)
      return false;
    if (payloadDuration != other.payloadDuration)
      return false;
//    if (pbcc != other.pbcc)
//      return false;
    if (preambleDuration != other.preambleDuration)
      return false;
    return true;
  }

  /**
   * DSSS PLCP header
   */
  public static class DSSS extends PlcpHeader
  {
//    /**
//     * True if and only if the modulation used is PBCC. Only applicable for
//     * 802.11b and g stations.
//     */
//    protected boolean   pbcc = false;

    public DSSS(Phy802_11 phy, int dataRate, int dataLength)
    {
      this(phy, dataRate, dataLength, false);
    }
    
    public DSSS(Phy802_11 phy, int dataRate, int dataLength, boolean pbcc) 
    {
      super(phy, dataRate, dataLength);
      preambleDuration = phy.preambleDuration(dataRate);
      headerDuration = phy.headerDuration(dataRate);
      payloadDuration = phy.payloadDuration(dataLength, dataRate, pbcc);
      
    }

//    /** Set the modulation to PBCC. The default is CCK. */
//    public void setPbcc(boolean pbccOn)
//    {
//      super.pbcc = pbccOn;
//      payloadDuration = phy.payloadDuration(dataLength, dataRate, pbcc);
//    }

    /*
     * (non-Javadoc)
     * @see jist.swans.phy.PlcpHeader#getSymbolDuration()
     */
    public long getSymbolDuration()
    {
      if (Constants.BANDWIDTH_1Mbps == payloadRate
          || Constants.BANDWIDTH_2Mbps == payloadRate)
        return Constants.MICRO_SECOND;
      else if (Constants.BANDWIDTH_5_5Mbps == payloadRate
          || Constants.BANDWIDTH_11Mbps == payloadRate)
        return 8 / 11 * Constants.MICRO_SECOND;
      throw new RuntimeException("Invalid bit rate");
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.phy.PlcpHeader#getCyclicPrefix()
     */
    public long getCyclicPrefix() 
    {
      throw new RuntimeException("Invalid bit rate");
    }

    @Override
    public long getServiceBitsDuration() {
      return 0;
    }

    @Override
    public long getTailDuration() {
      return 0;
    }

  } // DSSS

  /**
   * OFDM PLCP Header
   */
  public static class OFDM extends PlcpHeader
  {
    public OFDM(Phy802_11 phy, int dataRate, int dataLength)
    {
      super(phy, dataRate, dataLength);
      preambleDuration = phy.preambleDuration(dataRate);
      headerDuration = phy.headerDuration(dataRate);
      payloadDuration = phy.payloadDuration(dataLength, dataRate);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.phy.PlcpHeader#getSymbolDuration()
     */
    public long getSymbolDuration()
    {
      return Constants.SYMBOL_TX_TIME_OFDM;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.phy.PlcpHeader#getCyclicPrefix()
     */
    public long getCyclicPrefix() 
    {
      return Constants.CYCLIC_PREFIX_OFDM;
    }

    /**
     * 16 service bits for OFDM 
     */
    public long getServiceBitsDuration() {
      return 16 * Constants.SECOND / payloadRate;
    }

    @Override
    public long getTailDuration() {
      return 6 * Constants.SECOND / payloadRate;
    }
  } // OFDM
}
