package brn.swans.mac;

import java.util.List;

import jist.runtime.Main;
import jist.runtime.Util;
import jist.swans.mac.Mac802_11Message;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacDcfMessage;
import jist.swans.mac.MacMessageFactory;
import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;
import jist.swans.net.NetAddress;
import jist.swans.net.NetMessage;

/**
 * Message factory for txdor packets
 *
 * @author kurth
 */
public class MacTxDORMessageFactory extends MacMessageFactory.M802_11 {

  protected MacTxDOR mac;

  /**
   * @param mac the mac to set
   */
  public void setMac(MacTxDOR mac) {
    this.mac = mac;
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.M802_11#getSize(int)
   */
  public int getSize(int type) {
    switch (type) {
    case MacDcfMessage.TYPE_DATA:
      return MacTxDORMessage.Data.getHeaderSize();
    case MacDcfMessage.TYPE_ACK:
      return MacTxDORMessage.Ack.getHeaderSize();
    case Mac802_11Message.TYPE_RTS:
      return MacTxDORMessage.Rts.getHeaderSize();
    case Mac802_11Message.TYPE_CTS:
      return MacTxDORMessage.Cts.getHeaderSize();
    }
    return super.getSize(type);
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.M802_11#getSize(int, jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public int getSize(int type, Message packet, MessageAnno anno) {
    List candidates = (List) anno.get(MessageAnno.ANNO_MAC_CANDIDATES);
    if (Main.ASSERT)
      Util.assertion(null != candidates);
    switch (type) {
    case MacDcfMessage.TYPE_DATA:
      return MacTxDORMessage.Data.getHeaderSize(candidates.size());
    case Mac802_11Message.TYPE_RTS:
      return MacTxDORMessage.Rts.getHeaderSize(candidates.size());
    case Mac802_11Message.TYPE_CTS:
      return MacTxDORMessage.Cts.getHeaderSize(candidates.size());
    }
    return super.getSize(type, packet, anno);
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.M802_11#createDataUnicast(jist.swans.mac.MacAddress, jist.swans.mac.MacAddress, int, short, boolean, jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public MacDcfMessage.Data createDataUnicast(MacAddress dst, MacAddress src,
      int duration, short seqNo, boolean retry, Message body, MessageAnno bodyAnno) {

    if (dst == null || MacTxDOR.ADDR_ANYCAST.equals(dst)) {
      MacAddress source = (MacAddress) bodyAnno.get(MessageAnno.ANNO_MAC_SOURCE);
      src = (null != source ? source : src);
      Short seqNoObj = (Short) bodyAnno.get(MessageAnno.ANNO_MAC_SEQNO);
      seqNo = (null != seqNoObj ? seqNoObj.shortValue() : seqNo);
      Boolean retryObj = (Boolean) bodyAnno.get(MessageAnno.ANNO_MAC_RETRY);
      retry = (null != retryObj ? retryObj.booleanValue() : retry);
      List lstCandidates = (List) bodyAnno.get(MessageAnno.ANNO_MAC_CANDIDATES);
      Byte idxFinalDst = (Byte) bodyAnno.get(MessageAnno.ANNO_MAC_FINALDST_IDX);
      if (Main.ASSERT)
        Util.assertion(idxFinalDst.intValue() < lstCandidates.size());

      if (Main.ASSERT)
        Util.assertion(!lstCandidates.contains(src));
      return new MacTxDORMessage.Data(lstCandidates, src,
          duration, seqNo, retry, idxFinalDst.byteValue(), body);
    }

    return super.createDataUnicast(dst, src, duration, seqNo, retry, body, bodyAnno);
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.Dcf#createAck(jist.swans.mac.MacAddress, int, jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public MacDcfMessage.Ack createAck(MacAddress dst, int duration, Message msg, MessageAnno anno) {
//    NetMessage.Ip ipMsg = (NetMessage.Ip)((MacMessage.HasBody)msg).getBody();
    NetAddress src = (NetAddress) anno.get(MessageAnno.ANNO_MAC_NETSRC);
    Short netId = (Short) anno.get(MessageAnno.ANNO_MAC_NETID);
    Byte netTTL = (Byte) anno.get(MessageAnno.ANNO_MAC_NETTTL);
    // only the final destination sends the ack
    Byte idxFinalDst = (Byte) anno.get(MessageAnno.ANNO_MAC_FINALDST_IDX);

    return new MacTxDORMessage.Ack(dst, 0, src, netId.shortValue(),
        netTTL.byteValue(), idxFinalDst.intValue() >= 0 ? true : false);
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.M802_11#createRts(jist.swans.mac.MacAddress, jist.swans.mac.MacAddress, int, jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public Mac802_11Message.Rts createRts(MacAddress dst, MacAddress src,
      int duration, Message msg, MessageAnno anno) {
    if (dst == null || MacTxDOR.ADDR_ANYCAST.equals(dst)) {
      List lstCandidates = (List) anno.get(MessageAnno.ANNO_MAC_CANDIDATES);
      Byte idxFinalDst = (Byte) anno.get(MessageAnno.ANNO_MAC_FINALDST_IDX);
      if (Main.ASSERT)
        Util.assertion(idxFinalDst.intValue() < lstCandidates.size());
      NetAddress netSrc = ((NetMessage.Ip)msg).getSrc();
      short netId = ((NetMessage.Ip)msg).getId();
      byte netTTL = ((NetMessage.Ip)msg).getTTL();
      boolean retry = mac.getRetry() > 0;
      short seqNo = (short)((mac.getSeq() + (retry ? 0 : 1))% MacDcfMessage.Data.MAX_SEQ);
      if (Main.ASSERT)
        Util.assertion(!lstCandidates.contains(src));

      return new MacTxDORMessage.Rts(lstCandidates, src, duration,
          netSrc, netId, netTTL, seqNo, retry, idxFinalDst.byteValue());
    }
    return super.createRts(dst, src, duration, msg, anno);
  }

  /*
   * (non-Javadoc)
   * @see jist.swans.mac.MacMessageFactory.M802_11#createCts(jist.swans.mac.MacAddress, int, jist.swans.misc.Message, jist.swans.misc.MessageAnno)
   */
  public Mac802_11Message.Cts createCts(MacAddress dst, int duration,
      Message msg, MessageAnno anno) {

    // try to extract candidate set from rts packet
    List lstCandidates = (List) anno.get(MessageAnno.ANNO_MAC_CANDIDATES);
    NetAddress src = (NetAddress) anno.get(MessageAnno.ANNO_MAC_NETSRC);
    Short netId = (Short) anno.get(MessageAnno.ANNO_MAC_NETID);
    Byte netTTL = (Byte) anno.get(MessageAnno.ANNO_MAC_NETTTL);
    Short seqNo = (Short) anno.get(MessageAnno.ANNO_MAC_SEQNO);
    Boolean retry = (Boolean) anno.get(MessageAnno.ANNO_MAC_RETRY);
    if (Main.ASSERT)
      Util.assertion(!lstCandidates.contains(dst));
    Byte idxFinalDst = (Byte) anno.get(MessageAnno.ANNO_MAC_FINALDST_IDX);
    if (Main.ASSERT)
      Util.assertion(idxFinalDst.intValue() < lstCandidates.size());

    return new MacTxDORMessage.Cts(dst, lstCandidates, duration, src,
        netId.shortValue(), netTTL.byteValue(), seqNo.shortValue(),
        retry.booleanValue(), idxFinalDst.byteValue());
  }

}
