"""
<Purpose>
  A client uses these methods to figure out its correct 
  congestion window.

<Usage>
  CongestionController() - new obj

  Public Methods:
     update(unacked) - Called after a timeout or the server ACKs all packets.
       If a timeout meaning unacked packets exist, resets congestion window.  
       Otherwise, keeps increasing congestion window.

     incr() - Called when an ACK comes in.  Increases the window by slow start
       or avoidance depending on stage.

<Example>
  include congestion_controller.repy 

  # Account for ACK
  def callback(ip, port, mess, ch):
    pack = unpack(mess)
    if pack.is_ack():
      mycontext['num_acks_left'] -= 1
      mycontext['contr'].incr()
    
  # Update congestion window
  def check():
    mycontext['contr'].update(mycontext['num_acks_left'])

  def main():
    mycontext['contr'] = CongestionController()
    mycontext['num_acks_left'] = 10
    recvmess(getmyip(), 12345, callback

    # check to see if all ACK'd after 10 seconds
    settimer(10, check, ())

  if callfunc == 'initialize':
    main()
"""

include constants.repy

class CongestionController():

  def __init__(self):
    # Congestion window -- slow start
    self.window_size = CONG_WINDOW_SIZE # counts up on ACK's
    self.threshold = None # starts cong avoidance
    self.increment = None # increment for cong avoidance

  def update(self, unacked):
    """
    <Purpose>
      When the client times out or receives ACKs for all its packets,
      the client calls this function to update its congestion window
      for the next round of sending.
    
    <Arguments>
      unacked - True if the client timed out.  Otherwise False.

    <Side Effects>
      Sets all variable to new correct state based on unacked.
    """
    # Reset if any unacked packets.
    if unacked: 
      self.threshold = int(self.window_size * 1.0 / 2.0)
      self.increment = (CONG_WINDOW_SIZE * CONG_WINDOW_SIZE * 1.0)/ (self.threshold * 1.0)
      self.window_size = CONG_WINDOW_SIZE

    # Congestion Avoidance?
    elif self.threshold and self.window_size >= self.threshold:
      self.window_size = int(self.window_size)
      self.increment = (CONG_WINDOW_SIZE * CONG_WINDOW_SIZE * 1.0)/ (self.window_size * 1.0)

    # Slow Start
    else:
      pass

  def incr(self):
    """
    <Purpose>
      If the client receives a valid ACK, the client calls this
      function to increase the congestion window.
 
    <Side Effects>
      Increments window size.
    """

    # congestion avoidance
    if self.threshold and self.window_size >= self.threshold:
      self.window_size += self.increment

    # Slow-Start     
    else:
      self.window_size += CONG_WINDOW_SIZE

