package jist.swans.mac;

import jist.swans.misc.Message;
import jist.swans.misc.Pickle;

public interface Mac802_11Message extends MacDcfMessage {

  public static final int TYPE_RTS = 3;

  public static final int TYPE_CTS = 4;


  // ////////////////////////////////////////////////
  // RTS frame: (size = 20)
  // frame control size: 2
  // duration size: 2
  // address: destination size: 6
  // address: source size: 6
  // CRC size: 4
  //

  /**
   * An 802_11 Request-To-Send packet.
   *
   * @author Rimon Barr &lt;barr+jist@cs.cornell.edu&gt;
   * @since SWANS1.0
   */

  public static class Rts extends AbstractMacMessage
      implements Mac802_11Message, HasDst, HasSrc {

    /**
     * source of the packet;
     */
    protected MacAddress src;

    /**
     * destination of the packet;
     */
    protected MacAddress dst;

    /**
     * Packet transmission duration.
     */
    protected int duration;


    // ////////////////////////////////////////////////
    // initialization
    //

    /**
     * Create an 802_11 RTS packet.
     *
     * @param dst
     *          packet destination address
     * @param src
     *          packet source address
     * @param duration
     *          packet transmission duration
     */
    public Rts(MacAddress dst, MacAddress src, int duration) {
      super();
      this.dst = dst;
      this.src = src;
      this.duration = duration;
    }

    // ////////////////////////////////////////////////
    // accessors
    //

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacMessage#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
      if (adapter == classHasSrc)
        return this;
      if (adapter == classHasDst)
        return this;
      return super.getAdapter(adapter);
    }

    /**
     * Return packet type.
     *
     * @return packet type
     */
    public int getType() {
      return TYPE_RTS;
    }

    /**
     * Return packet destination address.
     *
     * @return packet destination address
     */
    public MacAddress getDst() {
      return dst;
    }

    /**
     * Return packet source address.
     *
     * @return packet source address
     */
    public MacAddress getSrc() {
      return src;
    }

    /**
     * Return packet transmission duration.
     *
     * @return packet transmission duration
     */
    public int getDuration() {
      return duration;
    }

    // ////////////////////////////////////////////////
    // message interface
    //

    // Message interface
    /** {@inheritDoc} */
    public int getSize() {
      return getHeaderSize();
    }

    // Message interface
    /** {@inheritDoc} */
    public void getBytes(byte[] msg, int offset) {
      /* See Figure 4-13 in Fledermaus guide for more detail.
       *
       * RTS frame (bytes):
       *  +--- 2 ---+-- 2 --+----- 6 ------+------- 6 -------+-- 4 -+
       *  |  Frame  | Dura- |   Receiver   |   Transmitter   | FCS* |
       *  | Control | tion  |   Address    |     Address     |      |
       *  +---------+-------+--------------+-----------------+------+
       *  *FCS = Frame Check Sequence
       * 
       * Frame control (bits):
       *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
       *  | 0 1 | 2 3 | 4 5 6 7 |  8  |  9  | 10 | 11 | 12 | 13 | 14 | 15 |
       *  |Proto| Type| Subtype |To DS|Fr.DS|Frag|Retr|Pwr |More|Prot|Ord |
       *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
       *  | 0 0 | 1 0 | 1 1 0 1 |  0  |  0  |  0 |  0 | 0* |  0 |  0 |  0 |
       *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
       *  *Power management bit: can be 1 or 0, here not important; TODO
       *  
       */

      /* 0-1: frame control */
      Pickle.ubyteToArray(0x2D, msg, offset + 0);
      Pickle.ubyteToArray(0x00, msg, offset + 1);
      /* 2-3: duration */
      Pickle.ushortToArray(duration, msg, offset + 2);
      /* 4-9: receiver address */
      Pickle.MacAddressToArray(dst, msg, offset + 4);
      /* 10-15: transmitter address */
      Pickle.MacAddressToArray(src, msg, offset + 10);
      /* 16-19: FCS - set to all zeros for now; TODO? */
      Pickle.uintToArray(0, msg, offset + 16);
    }

    public static int getHeaderSize() {
      return 20;
    }

