package test.sim.scenario.mac;

import org.apache.log4j.Logger;

import jist.swans.Constants;
import jist.swans.misc.Util;

/**
 * Separate class to return the expected total tx time for a packet tx 
 * 
 * @author Oliver
 *
 */
public class MacTransmissionTimes
{
  private static final Logger log = Logger.getLogger(MacTransmissionTimes.class);
  boolean longHeaders;
  boolean minimalBasicRates;
  boolean shortDifs;
  int     rtsThreshold;

  public MacTransmissionTimes(boolean longHeaders, boolean minimalBasicRates,
                              boolean shortDifs, int rtsThreshold)
  {
    this.longHeaders = longHeaders;
    this.minimalBasicRates = minimalBasicRates;
    this.shortDifs = shortDifs;
    this.rtsThreshold = rtsThreshold;
  }

  public long get(int dataRate, int msgSize)
  {
    if (Util.isInArray(Constants.BITRATES_DSSS, dataRate) >= 0)
      return getB(dataRate, msgSize);
    if (Util.isInArray(Constants.BITRATES_OFDM, dataRate) >= 0)
      return getG(dataRate, msgSize);
    throw new RuntimeException("BitrateTest: data rate '" + dataRate
        + "' not (yet) supported");
  }

  private long getB(int dataRate, int msgSize)
  {
    // DIFS
    long difs = 50;
    // SIFS
    long sifs = 10;
    // Preamble + PLCP header
    long plcp = 0;
    if (dataRate == Constants.BANDWIDTH_1Mbps || longHeaders)
      plcp += 144 + 48;
    else plcp += 72 + 24;

    long rts = 0;
    long cts = 0;
    long data = 0;
    long ack = 0;
    long tt = 0;

    /* RTS + CTS */
    if (msgSize > rtsThreshold) {
      /* RTS */
      rts = difs
      + plcp
      // MAC headers + payload
      + (long) Math.ceil(((float) 20 * 8)
          / ((float) dataRate / (float) Constants.BANDWIDTH_1Mbps));

      /* CTS */
      cts = sifs
      + plcp
      // MAC headers + payload
      + (long) Math.ceil(((float) 14 * 8)
          / ((float) dataRate / (float) Constants.BANDWIDTH_1Mbps));

//    log.debug("rts:" + rts);
//    log.debug("cts:" + cts);
      tt += rts + cts;
    }

    /* DATA */
    data = (rts > 0 ? sifs : difs)
    + plcp
    // LLC + MAC headers + payload
    + (long) Math.ceil(((float) (8 + 28 + msgSize) * 8.)
        / ((float) dataRate / (float) Constants.BANDWIDTH_1Mbps));

    /* ACK */
    // SIFS
    ack = sifs
    + plcp
    // MAC headers + payload
    + (long) Math.ceil(((float) 14 * 8)
        / ((float) dataRate / (float) Constants.BANDWIDTH_1Mbps));

//  log.debug("dat:" + data);
//  log.debug("ack:" + ack);

    tt += data + ack;
    if (log.isDebugEnabled())
      log.debug("len:" + msgSize + " rate:" + ((float) dataRate / (float)
          Constants.BANDWIDTH_1Mbps) + " rts:" + rts + " cts:" + cts +
          " data:" + data + " ack:" + ack + " tot:" + tt);
    return tt * Constants.MICRO_SECOND;
  }

  private long getG(int dataRate, int msgSize)
  {
    // DIFS
    long difs = shortDifs ? 28 : 50;
    // SIFS
    long sifs = 10;
    // Preamble + PLCP header
    long plcp = 16 + 4;
    // signal extension
    long sign = 6;

    long rts = 0;
    long cts = 0;
    long data = 0;
    long ack = 0;
    long tt = 0;

    /* RTS + CTS */
    if (msgSize > rtsThreshold) {
      /* RTS */
      rts = difs
      + plcp
      // MAC headers + payload
      + (long) (Math.ceil((float)(16 + 20 * 8 + 6)
          / ((float) dataRate * 4 / Constants.BANDWIDTH_1Mbps))) * 4
          + sign;

      /* CTS */
      cts = sifs
      + plcp
      // MAC headers + payload
      + (long) (Math.ceil((float)(16 + 14 * 8 + 6)
          / ((float) dataRate * 4 / Constants.BANDWIDTH_1Mbps))) * 4
          + sign;

//    log.debug("rts:" + rts);
//    log.debug("cts:" + cts);
      tt += rts + cts;
    }

    /* DATA */
    data = (rts > 0 ? sifs : difs)
    + plcp
    // LLC + MAC headers + payload
    + (long) (Math.ceil((float)(16 + (8 + 28 + msgSize) * 8 + 6)
        / ((float) dataRate * 4 / Constants.BANDWIDTH_1Mbps))) * 4
        + sign;

    /* ACK */
    // SIFS
    ack = sifs
    + plcp
    // MAC headers + payload
    + (long) (Math.ceil((float)(16 + 14 * 8 + 6)
        / ((float) dataRate * 4 / Constants.BANDWIDTH_1Mbps))) * 4
        + sign;

//  log.debug("dat:" + data);
//  log.debug("ack:" + ack);

    tt += data + ack;
    if (log.isDebugEnabled())
      log.debug("len:" + msgSize + " rate:" + ((float) dataRate / (float)
          Constants.BANDWIDTH_1Mbps) + " rts:" + rts + " cts:" + cts +
          " data:" + data + " ack:" + ack + " tot:" + tt);
    return tt * Constants.MICRO_SECOND;
  }
} // class TransmissionTimes 
