package click.swans.mac;

import java.util.Arrays;

import jist.swans.Constants;
import jist.swans.mac.Mac802_11Message;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacMessage;
import jist.swans.misc.Message;
import jist.swans.misc.MessageBytes;
import jist.swans.misc.Pickle;

public interface MacClickMessage extends Mac802_11Message {

  // ////////////////////////////////////////////////
  // frame control
  //

  public static final int WIFI_FC0_TYPE_MASK = 0x0c;

  public static final int WIFI_FC0_TYPE_MGT = 0x00;

  public static final int WIFI_FC0_TYPE_CTL = 0x04;

  public static final int WIFI_FC0_TYPE_DATA = 0x08;

  public static final int WIFI_FC0_SUBTYPE_MASK = 0xf0;

  /* for TYPE_MGT */
  public static final int WIFI_FC0_SUBTYPE_ASSOC_REQ = 0x00;

  public static final int WIFI_FC0_SUBTYPE_ASSOC_RESP = 0x10;

  public static final int WIFI_FC0_SUBTYPE_REASSOC_REQ = 0x20;

  public static final int WIFI_FC0_SUBTYPE_REASSOC_RESP = 0x30;

  public static final int WIFI_FC0_SUBTYPE_PROBE_REQ = 0x40;

  public static final int WIFI_FC0_SUBTYPE_PROBE_RESP = 0x50;

  public static final int WIFI_FC0_SUBTYPE_BEACON = 0x80;

  public static final int WIFI_FC0_SUBTYPE_ATIM = 0x90;

  public static final int WIFI_FC0_SUBTYPE_DISASSOC = 0xa0;

  public static final int WIFI_FC0_SUBTYPE_AUTH = 0xb0;

  public static final int WIFI_FC0_SUBTYPE_DEAUTH = 0xc0;

  /* for TYPE_CTL */
  public static final int WIFI_FC0_SUBTYPE_PS_POLL = 0xa0;

  public static final int WIFI_FC0_SUBTYPE_RTS = 0xb0;

  public static final int WIFI_FC0_SUBTYPE_CTS = 0xc0;

  public static final int WIFI_FC0_SUBTYPE_ACK = 0xd0;

  public static final int WIFI_FC0_SUBTYPE_CF_END = 0xe0;

  public static final int WIFI_FC0_SUBTYPE_CF_END_ACK = 0xf0;

  /* for TYPE_DATA (bit combination) */
  public static final int WIFI_FC0_SUBTYPE_DATA = 0x00;

  public static final int WIFI_FC0_SUBTYPE_CF_ACK = 0x10;

  public static final int WIFI_FC0_SUBTYPE_CF_POLL = 0x20;

  public static final int WIFI_FC0_SUBTYPE_CF_ACPL = 0x30;

  public static final int WIFI_FC0_SUBTYPE_NODATA = 0x40;

  public static final int WIFI_FC0_SUBTYPE_CFACK = 0x50;

  public static final int WIFI_FC0_SUBTYPE_CFPOLL = 0x60;

  public static final int WIFI_FC0_SUBTYPE_CF_ACK_CF_ACK = 0x70;

  public static final int WIFI_FC0_SUBTYPE_QOS = 0x80;

  public static final int WIFI_FC0_SUBTYPE_QOS_NULL = 0xc0;


  /** fake type for extra header, unused by 802.11 */
  public static final int TYPE_WIFI_EXTRA = 0x0C;


  public static final byte WIFI_FC1_DIR_MASK       =0x03;

  public static final byte DIR_NODS = 0; /* STA->STA */

  public static final byte DIR_TODS = 1; /* STA->AP */

  public static final byte DIR_FROMDS = 2; /* AP ->STA */

  public static final byte DIR_DSTODS = 3; /* AP ->AP */


  public static final int WIFI_FC1_MORE_FRAG = 0x04;
  public static final int WIFI_FC1_RETRY = 0x08;
  public static final int WIFI_FC1_PWR_MGT= 0x10;
  public static final int WIFI_FC1_MORE_DATA = 0x20;
  public static final int WIFI_FC1_WEP = 0x40;
  public static final int WIFI_FC1_ORDER= 0x80;

  /**
   * Get the 802.11 frame type (see WIFI_FC0_TYPE_...)
   * @return the 802.11 frame type (see WIFI_FC0_TYPE_...)
   */
  int get802_11Type();

  /**
   * Get the 802.11 frame sub-type (see WIFI_FC0_SUBTYPE_...)
   * @return the 802.11 frame sub-type (see WIFI_FC0_SUBTYPE_...)
   */
  int get802_11Subtype();