    public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + ((dst == null) ? 0 : dst.hashCode());
      result = PRIME * result + duration;
      result = PRIME * result + ((src == null) ? 0 : src.hashCode());
      return result;
    }

    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final Rts other = (Rts) obj;
      if (dst == null) {
        if (other.dst != null)
          return false;
      } else if (!dst.equals(other.dst))
        return false;
      if (duration != other.duration)
        return false;
      if (src == null) {
        if (other.src != null)
          return false;
      } else if (!src.equals(other.src))
        return false;
      return true;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Rts [" + src.toString() + "->" + dst.toString() + "]";
    }

  } // class: RTS


  // ////////////////////////////////////////////////
  // CTS frame: (size = 14)
  // frame control size: 2
  // duration size: 2
  // address: destination size: 6
  // CRC size: 4

  /**
   * An 802_11 Clear-To-Send packet.
   *
   * @author Rimon Barr &lt;barr+jist@cs.cornell.edu&gt;
   * @since SWANS1.0
   */

  public static class Cts extends AbstractMacMessage
      implements Mac802_11Message, HasDst {

    /**
     * destination of the packet;
     */
    protected MacAddress dst;

    /**
     * Packet transmission duration.
     */
    protected int duration;

    // ////////////////////////////////////////////////
    // initialization
    //

    /**
     * Create an 802_11 CTS packet.
     *
     * @param dst
     *          packet destination address
     * @param duration
     *          packet transmission duration
     */
    public Cts(MacAddress dst, int duration) {
      super();
      this.dst = dst;
      this.duration = duration;
    }

    // ////////////////////////////////////////////////
    // accessors
    //

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacMessage#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
      if (adapter == classHasDst)
        return this;
      return super.getAdapter(adapter);
    }

    /**
     * Return packet type.
     *
     * @return packet type
     */
    public int getType() {
      return TYPE_CTS;
    }

    /**
     * Return packet destination address.
     *
     * @return packet destination address
     */
    public MacAddress getDst() {
      return dst;
    }

    /**
     * Return packet transmission duration.
     *
     * @return packet transmission duration
     */
    public int getDuration() {
      return duration;
    }

    // ////////////////////////////////////////////////
    // message interface
    //

    // Message interface
    /** {@inheritDoc} */
    public int getSize() {
      return getHeaderSize();
    }

    // Message interface
    /** {@inheritDoc} */
    public void getBytes(byte[] msg, int offset) {
      /* See Figure 4-13 in Fledermaus guide for more detail.
      *
      * RTS frame (bytes):
      *  +--- 2 ---+-- 2 --+----- 6 ------+-- 4 -+
      *  |  Frame  | Dura- |   Receiver   | FCS* |
      *  | Control | tion  |   Address    |      |
      *  +---------+-------+--------------+------+
      *  *FCS = Frame Check Sequence
      * 
      * Frame control (bits):
      *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
      *  | 0 1 | 2 3 | 4 5 6 7 |  8  |  9  | 10 | 11 | 12 | 13 | 14 | 15 |
      *  |Proto| Type| Subtype |To DS|Fr.DS|Frag|Retr|Pwr |More|Prot|Ord |
      *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
      *  | 0 0 | 1 0 | 0 0 1 1 |  0  |  0  |  0 |  0 | 0* |  0 |  0 |  0 |
      *  +-----+-----+---------+-----+-----+----+----+----+----+----+----+
      *  *Power management bit: can be 1 or 0, here not important; TODO
      *  
      */

     /* 0-1: frame control */
     Pickle.ubyteToArray(0x23, msg, offset + 0);
     Pickle.ubyteToArray(0x00, msg, offset + 1);
     /* 2-3: duration */
     Pickle.ushortToArray(duration, msg, offset + 2);
     /* 4-9: receiver address */
     Pickle.MacAddressToArray(dst, msg, offset + 4);
     /* 10-13: FCS - set to all zeros for now; TODO? */
     Pickle.uintToArray(0, msg, offset + 10);
    }

    public static int getHeaderSize() {
      return 14;
    }

    public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + ((dst == null) ? 0 : dst.hashCode());
      result = PRIME * result + duration;
      return result;
    }

    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final Cts other = (Cts) obj;
      if (dst == null) {
        if (other.dst != null)
          return false;
      } else if (!dst.equals(other.dst))
        return false;
      if (duration != other.duration)
        return false;
      return true;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Cts [->" + dst.toString() +"]";
    }

  } // class: CTS



  public static class Data extends MacDcfMessage.Data
      implements Mac802_11Message, HasSrc, HasDst, HasSeq {

    /**
     * Packet moreFlag bit.
     */
    protected boolean moreFrag;

    protected short frag;

    /**
     * Create 802_11 data packet.
     *
     * @param dst
     *          packet destination address
     * @param src
     *          packet source address
     * @param duration
     *          packet transmission duration
     * @param seq
     *          packet sequence number
     * @param frag
     *          packet fragment number
     * @param moreFrag
     *          packet moreFrag flag
     * @param retry
     *          packet retry bit
     * @param body
     *          packet data payload
     */
    public Data(MacAddress dst, MacAddress src, int duration, short seq,
        short frag, boolean moreFrag, boolean retry, Message body) {
      super(dst, src, duration, seq, retry, body);
      this.moreFrag = moreFrag;
      this.frag = frag;
    }

    /**
     * Create 802_11 data packet.
     *
     * @param dst
     *          packet destination address
     * @param src
     *          packet source address
     * @param duration
     *          packet transmission duration
     * @param body
     *          packet data payload
     */
    public Data(MacAddress dst, MacAddress src, int duration, Message body) {
      super(dst, src, duration, body);
      this.moreFrag = false;
      this.frag = -1;
    }

    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + frag;
      result = PRIME * result + (moreFrag ? 1231 : 1237);
      return result;
    }

    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (!super.equals(obj))
        return false;
      if (getClass() != obj.getClass())
        return false;
      final Mac802_11Message.Data other = (Mac802_11Message.Data) obj;
      if (frag != other.frag)
        return false;
      if (moreFrag != other.moreFrag)
        return false;
      return true;
    }
  }
}
