##########################################################################################
##                                                                                      ##
##                                  Market Simulation                                   ##
##                                                                                      ##
##########################################################################################

import random
import os.path

def shouldIAddCustomer( percent ):
    """This function returns True with a certain probability.  For
      example, if percent is 35, then this returns True 35% of the 
      times you call it and returns False 65% of the time. This is 
      a useful trick in a simulation. """
    return random.randint(0, 99) < percent

def generateEventsFile( N, perc, maxItems, fileName ):
    """Create a file of N lines to drive the simulation.  Each line
      in the file Contains a single positive integer in range
      [0...maxItems].  Lines are non-zero perc percentage of the
      time.  The idea is that if a line is zero, no new customer has
      arrived during that clock tick.  Otherwise, a new customer has
      arrived at the cashier line with that many items in their
      basket.

    """ 
    # Open the output file for writing.  If the file exists, it
    # will be overwritten.
    eventsFile = open( fileName, "w" )

    # The file is going to contain N lines.
    for i in range(N):
        # Each line contains a zero or a randomly generated positive
        # integer up to maxItems.  It's non-zero perc percent of the 
        # time. 
        val = ( random.randint(1, maxItems ) if shouldIAddCustomer( perc ) else 0 )

        # Write the newly generated value into the file, one value per line.
        eventsFile.write( str(val) + "\n" ) 

    # Be sure to close the file.
    eventsFile.close()

def generateEventListFromFile( filename ):
    """Given a file, probably generated by the function generateEventsFile,
       read values from the file one line at a time and store those values
       on a list.  It's that list that will be used to drive the simulation.
    """

    # Check that the file exists;  if not, print an error message and leave.
    if not os.path.isfile( filename ):
        print( "File", filename, "does not exist.")
        return

    # If the file exists, open it for reading
    infile = open(filename, "r")

    # Read each line from the file and add to an initially empty list.
    eventsList = []
    line = infile.readline()
    while line:
        eventsList.append( int(line) )
        line = infile.readline()
    
    # Close the file and return the list.
    infile.close()
    return eventsList

class Customer:

    """A customer is generated with a random number of items in his
    basket.  We assume it takes a cashier 1 tick to process each
    item.

    """

    def __init__(self, custNum, itemCount):
        """A new customer is assigned a customer number and also a number of
        items in their basket, assigned randomly up to maxItems.  The
        variable __itemsCount indicates not only the number of items
        in the customers basket, but also the time to check the
        customer out, since we assume that the cashier can process one
        item per tick of the simulator clock.

        """
        self.__customerNumber = custNum
        self.__itemsCount = itemCount

    def getCustomerNumber(self):
        return self.__customerNumber

    def getItemsCount(self):
        return self.__itemsCount

    def decrementItemsCount(self):
        """On each tick, the cashier rings up one item from the
        basket of the customer at the head of the line.""" 
        self.__itemsCount -= 1

    def customerFinished(self):
        """Boolean function indicating that this first customer
        in line will depart on the next tick."""
        return self.__itemsCount < 1

    def __str__(self):
        """If this is customer n with k items in basket,
        print 'Cn(k)' """
        return "C" + str( self.getCustomerNumber() ) + \
               "(" + str( self.getItemsCount() ) + ")" 

class CheckoutLine:
    """A checkout counter is just a list with customers added at the front
    (L[0]) and removed from the rear (L[-1]).  Customers enter and
    move through the line.  At each tick, one item is removed from the
    basket of the first customer in line.  When their basket becomes
    empty, the first customer pays and departs the line.

    """

    def __init__(self):
        """Open a new line, with no customers initially."""
        self.__line = []
    
    def customerJoinsLine(self, cust):
        """Add a new customer at the rear of the line."""
        self.__line.insert(0, cust)
        print( "Customer C", cust.getCustomerNumber(), " joining line.", sep="" )

    def customerLeavesLine(self):
        """The first customer in line departs."""
        cust = self.__line.pop()
        print( "Customer C", cust.getCustomerNumber(), " leaving line.", sep="" )

    def firstInLine(self):
        """Return the first customer in the line."""
        if not self.__line:
            print("Line is empty")
        else:
            return self.__line[-1]

    def advanceLine( self ):
        # Can't advance unless line is not empty.
        # If it's empty, don't do anything.
        if len( self.__line ) != 0:
            # Get the first customer in line.
            firstCustomer = self.firstInLine()
            # If the first customer has one item then
            # scan that item and that customer will leave. 
            firstCustomer.decrementItemsCount()
            if firstCustomer.customerFinished():
                self.customerLeavesLine()

    def __str__(self):
        s = '[ '
        for cust in self.__line:
            s = s + str( cust ) + " "
        return "   Line: " + s + "]"

    def __len__(self):
        return len(self.__line)

def simulateCheckoutLine( eventList ):
    """ This is the driver program for this system.  We monitor the
        progress of customers in the checkout line.  The eventList
        decides when a new customer is added with how many items
        in their cart. Customers are numbered as they enter. At each 
        tick of the simulator clock (each new item in eventList),
        the cashier processes one item in the basket of the first customer.

    """
    print( "Simulating a simple market, with one cashier.")

    # We'll number the customers as they arrive.
    custNumber = 1

    # And we'll need a cashierLine to add them to.
    cashierLine = CheckoutLine()
    
    stepCount = 1
    for itemCount in eventList:
        print("\nStep:", stepCount)
        stepCount += 1

        # Advance the line, unless it's empty.
        cashierLine.advanceLine( )

        if itemCount != 0:
            # Create a new customer.
            newCustomer = Customer( custNumber, itemCount  )
            custNumber += 1

            # Add the new customer to the line.
            cashierLine.customerJoinsLine( newCustomer )

        # Finally, print the new state of the Market. 
        print( cashierLine )

def main():
    # Accept a filename from the user.
    filename = input("Enter a filename: ").strip()

    # Populate the file with 10 events (integers).  Approximately
    # 50 percent are non-zero.  Each is between [1..7] (items).
    generateEventsFile( 10, 50, 7, filename )
    
    # From the file, generate a list of events (integers)
    eventlist = generateEventListFromFile( filename )
    print("The eventlist:", eventlist)

    # Use the event list to simulate the market behavior.
    simulateCheckoutLine( eventlist )

# Be sure to comment this out before submitting.
# main()