  /**
   * 802.11 rts frame
   *
   * @author kurth
   */
  public static class Rts extends Mac802_11Message.Rts implements MacClickMessage {

    public Rts(MacAddress dst, MacAddress src, int duration) {
      super(dst, src, duration);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Type()
     */
    public int get802_11Type() {
      return WIFI_FC0_TYPE_CTL;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Subtype()
     */
    public int get802_11Subtype() {
      return WIFI_FC0_SUBTYPE_RTS;
    }
  }


  /**
   * 802.11 cts frame
   *
   * @author kurth
   */
  public static class Cts extends Mac802_11Message.Cts implements MacClickMessage {

    public Cts(MacAddress dst, int duration) {
      super(dst, duration);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Type()
     */
    public int get802_11Type() {
      return WIFI_FC0_TYPE_CTL;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Subtype()
     */
    public int get802_11Subtype() {
      return WIFI_FC0_SUBTYPE_CTS;
    }
  }


  /**
   * 802.11 ack frame
   *
   * @author kurth
   */
  public static class Ack extends Mac802_11Message.Ack implements MacClickMessage {

    public Ack(MacAddress dst, int duration) {
      super(dst, duration);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Type()
     */
    public int get802_11Type() {
      return WIFI_FC0_TYPE_CTL;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Subtype()
     */
    public int get802_11Subtype() {
      return WIFI_FC0_SUBTYPE_ACK;
    }
  }


  /**
   * Base class for messages with body.
   *
   * @author kurth
   */
  public abstract static class AbstractMessageWithBody extends Mac802_11Message.Data
      implements MacClickMessage {

    /**
     * Packet source address.
     */
    protected MacAddress bssid;

    /**
     * packet direction
     */
    protected byte dir;

    /**
     * 802.11 subtype
     */
    protected int subtype;

    /**
     * Create 802_11 packet with body for click
     *
     * @param subtype
     *          802.11 subtype
     * @param dir
     *          direction of the frame
     * @param dst
     *          packet destination address
     * @param src
     *          packet source address
     * @param bssid
     *          the bssid to use
     * @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 AbstractMessageWithBody(int subtype, byte dir,
        MacAddress dst, MacAddress src, MacAddress bssid, int duration, short seq,
        short frag, boolean moreFrag, boolean retry, Message body) {
      super(dst, src, duration, seq, frag, moreFrag, retry, body);
      this.bssid = bssid;
      this.dir = dir;
      this.subtype = subtype;
    }

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

    /**
     * Return the direction flag.
     *
     * @return the direction flag.
     */
    public byte getDir() {
      return dir;
    }

    public void setDuration(int duration) {
      this.duration = duration;
    }

    public void setSeq(short seq) {
      this.seq = seq;
    }

    public void setRetry(boolean retry) {
      this.retry = retry;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacClickMessage#get802_11Subtype()
     */
    public int get802_11Subtype() {
      return subtype;
    }

    public void get802_11HeaderBytes(byte[] msg, int offset) {
      // 0: fc[0]
      Pickle.ubyteToArray(get802_11Type() | get802_11Subtype(), msg, offset + 0);
      // 1: fc[1]
      Pickle.ubyteToArray(dir + (moreFrag ? WIFI_FC1_MORE_FRAG : 0)
          + (retry ? WIFI_FC1_RETRY : 0), msg, offset + 1);
      // 2-3: dur
      Pickle.ushortToArray(duration, msg, offset + 2);
      // 4-9: addr1
      Pickle.MacAddressToArray(dst, msg, offset + 4);
      // 10-15: addr2
      Pickle.MacAddressToArray(src, msg, offset + 10);
      // 16-21: addr3
      if (null != bssid)
        Pickle.MacAddressToArray(bssid, msg, offset + 16);
      else
        Arrays.fill(msg, offset + 16, offset + 20, (byte)0);
      // 22-23: seq
      Pickle.shortToArray((short)(seq << 4), msg, offset + 22);
    }

    /**
     * Update the byte representation of this message, e.g. place in the retry
     * bit and sequence number.
     *
     * @param msg the message as bytes.
     * @param offset the offset where the mac header starts
     */
    public void updateRecvPacket(byte[] msg, int offset) {
      // 1: fc[1], update retry
      Pickle.ubyteToArray((msg[offset + 1] & ~WIFI_FC1_RETRY) +
          (retry ? WIFI_FC1_RETRY : 0), msg, offset + 1);
      // 2-3: dur
      Pickle.ushortToArray(duration, msg, offset + 2);
      // 22-23: seq
      Pickle.shortToArray((short)(seq << 4), msg, offset + 22);
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + ((bssid == null) ? 0 : bssid.hashCode());
      result = PRIME * result + subtype;
      return result;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (!super.equals(obj))
        return false;
      if (getClass() != obj.getClass())
        return false;
      final AbstractMessageWithBody other = (AbstractMessageWithBody) obj;
      if (bssid == null) {
        if (other.bssid != null)
          return false;
      } else if (!bssid.equals(other.bssid))
        return false;
      if (dir != other.dir)
        return false;
      if (subtype != other.subtype)
        return false;
      return true;
    }
  }


  /**
   * 802.11 management frame.
   *
   * @author kurth
   */
  public static class Management extends AbstractMessageWithBody
      implements MacClickMessage {

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

    /**
     * Create 802_11 management packet
     *
     * @param subtype
     *          802.11 subtype
     * @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 Management(int subtype, MacAddress dst, MacAddress src, MacAddress bssid,
        int duration, short seq, short frag, boolean moreFrag, boolean retry, Message body) {
      super(subtype, DIR_NODS, dst, src, bssid, duration, seq, frag, moreFrag,
          retry, body);
    }

    /**
     * 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 Management(int subtype, MacAddress dst, MacAddress src, MacAddress bssid,
        int duration, Message body) {
      this(subtype, dst, src, bssid, duration, (short) -1, (short) -1, false, false, body);
    }

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

    public int get802_11Type() {
      return WIFI_FC0_TYPE_MGT;
    }

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

    public static int getHeaderSize() {
      return 24; // TODO 28
    }

    // Message interface
    /** {@inheritDoc} */
    public int getSize() {
      int size = body.getSize();
      if (size == Constants.ZERO_WIRE_SIZE) {
        return Constants.ZERO_WIRE_SIZE;
      }
      return size + getHeaderSize();
    }


    // Message interface
    /** {@inheritDoc} */
    public void getBytes(byte[] msg, int offset) {
      // Prepare 802.11 header
      get802_11HeaderBytes(msg, offset);

      // Prepend body
      body.getBytes(msg, offset + 24);
    }

  } // class: Management


  // ////////////////////////////////////////////////
  // DATA frame: (size = 34 + body)
  // frame control size: 2
  // duration / ID size: 2
  // address: destination size: 6
  // address: source size: 6
  // address: #3 size: 6
  // sequence control size: 2
  // address: #4 size: 6
  // frame body size: 0 - 2312
  // CRC size: 4

  /**
   * 802.11 data frame
   *
   * @author kurth
   */
  public static class Data extends AbstractMessageWithBody implements MacClickMessage {

    /**
     * Type of body.
     */
    protected int etherType;

    /**
     * Create 802_11 management packet
     *
     * @param subtype
     *          802.11 subtype
     * @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(int subtype, byte dir, MacAddress dst, MacAddress src, MacAddress bssid,
        int duration, short seq, short frag, boolean moreFrag, boolean retry,
        int etherType, Message body) {
      super(subtype, dir, dst, src, bssid, duration, seq, frag, moreFrag,
          retry, body);
      this.etherType = etherType;

//      if (body instanceof NetMessage.Ip)
//        etherType = ETHER_TYPE_IP;
//      else
//        etherType = ETHER_TYPE_UNKNOWN;
//
//      if (Main.ASSERT)
//        Util.assertion(this.etherType == etherType);
    }


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

    public int get802_11Type() {
      return WIFI_FC0_TYPE_DATA;
    }

    public int getEtherType() {
      return etherType;
    }

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

    public static int getHeaderSize() {
      return 24 + 8; // TODO 28
    }

    // Message interface
    /** {@inheritDoc} */
    public int getSize() {
      int size = body.getSize();
      if (size == Constants.ZERO_WIRE_SIZE) {
        return Constants.ZERO_WIRE_SIZE;
      }
      return size + getHeaderSize();
    }

    // Message interface
    /** {@inheritDoc} */
    public void getBytes(byte[] msg, int offset) {
      // Prepare 802.11 header
      get802_11HeaderBytes(msg, offset);

      // 0-7 llc
      Pickle.uintToArray(0xaaaa0300, msg, offset + 24);
      msg[offset + 24 + 4] = 0;
      msg[offset + 24 + 5] = 0;
      Pickle.ushortToArray(etherType, msg, offset + 24 + 6);

      // Prepend body
      body.getBytes(msg, offset + 24 + 8);
    }


    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + etherType;
      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 MacClickMessage.Data other = (MacClickMessage.Data) obj;
      if (etherType != other.etherType)
        return false;
      return true;
    }
  }


  /**
   * Same as super class, but returns also the whole message
   *
   * TODO store and compare encodings (error check)
   *
   * @author kurth
   */
  public class RawMessage extends MessageBytes {

    public RawMessage(byte[] data, int offset, int length) {
      super(data, offset, length);
    }

    public RawMessage(byte[] data) {
      super(data);
    }

    public RawMessage(String data) {
      super(data);
    }

  }

  public class ExtraHeader extends AbstractMacMessage implements MacMessage {

    /** size of this header in octets */
    public static final int HEADER_SIZE = 24;

    /** packet transmission */
    public static final int WIFI_EXTRA_TX         = (1<<0) << 24;

    /** transmission failed */
    public static final int WIFI_EXTRA_TX_FAIL        = (1<<1)<< 24;

    /** used alternate bitrate */
    public static final int WIFI_EXTRA_TX_USED_ALT_RATE   = (1<<2)<< 24;

    /** failed crc check */
    public static final int WIFI_EXTRA_RX_ERR     = (1<<3)<< 24;

    /** first part of a fragmented skb */
    public static final int WIFI_EXTRA_RX_MORE        = (1<<4)<< 24;

    public static final int WIFI_EXTRA_NO_SEQ     = (1<<5)<< 24;
    public static final int WIFI_EXTRA_NO_TXF     = (1<<6)<< 24;
    public static final int WIFI_EXTRA_DO_RTS_CTS     = (1<<7)<< 24;
    public static final int WIFI_EXTRA_DO_CTS     = (1<<8)<< 24;

    public int flags;

    public byte rssi;
    public byte silence;
    public byte power;
    public byte pad;

    public byte rate;         /* bitrate in Mbps*2 */
    public byte rate1;        /* bitrate in Mbps*2 */
    public byte rate2;        /* bitrate in Mbps*2 */
    public byte rate3;        /* bitrate in Mbps*2 */

    public byte max_tries;
    public byte max_tries1;
    public byte max_tries2;
    public byte max_tries3;

    public byte virt_col;
    public byte retries;
    public short len;

    public MacClickMessage body;

    ///////////////////////////////////////////////////////////////////
    // Message interface

    public ExtraHeader(
        //int type, byte dir, boolean moreFrag, boolean retry, int duration,
        //MacAddress addr1, MacAddress addr2, MacAddress addr3, short seq,
        int flags, byte rssi, byte silence, byte power, byte pad,
        byte rate, byte rate1, byte rate2, byte rate3, byte max_tries, byte max_tries1,
        byte max_tries2, byte max_tries3, byte virt_col, byte retries, short len,
        MacClickMessage body) {
      //super(type, dir, moreFrag, retry, duration, addr1, addr2, addr3, seq);
      this.flags = flags;
      this.rssi = rssi;
      this.silence = silence;
      this.power = power;
      this.pad = pad;
      this.rate = rate;
      this.rate1 = rate1;
      this.rate2 = rate2;
      this.rate3 = rate3;
      this.max_tries = max_tries;
      this.max_tries1 = max_tries1;
      this.max_tries2 = max_tries2;
      this.max_tries3 = max_tries3;
      this.virt_col = virt_col;
      this.retries = retries;
      this.len = len;
      this.body = body;
    }

    /**
     * Information for received frames
     *
     * @param flags
     * @param rssi
     * @param silence
     * @param body
     */
    public ExtraHeader(int flags, byte rssi, byte silence, MacClickMessage body) {
      this.flags = flags;
      this.rssi = rssi;
      this.silence = silence;
      this.body = body;
    }

    public void updateHeader(byte[] msg, int offset) {
      // flags
      Pickle.uintToArray(0x01204907, msg, offset + 0);
      // flags
      Pickle.uintToArray(flags, msg, offset + 4);

      // rssi;
      Pickle.ubyteToArray(rssi, msg, offset + 8);
      // silence;
      Pickle.ubyteToArray(silence, msg, offset + 9);
      // power;
      Pickle.ubyteToArray(power, msg, offset + 10);
      // pad;
      Pickle.ubyteToArray(pad, msg, offset + 11);

      // rate;         /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate, msg, offset + 12);
      // rate1;        /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate1, msg, offset + 13);
      // rate2;        /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate2, msg, offset + 14);
      // rate3;        /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate3, msg, offset + 15);

      // max_tries;
      Pickle.ubyteToArray(max_tries, msg, offset + 16);
      // max_tries1;
      Pickle.ubyteToArray(max_tries1, msg, offset + 17);
      // max_tries2;
      Pickle.ubyteToArray(max_tries2, msg, offset + 18);
      // max_tries3;
      Pickle.ubyteToArray(max_tries3, msg, offset + 19);

      // virt_col;
      Pickle.ubyteToArray(virt_col, msg, offset + 20);
      // retries;
      Pickle.ubyteToArray(retries, msg, offset + 21);
      // len;
      Pickle.ushortToArray(len, msg, offset + 22);

    }

    public void getBytes(byte[] msg, int offset) {
      updateHeader(msg, offset);

      // MacMessage body;
      body.getBytes(msg, offset + 24);
    }

    public int getSize() {
      return 24 + body.getSize();
    }

    public static void updateRecvPacket(byte[] msg, int offset,
        int flags, byte rssi, byte rate) {
      // flags
      Pickle.uintToArray(0x01204907, msg, offset + 0);
      // flags
      Pickle.uintToArray(flags, msg, offset + 4);

      // rssi;
      Pickle.ubyteToArray(rssi, msg, offset + 8);
      // silence;
      msg[offset + 9] = 0;
      // power;
      msg[offset + 10] = 0;
      // pad;
      msg[offset + 11] = 0;

      // rate;         /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate, msg, offset + 12);
      // rate1;        /* bitrate in Mbps*2 */
      msg[offset + 13] = 0;
      // rate2;        /* bitrate in Mbps*2 */
      msg[offset + 14] = 0;
      // rate3;        /* bitrate in Mbps*2 */
      msg[offset + 15] = 0;

      // max_tries;
      msg[offset + 16] = 0;
      // max_tries1;
      msg[offset + 17] = 0;
      // max_tries2;
      msg[offset + 18] = 0;
      // max_tries3;
      msg[offset + 19] = 0;

      // virt_col;
      msg[offset + 20] = 0;
      // retries;
      msg[offset + 21] = 0;
      // len;
      // skip len
    }

    public static void updateFeedbackedPacket(byte[] msg, int offset, int flags,
        byte rate, byte rate1, byte max_tries, byte max_tries1, byte retries,
        byte virt_col) {
      // flags
      Pickle.uintToArray(0x01204907, msg, offset + 0);
      // flags
      Pickle.uintToArray(flags, msg, offset + 4);

      // rssi;
      msg[offset + 8] = 0;
      // silence;
      msg[offset + 9] = 0;
      // power;
      msg[offset + 10] = 0;
      // pad;
      msg[offset + 11] = 0;

      // rate;         /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate, msg, offset + 12);
      // rate1;        /* bitrate in Mbps*2 */
      Pickle.ubyteToArray(rate1, msg, offset + 13);
      // rate2;        /* bitrate in Mbps*2 */
      msg[offset + 14] = 0;
      // rate3;        /* bitrate in Mbps*2 */
      msg[offset + 15] = 0;

      // max_tries;
      Pickle.ubyteToArray(max_tries, msg, offset + 16);
      // max_tries1;
      Pickle.ubyteToArray(max_tries1, msg, offset + 17);
      // max_tries2;
      msg[offset + 18] = 0;
      // max_tries3;
      msg[offset + 19] = 0;

      // virt_col;
      Pickle.ubyteToArray(virt_col, msg, offset + 20);
      // retries;
      Pickle.ubyteToArray(retries, msg, offset + 21);
      // len;
      // skip len
    }

    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
      final int PRIME = 31;
      int result = 1;
      result = PRIME * result + ((body == null) ? 0 : body.hashCode());
      result = PRIME * result + flags;
      result = PRIME * result + len;
      return result;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#equals(java.lang.Object)
     */
    public boolean equals(Object obj) {
      if (this == obj)
        return true;
      if (obj == null)
        return false;
      if (getClass() != obj.getClass())
        return false;
      final ExtraHeader other = (ExtraHeader) obj;
      if (body == null) {
        if (other.body != null)
          return false;
      } else if (!body.equals(other.body))
        return false;
      if (flags != other.flags)
        return false;
      if (len != other.len)
        return false;
      if (max_tries != other.max_tries)
        return false;
      if (max_tries1 != other.max_tries1)
        return false;
      if (max_tries2 != other.max_tries2)
        return false;
      if (max_tries3 != other.max_tries3)
        return false;
      if (pad != other.pad)
        return false;
      if (power != other.power)
        return false;
      if (rate != other.rate)
        return false;
      if (rate1 != other.rate1)
        return false;
      if (rate2 != other.rate2)
        return false;
      if (rate3 != other.rate3)
        return false;
      if (retries != other.retries)
        return false;
      if (rssi != other.rssi)
        return false;
      if (silence != other.silence)
        return false;
      if (virt_col != other.virt_col)
        return false;
      return true;
    }

    public Object getAdapter(Class adapter) {
      return (null != body ? body.getAdapter(adapter) : null);
    }
  }

}
