"""
  <Description>
    Structures UDP payloads into TCP packet objects.

  <Usage>
    Static methods:
      unpack(message) - decodes message into packet
   
    Class methods:
      TcpPacket() - new Packet constructor
      packet.pack() - encode into string UDP message
      packet.copy() - clone a duplicate of the packet

    Boolean check methods: 
      packet.is_syn()
      packet.is_synack()
      packet.is_ack()
      packet.is_payload()
      packet.is_fin()

    Attributes:
      packet.srcport - source port
      packet.destport - destination port
      packet.seq_num - sequence number
      packet.ack_num - acknowledgment number
      packet.control_bits - the type (ex. ACK, FIN, etc.)
      packet.window = advertized window size in bytes
      packet.payload - data

  <TCP Header Format>

    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                            TCP Header Format

          Note that one tick mark represents one bit position.

                               Figure 3.
"""
# Keeps track of our packet.
class TcpPacket:
  """
  <Example Use>
    pack = TcpPacket(12345, 12356, 2, 0, "SYN", 100, "will you connect with me?")
    sendmess(124.3.4.1, 12346, pack.pack()) 
  """

  def __init__(self, srcport, destport, seq_num, ack_num, control_bits, window, payload):
    """
    <Purpose>
      Assign attributes.      
    
    <Arguments>
      packet.srcport - source port
      packet.destport - destination port
      packet.seq_num - sequence number
      packet.ack_num - acknowledgment number
      packet.control_bits - the type (ex. ACK, FIN, etc.)
      packet.window = advertized window size in bytes
      packet.payload - data      
    
    <Exceptions>
      None
         
    <Side Effects>
      Type casts numbers to integers.

    <Returns>
      The object packet
    """
    
    self.srcport = int(srcport)
    self.destport = int(destport)
    self.seq_num = int(seq_num)
    self.ack_num = int(ack_num)
    self.control_bits = control_bits
    self.window = int(window)
    self.payload = payload

  def is_ack(self):
    """
    <Purpose>
      What type of packet is this one?      
    
    <Returns>
      Returns true if this packet has control bits of an ack.
      Otherwise, false.      
    """
    return self.control_bits == "ACK"


  def is_syn(self):
    """
    <Purpose>
      What type of packet is this one?      
    
    <Returns>
      Returns true if this packet has control bits of a syn.
      Otherwise, false.
    """
    return self.control_bits == "SYN"

  def is_synack(self):
    """
    <Purpose>
      What type of packet is this one?      
      
    <Returns>
      Returns true if this packet has control bits of an syn, ack.
      Otherwise, false.
    """
    return self.control_bits == "SYNACK"

  def is_payload(self):
    """
    <Purpose>
      What type of packet is this one?      

    <Returns>
      Returns true if this packet has control bits of a payload packet.
      Otherwise, false.
    """
    return self.control_bits == "PSH"

  def is_fin(self):
    """
    <Purpose>
      What type of packet is this one?      

    <Returns>
      Returns true if this packet has control bits of a FIN packet.
      Otherwise, false.
    """
    return self.control_bits == "FIN"

  def is_finack(self):
    """
    <Purpose>
      What type of packet is this one?      

    <Returns>
      Returns true if this packet has control bits of a FINACK packet.
      Otherwise, false.
    """
    return self.control_bits == "FINACK"


  def pack(self):
    """
    <Purpose>
      Pack up into Tcp header format.
      BUG: Need to encode in bits
      
    <Side Effects>
      Uses tildas as delimiters.
     
    <Returns>
      Returns encoded message.
    """
    
    return "%d~%d~%d~%d~%s~%d~%s" % (self.srcport, self.destport, \
                                self.seq_num, self.ack_num, self.control_bits, \
                                self.window, self.payload)


  def copy(self):
    """
    <Purpose>
      Copy constructor
    
    <Returns>
      Exact duplicate of packet with own memory address.
    """
    return unpack(self.pack())


##############################################################
# Static Methods
##############################################################

def unpack(data):
  """
  <Purpose>
    Decode a TCP packet message into parts.

  <Arguments>
    Encoded packet as string    
    
  <Side Effects>
    Relies on tildas delimiters.
     
  <Returns>
   Returns the packet.
  """
  
  res = data.split("~")
  return TcpPacket(res[0], res[1], res[2], res[3], \
                   res[4], res[5], ''.join(res[6:]))

def tcup_srcport(data):
  """
  <Purpose>
    Decode a TCUP packet message and
    only return the srcport.

  <Arguments>
    Encoded packet as string    
    
  <Side Effects>
    Relies on tildas delimiters.
     
  <Returns>
    Just the port number
  """
  return unpack(data).srcport

def tcup_destport(data):
  """
  <Purpose>
    Decode a TCUP packet message and
    only return the destport.

  <Arguments>
    Encoded packet as string    
    
  <Side Effects>
    Relies on tildas delimiters.
     
  <Returns>
    Just the port number
  """
  return unpack(data).destport
