from DEVS import *
from Simulator import *

from constant import *

from state import *  # state module from our project.

import sys


__DEBUG__ = 1  # turn on/off additional output


"""
Represents a road segment before the road about.
"""

class Segment(AtomicDEVS):  


  def __init__(self, traveltime = 5 * 60, name = ""):
    """
    Constructs a new road Segment.
    
    traveltime - time to get to other end of segment in seconds.
    name       - a descriptive name used in debugging output.
    """

    # Always call parent class' constructor FIRST:
    AtomicDEVS.__init__(self)

    # set up fields
    self.traveltime = traveltime
    self.name = name

    #
    # state vars for performance metrics
    #
    # Classes come from our ES/AS project where the
    # *current_time* state var is maintained by our
    # simulator.  To use the QueueVar in the context
    # of this assignment, we need to maintain also
    # current_time.
    #
    #
    self.state = State()
    self.currtime = StateVar("current_time", 0)
    self.queuevar = QueueVar("queue")
    self.state.add(self.currtime)
    self.state.add(self.queuevar)


    # Cars that are currently on the segment
    # Timelefts contains the time remaining to get to
    # the roundabout, negative values are to be interpreted
    # as a positive waiting time.
    self.cars = []
    self.timelefts = []

    # TOTAL STATE:
    # field 1: boolean - at least one car ready to go (if roundabout available)
    # field 2: boolean - a car coming from the left quadrant
    # field 3: boolean - right quadrant is full
    self.state = [NOWAIT, EMPTY, NOTFULL]

    # ELAPSED TIME:
    #  Initialize 'elapsed time' attribute if required
    #  (by default, value is 0.0):
    #
    # The default is good.
    #self.elapsed = <X>
    
    
    # PORTS:
    #  Declare as many input and output ports as desired
    #  (usually store returned references in local variables):
    self.IN = self.addInPort()       # incoming cars from gen
    self.LeftSeg = self.addInPort()  # seg to check for cars
    self.RightSeg = self.addInPort() # seg to check for full
    self.OUT = self.addOutPort()     # outputting cars on segment

  ###
  def extTransition(self):
    """External Transition Function."""
    
    # Compute the new state 'Snew' based (typically) on current
    # State, Elapsed timeparameters and calls to 'self.peek(self.IN)'.
    #return Snew

    if __DEBUG__: 
      print "Before"
      print self


    # update current time state var
    self.currtime.setValue(self.currtime.getValue() + self.elapsed)

    # update timelefts
    for i in range(len(self.timelefts)):
      deltaT = self.elapsed
      if self.timelefts[i] > 0 and self.timelefts[i] <= deltaT:
        self.queuevar.enqueue()  # one more waiting
      self.timelefts[i] = self.timelefts[i] - deltaT

    # compute wait flag
    waitflag = NOWAIT
    if len(self.timelefts) > 0:
      if self.timelefts[0] <= 0:
        waitflag = WAIT

    # get input
    car = self.peek(self.IN)
    acar = self.peek(self.LeftSeg)
    full = self.peek(self.RightSeg)


    # for debugging
    if __DEBUG__:
      print "><<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<", \
            car, acar, full

    # compute new state
    if car != None:  # new car added on segment
      self.cars.append(car)
      self.timelefts.append(self.traveltime)
      newS = [waitflag, self.state[1], self.state[2]]
    elif acar != None:  # emptiness status changed
      newS = [waitflag, acar, self.state[2]]
    elif full != None:  # fullness status changed
      newS = [waitflag, self.state[1], full]

    # debugging output
    if __DEBUG__:
      print "After"
      print self
      self.printQuads()

    return newS


    
  ###
  def intTransition(self):
    """Internal Transition Function.
    """

    # update current time state var
    self.currtime.setValue(self.timeNext)
    

    if self.state == [WAIT, EMPTY, NOTFULL]:
      # remove car that was outputted
      self.queuevar.dequeue()
      self.cars[0].setQueueTime(-self.timelefts[0])
      del self.cars[0]
      del self.timelefts[0]


    # update time lefts
    for i in range(len(self.timelefts)):
      deltaT = self.timeNext - self.timeLast
      if self.timelefts[i] > 0 and self.timelefts[i] <= deltaT:
        self.queuevar.enqueue()  # one more car waiting
      self.timelefts[i] = self.timelefts[i] - deltaT


    # compute wait flag
    waitflag = NOWAIT
    if len(self.timelefts) > 0:
      if self.timelefts[0] <= 0:
        waitflag = WAIT


    # compute new state
    #
    # first if condition: if the internal transition was
    #   triggered when in state [NOWAIT, *, *] the only
    #   possible cause of this internal transition is
    #   the arrival of a car to the roundabout.  So
    #   the state is changed to [WAIT, *, *]
    #
    # second if condition: if a car was waiting and
    #   the intersection was EMPTY and NOTFULL then
    #   the car waiting went into the roundabout.
    #   The WAIT flag value depends if there is another
    #   car waiting at the roundabout.
    #
    # else: impossible that an internal transition was
    #   triggered when in other states.
    #
    # Note that empty flag and full flag are changed ONLY
    # by external transition.
    #
    newS = self.state
    if self.state[0] == NOWAIT:
      newS[0] = WAIT
    elif self.state == [WAIT, EMPTY, NOTFULL]:
      newS[0] = waitflag
    else:
      print ">>>> error Segment intTrans"
      sys.exit()


    if __DEBUG__:
      print self
      self.printQuads()

    return newS    

  
  ###
  def outputFnc(self):
    """Output Funtion.
    """
    
    # Send messages (events) to a subset of the atomic-DEVS' 
    # output ports by means of the 'poke' method, i.e.:
    # The content of the messages is based (typically) on current State.

    if self.state == [WAIT, EMPTY, NOTFULL]:
      # send car on quadrant
      self.poke(self.OUT, self.cars[0])

      
  ###
  def timeAdvance(self):
    """Time-Advance Function.
    """
    
    # Compute 'ta', the time to the next scheduled internal transition,
    # based (typically) on current State.

    if self.state == [WAIT, EMPTY, NOTFULL]:
      return 0                   # car ready to go right now
    elif self.state[0] == 0 and len(self.timelefts) > 0:
      return self.timelefts[0]   # minimum time left
    else:
      return INFINITY            # no internal transition in view
    

  ###
  def getAvgUse(self,endtime):
    """
    Returns the average queue length so far.

    endtime - time when the simulation ended.  Needed to
              compute last interval.
    """
    self.currtime.setValue(endtime)
    return self.queuevar.getAvgUse()


    
  def __str__(self):
    """
    Returns a string that describes the current content
    of the segment.
    """
    s = ">>>>>>>>"
    s += "segment: " + self.name + "  queue size: " + \
         `self.queuevar.getValue()` + "\n"
    for i in range(len(self.cars)):
      s += str(self.cars[i]) + "<" + `self.timelefts[i]` + ">\n"
    s += "<<<<<<<<<<"
    return s


  # prints some debugging output
  def printQuads(self):
    for item in Segment.quads:
      print item



####### End of segment.py #######







