package jist.swans.mac;

import jist.swans.misc.Message;
import jist.swans.misc.MessageAnno;

public abstract class MacMessageFactory {

  public abstract MacMessage fromBytes(byte[] msg, int offset) throws Message.IllegalFormatException;

  public abstract int getSize(int type_ack);

  /**
   * Message factory for dcf packets
   *
   * @author kurth
   */
  public static class Dcf extends MacMessageFactory {

    public MacDcfMessage.Data createDataBroadcast(MacAddress dst, MacAddress src,
        int duration, Message body, MessageAnno bodyAnno) {
      return new MacDcfMessage.Data(dst, src, duration, body);
    }

    public MacDcfMessage.Data createDataUnicast(MacAddress dst, MacAddress src,
        int duration, short seqNo, boolean retry, Message body, MessageAnno bodyAnno) {
      return new MacDcfMessage.Data(dst, src, duration, seqNo, retry, body);
    }

    public MacDcfMessage.Ack createAck(MacAddress dst, int duration, Message msg, MessageAnno anno) {
      return new MacDcfMessage.Ack(dst, 0);
    }

    public MacMessage fromBytes(byte[] msg, int offset) throws Message.IllegalFormatException {
      // TODO Auto-generated method stub
      return null;
    }

    public Message getPayload(MacMessage msg) {
      return ((MacDcfMessage.Data)msg).getBody();
    }

    public int getSize(int type) {
      switch (type) {
      case MacDcfMessage.TYPE_DATA:
        return MacDcfMessage.Data.getHeaderSize();
      case MacDcfMessage.TYPE_ACK:
        return MacDcfMessage.Ack.getHeaderSize();
      }
      throw new RuntimeException("unknown type");
    }
  }


  /**
   * Message factory for 802.11 packets
   *
   * @author kurth
   */
  public static class M802_11 extends Dcf {

    /**
     * Creates a rts packet
     *
     * @param dst destination mac address
     * @param src source mac address
     * @param duration duration of the packet exchange
     * @param packet the data packet to protect via rts/cts
     * @param anno annotations for the data packet
     * @return a newly created rts packet
     */
    public Mac802_11Message.Rts createRts(MacAddress dst, MacAddress src,
        int duration, Message packet, MessageAnno anno) {
      return new Mac802_11Message.Rts(dst, src, duration); // rts
    }

    public Mac802_11Message.Cts createCts(MacAddress dst, int duration,
        Message msg, MessageAnno anno) {
      return new Mac802_11Message.Cts(dst, duration);
    }
    public MacDcfMessage.Data createDataBroadcast(MacAddress dst, MacAddress src,
        int duration, Message body, MessageAnno bodyAnno) {
      return new Mac802_11Message.Data(dst, src, duration, body);
    }

    public MacDcfMessage.Data createData(MacAddress dst, MacAddress src,
        int duration, short seqNo, boolean retry, Message body, MessageAnno bodyAnno) {
      return new Mac802_11Message.Data(dst, src, duration, seqNo,
          (short)0, false, retry, body);
    }

    public int getSize(int type) {
      switch (type) {
      case MacDcfMessage.TYPE_DATA:
        return Mac802_11Message.Data.getHeaderSize();
      case MacDcfMessage.TYPE_ACK:
        return Mac802_11Message.Ack.getHeaderSize();
      case Mac802_11Message.TYPE_RTS:
        return Mac802_11Message.Rts.getHeaderSize();
      case Mac802_11Message.TYPE_CTS:
        return Mac802_11Message.Cts.getHeaderSize();
      }
      return super.getSize(type);
    }

    public int getSize(int type, Message packet, MessageAnno anno) {
      return getSize(type);
    }

  }
  
  /**
   * Message factory for 802.11e packets
   *
   * @author beilke
   */
  public static class M802_11e extends M802_11 {

    public Mac802_11eMessage.QOS_Data createQOSDataUnicast(MacAddress dst, MacAddress src,
        int duration, short seqNo, boolean retry, short qosCtl, Message body, MessageAnno bodyAnno) {
      return new Mac802_11eMessage.QOS_Data(dst, src, duration, seqNo, (short)0, false, retry, body, qosCtl);
    }
    
    public Mac802_11eMessage.QOS_Data createQOSDataBroadcast(MacAddress dst, MacAddress src,
        int duration, short qosCtl, Message body, MessageAnno bodyAnno) {
      return new Mac802_11eMessage.QOS_Data(dst, src, duration, (short) -1, (short)0, false, false, body, qosCtl);
    }
    
    public Mac802_11eMessage.QOS_Null createQOSNull(MacAddress dst, MacAddress src,
        int duration, short seqNo, boolean retry, short qosCtl) {//, Message body, MessageAnno bodyAnno) {
      return new Mac802_11eMessage.QOS_Null(dst, src, duration, seqNo, (short)0, false, retry, qosCtl);
    }
    
    public Mac802_11eMessage.QOS_BlockAckReq createQOSBlockAckReq(MacAddress dst, MacAddress src,
        int duration, short barControl, short barSeqControl) {
      return new Mac802_11eMessage.QOS_BlockAckReq(dst, src, duration, barControl, barSeqControl);
    }
    
    public Mac802_11eMessage.QOS_BlockAckResp createQOSBlockAckResp(MacAddress dst, MacAddress src,
        int duration, short barControl, short barSeqControl, short[] blockAckBitmap) {
      return new Mac802_11eMessage.QOS_BlockAckResp(dst, src, duration, barControl, barSeqControl, blockAckBitmap);
    }
    
    public int getSize(int type, int subType) {
      switch (type) {
      case Mac802_11eMessage.WIFI_FC0_TYPE_DATA:
        switch (subType) {
        case Mac802_11eMessage.WIFI_FC0_SUBTYPE_DATA:
          return Mac802_11eMessage.QOS_Data.getHeaderSize();
        case Mac802_11eMessage.WIFI_FC0_SUBTYPE_QOS_NULL:
          return Mac802_11eMessage.QOS_Null.getHeaderSize();
        }
      case Mac802_11eMessage.WIFI_FC0_TYPE_CONTROL:
        switch (subType) {
        case Mac802_11eMessage.WIFI_FC0_SUBTYPE_BLOCKACK_REQ:
          return Mac802_11eMessage.QOS_BlockAckReq.getHeaderSize();
        case Mac802_11eMessage.WIFI_FC0_SUBTYPE_BLOCKACK_RESP:
          return Mac802_11eMessage.QOS_BlockAckResp.getHeaderSize();
        }
      }
      return super.getSize(type);
    }
  }

}
