package brn.swans.mac;

import java.util.List;

import jist.swans.Constants;
import jist.swans.mac.Mac802_11Message;
import jist.swans.mac.MacAddress;
import jist.swans.mac.MacDcfMessage;
import jist.swans.mac.MacMessage;
import jist.swans.misc.Message;
import jist.swans.net.NetAddress;

public interface MacTxDORMessage extends Mac802_11Message {

  /**
   * An rts packet with additional ip address and ip id. this way receiving
   * nodes could check if they are going to send the same packet in near
   * future.
   *
   * @author kurth
   */
  public static class Rts extends Mac802_11Message.Rts
      implements MacTxDORMessage, MacMessage.HasCandidates, MacMessage.HasSrc {

    protected List candidates;

    /** ip packet source address. */
    protected NetAddress netSrc;
    /** ip packet identification. */
    protected short netId;
    /** ip packet ttl. */
    protected byte netTTL;
    /** sequence number on mac level */
    protected short seqNo;
    /** mac retry indicator */
    protected boolean retry;

    protected byte idxFinalDst;

    protected static Class classMacTxDORMessage = MacTxDORMessage.class;

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

    public Rts(List lstCandidates, MacAddress src, int duration, NetAddress netSrc,
        short netId, byte netTtl, short seqNo, boolean retry, byte idxFinalDst) {
      super(null, src, duration);
      this.netSrc = netSrc;
      this.netId = netId;
      this.netTTL = netTtl;
      this.seqNo = seqNo;
      this.retry = retry;
      this.candidates = lstCandidates;
      this.idxFinalDst = idxFinalDst;
    }


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

    public short getNetId() {
      return netId;
    }

    public NetAddress getNetSrc() {
      return netSrc;
    }

    public byte getNetTTL() {
      return netTTL;
    }

    public boolean isRetry() {
      return retry;
    }

    public short getSeqNo() {
      return seqNo;
    }

    public byte getIdxFinalDst() {
      return idxFinalDst;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacMessage.MacMessageWithCandidates#getCandidates()
     */
    public List getCandidates() {
      return candidates;
    }

    // ////////////////////////////////////////////////
    // overwrites
    //

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

    public static int getHeaderSize() {
      throw new RuntimeException("not supported");
    }

    public static int getHeaderSize(MacTxDORMessage.Rts msg) {
      return getHeaderSize(msg.candidates.size());
    }

    public static int getHeaderSize(int csSize) {
      return Mac802_11Message.Rts.getHeaderSize() + 10 + 6 * (csSize - 1);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacDcfMessage.Ack#getSize()
     */
    public int getSize() {
      return MacTxDORMessage.Rts.getHeaderSize(this);
    }


    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + ((candidates == null) ? 0 : candidates.hashCode());
      result = PRIME * result + netId;
      result = PRIME * result + ((netSrc == null) ? 0 : netSrc.hashCode());
      result = PRIME * result + (retry ? 1231 : 1237);
      result = PRIME * result + seqNo;
      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 MacTxDORMessage.Rts other = (MacTxDORMessage.Rts) obj;
      if (candidates == null) {
        if (other.candidates != null)
          return false;
      } else if (!candidates.equals(other.candidates))
        return false;
      if (netId != other.netId)
        return false;
      if (netSrc == null) {
        if (other.netSrc != null)
          return false;
      } else if (!netSrc.equals(other.netSrc))
        return false;
      if (netTTL != other.netTTL)
        return false;
      if (retry != other.retry)
        return false;
      if (seqNo != other.seqNo)
        return false;
      return true;
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Rts [" + src.toString() + "->" + candidates.toString() + ", seq="
        + seqNo + (retry ? ", retry, [net src=" : ", [net src=") + netSrc
        + ", id=" + netId + ", ttl=" + netTTL + "]]";
    }
  }


  /**
   * Cts packet with a candidate list and packet identification for two purposes:
   * firstly, a station decides to use transmit diversity for data packets
   * based on packet identifications and candidate sets from rts and cts packets.
   * As second purpose, the candidate set is used to identify the belonging of
   * a station to a particular opportunistic transmission.
   *
   * @author kurth
   */
  public static class Cts extends Mac802_11Message.Cts
      implements MacTxDORMessage, MacMessage.HasCandidates, MacMessage.HasDst {
    protected List candidates;

    /** ip packet source address. */
    protected NetAddress netSrc;
    /** ip packet identification. */
    protected short netId;
    /** ip packet ttl. */
    protected byte netTTL;
    /** sequence number on mac level */
    protected short seqNo;
    /** mac retry indicator */
    protected boolean retry;

    protected byte idxFinalDst;

    protected static Class classMacTxDORMessage = MacTxDORMessage.class;

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

    public Cts(MacAddress dst, List lstCandidates, int duration, NetAddress netSrc,
        short netId, byte netTtl, short seqNo, boolean retry, byte idxFinalDst) {
      super(dst, duration);
      this.netSrc = netSrc;
      this.netId = netId;
      this.netTTL = netTtl;
      this.seqNo = seqNo;
      this.retry = retry;
      this.candidates = lstCandidates;
      this.idxFinalDst = idxFinalDst;
    }


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

    public short getNetId() {
      return netId;
    }

    public NetAddress getNetSrc() {
      return netSrc;
    }

    public byte getNetTTL() {
      return netTTL;
    }

    public boolean isRetry() {
      return retry;
    }

    public short getSeqNo() {
      return seqNo;
    }

    public byte getIdxFinalDst() {
      return idxFinalDst;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacMessage.MacMessageWithCandidates#getCandidates()
     */
    public List getCandidates() {
      return candidates;
    }

    // ////////////////////////////////////////////////
    // overwrites
    //

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

    public static int getHeaderSize() {
      throw new RuntimeException("not supported");
    }

    public static int getHeaderSize(MacTxDORMessage.Cts msg) {
      return getHeaderSize(msg.candidates.size());
    }

    public static int getHeaderSize(int csSize) {
      return Mac802_11Message.Cts.getHeaderSize() + 10 + 6 * (csSize - 1);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacDcfMessage.Ack#getSize()
     */
    public int getSize() {
      return MacTxDORMessage.Cts.getHeaderSize(this);
    }


    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Cts [" + candidates.toString() + "->" + dst.toString() + ", seq="
        + seqNo + (retry ? ", retry, [net src=" : ", [net src=") + netSrc
        + ", id=" + netId + ", ttl=" + netTTL + "]]";
    }


    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + ((candidates == null) ? 0 : candidates.hashCode());
      result = PRIME * result + netId;
      result = PRIME * result + ((netSrc == null) ? 0 : netSrc.hashCode());
      result = PRIME * result + (retry ? 1231 : 1237);
      result = PRIME * result + seqNo;
      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 MacTxDORMessage.Cts other = (MacTxDORMessage.Cts) obj;
      if (candidates == null) {
        if (other.candidates != null)
          return false;
      } else if (!candidates.equals(other.candidates))
        return false;
      if (netId != other.netId)
        return false;
      if (netSrc == null) {
        if (other.netSrc != null)
          return false;
      } else if (!netSrc.equals(other.netSrc))
        return false;
      if (netTTL != other.netTTL)
        return false;
      if (retry != other.retry)
        return false;
      if (seqNo != other.seqNo)
        return false;
      return true;
    }
  }


  /**
   * Data packet with
   *
   * @author kurth
   */
  public static class Data extends Mac802_11Message.Data implements
      MacTxDORMessage, MacMessage.HasCandidates {

    protected List candidates;

    protected byte idxFinalDst;

    protected static Class classMacTxDORMessage = MacTxDORMessage.class;

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

    public Data(List lstCandidates, MacAddress src, int duration, short seq,
        boolean retry, byte idxFinalDst, Message body) {
      super(null, src, duration, seq, (short) -1, false, retry, body);
      this.candidates = lstCandidates;
      this.idxFinalDst = idxFinalDst;
    }


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

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacDcfMessage.Data#getDst()
     */
    public MacAddress getDst() {
      throw new RuntimeException("not supported");
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacMessage.MacMessageWithCandidates#getCandidates()
     */
    public List getCandidates() {
      return candidates;
    }

    /**
     * @return the idxFinalDst
     */
    public byte getIdxFinalDst() {
      return idxFinalDst;
    }

    public static int getHeaderSize() {
      throw new RuntimeException("not supported");
    }

    public static int getHeaderSize(MacTxDORMessage.Data msg) {
      return getHeaderSize(msg.candidates.size());
    }

    public static int getHeaderSize(int csSize) {
      return Mac802_11Message.Data.getHeaderSize() + 6 + 1 * (csSize - 1);
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.misc.Message#getSize()
     */
    public int getSize() {
      int size = body.getSize();
      if (size == Constants.ZERO_WIRE_SIZE) {
        return Constants.ZERO_WIRE_SIZE;
      }
      return getHeaderSize(this) + size;
    }

    // ////////////////////////////////////////////////
    // overwrites
    //

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

    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + ((candidates == null) ? 0 : candidates.hashCode());
      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 MacTxDORMessage.Data other = (MacTxDORMessage.Data) obj;
      if (candidates == null) {
        if (other.candidates != null)
          return false;
      } else if (!candidates.equals(other.candidates))
        return false;
      return true;
    }


    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Data [" + src.toString() + "->" + candidates.toString() + ", seq "
        + seq + (retry ? ", retry][" : "][") + body.toString() + "]";
    }
  }

  /**
   * An ack packet with additional ip address and ip id. this way receiving
   * nodes could check if they are going to send the same packet in near
   * future.
   *
   * @author kurth
   */
  public static class Ack extends MacDcfMessage.Ack implements MacTxDORMessage {

    /** ip packet source address. */
    protected NetAddress netSrc;
    /** ip packet identification. */
    protected short netId;
    /** ip ttl counter */
    protected byte netTTL;
    /** whether the ack comes from the final destination */
    protected boolean fromFinalDst;

    protected static Class classMacTxDORMessage = MacTxDORMessage.class;

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

    public Ack(MacAddress dst, int duration, NetAddress netSrc, short netId,
        byte netTTL, boolean fromFinalDst) {
      super(dst, duration);
      this.netSrc = netSrc;
      this.netId = netId;
      this.netTTL = netTTL;
      this.fromFinalDst = fromFinalDst;
    }


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

    public short getNetId() {
      return netId;
    }

    public NetAddress getNetSrc() {
      return netSrc;
    }

    public byte getNetTTL() {
      return netTTL;
    }

    // ////////////////////////////////////////////////
    // overwrites
    //

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

    public static int getHeaderSize() {
      return MacDcfMessage.Ack.getHeaderSize() + 7;
    }

    /*
     * (non-Javadoc)
     * @see jist.swans.mac.MacDcfMessage.Ack#getSize()
     */
    public int getSize() {
      return MacTxDORMessage.Ack.getHeaderSize();
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    public String toString() {
      return "Ack [->" + dst.toString() + ", [net src=" + netSrc + ", id=" +
      netId + ", ttl=" + netTTL + "]]";
    }


    /* (non-Javadoc)
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
      final int PRIME = 31;
      int result = super.hashCode();
      result = PRIME * result + netId;
      result = PRIME * result + ((netSrc == null) ? 0 : netSrc.hashCode());
      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 MacTxDORMessage.Ack other = (MacTxDORMessage.Ack) obj;
      if (netId != other.netId)
        return false;
      if (netSrc == null) {
        if (other.netSrc != null)
          return false;
      } else if (!netSrc.equals(other.netSrc))
        return false;
      if (netTTL != other.netTTL)
        return false;
      return true;
    }


    /**
     * @return the fromFinalDst
     */
    public boolean isFromFinalDst() {
      return fromFinalDst;
    }

  }

}
