// TCPMessage.java

package protocol;				// protocol package

import support.*;				// import Jasper support classes

/**
  This is the class for a TCP segment.

  @author	Iain A. Robin, Kenneth J. Turner
  @version	1.0 (1st September 1999, IAR): initial version
  <br/>		1.4 (9th March 2006, KJT): updated for JDK 1.5
  <br/>		1.5 (26th July 2010, KJT): minor tidying, proper "toString"
		  method added
*/
public class TCPMessage extends PDU {

  /** Urgent flag */
  public final static int URG = 32;

  /** Acknowledgement flag */
  public final static int ACK = 16;

  /** Push flag */
  public final static int PSH = 8;

  /** Reset flag */
  public final static int RST = 4;

  /** Synchronise flag */
  public final static int SYN = 2;

  /** Finish flag */
  public final static int FIN = 1;

  /** Acknowledgement sequence number */
  protected int ack = -1;

  /** Flag setting */
  protected int flags = 0;

  /** Window sequence number */
  protected int win = -1;

  /** Protocol flags */
  protected boolean urgFlag, ackFlag, pshFlag, rstFlag, synFlag, finFlag;

  /**
    Constructor for a TCP segment.

    @param seq		sequence number
    @param flags	flags
    @param size		message size
  */
  public TCPMessage(int seq, int flags, int size) {
    this.seq = seq;
    this.flags = flags;
    setFlags();
    this.size = size;
  }

  /**
    Constructor for a TCP segment.

    @param seq		sequence number
    @param ack		acknowlegement sequence number
    @param flags	flags
    @param size		message size
  */
  public TCPMessage(int seq, int ack, int flags, int size) {
    this.seq = seq;
    this.ack = ack;
    this.flags = flags;
    setFlags();
    this.size = size;
  }

  /**
    Return the label of a TCP segment.

    @return		segment label
  */
  public String getLabel() {
    // return label for arrow representing this segment
    // in a time sequence diagram
    String label = "";
    if (size > 0)
      label += "Seq " + seq + ", ";
    if (ackFlag)
      label += "Ack " + ack + ", ";
    if (win >= 0)
      label += "Win " + win;
    if (urgFlag)
      label += ", URG";
    if (pshFlag)
      label += ", PSH";
    if (rstFlag)
      label += ", RST";
    if (synFlag)
      label += ", SYN";
    if (finFlag)
      label += ", FIN";
    return (label);
  }

  /**
    Return the Acknowledgement attribute of a TCP segment.

    @return		Acknowledgement value
  */
  public boolean isAck() {
    return (ackFlag);
  }

  /**
    Return the Finish attribute of a TCP segment.

    @return		Finish value
  */
  public boolean isFin() {
    return (finFlag);
  }

  /**
    Return the Push attribute of a TCP segment.

    @return		Push value
  */
  public boolean isPsh() {
    return (pshFlag);
  }

  /**
    Return the Reset attribute of a TCP segment.

    @return		Reset value
  */
  public boolean isRst() {
    return (rstFlag);
  }

  /**
    Return the Synchronise attribute of a TCP segment.

    @return		Synchronise value
  */
  public boolean isSyn() {
    return (synFlag);
  }

  /**
    Return the Urgent attribute of a TCP segment.

    @return		Urgent value
  */
  public boolean isUrg() {
    return (urgFlag);
  }

  /**
    Check if the given PDU matches this one.

    @param pdu		PDU
    @return		true/false if PDU does/does not match
  */
  public boolean matches(PDU pdu) {
    if (pdu == null)
      return (false);
    if (this.getSource() != pdu.getSource())	// entities must match
      return (false);
    TCPMessage tcpdu = (TCPMessage) pdu;
    // if both segments have valid ACK numbers, check that they match
    if (this.isAck() && tcpdu.isAck() && this.ack != tcpdu.ack)
      return (false);
    return (this.seq == tcpdu.seq && this.flags == tcpdu.flags);
  }

  /**

    Set the flags attribute of the TCPMessage object
  */
  private void setFlags() {
    int f = flags;
    finFlag = f % 2 > 0;
    f /= 2;
    synFlag = f % 2 > 0;
    f /= 2;
    rstFlag = f % 2 > 0;
    f /= 2;
    pshFlag = f % 2 > 0;
    f /= 2;
    ackFlag = f % 2 > 0;
    f /= 2;
    urgFlag = f % 2 > 0;
  }

  /**
    Set the window size of a TCP message.

    @param win		window size
  */
  public void setWindowSize(int win) {
    if (win >= 0)
      this.win = win;
  }

  /**
    Convert a PDU to a string representation.

    @return		string representation of a PDU
 */
  public String toString() {
    return ("PDU <" + getLabel() + ">");
  }

}

