#
#
# $Id: state.py,v 1.1.1.1 2003/03/22 05:15:52 dbelan2 Exp $
#
#
# $Log: state.py,v $
# Revision 1.1.1.1  2003/03/22 05:15:52  dbelan2
# Initial import of all public_html on www.cs.mcgill.ca.
#
# Revision 1.5  2001/12/04 04:15:31  dbelan2
# Removed debugging output.
#
# Revision 1.4  2001/12/04 02:17:32  dbelan2
# Queue bug fixed.
#
# Revision 1.12  2001/12/04 02:01:17  dbelan2
# Fixed queue.getAvgLen() and successfully run the
# fixture that tests it.
#
# Revision 1.3  2001/12/03 04:57:30  hchen19
# get rid of one output.
#
# Revision 1.2  2001/12/03 03:19:48  dbelan2
# avg queue metric added but not debugged.
#
# Revision 1.1  2001/12/03 02:20:48  dbelan2
# Added state module from ES/AS project.
#
# Revision 1.11  2001/11/23 23:59:15  hchen19
# Performance metrics implemented, but not tested.
#
# Revision 1.10  2001/11/22 03:20:55  dbelan2
# Implemented a few methods quickly in order
# to run simulation.
#
# Revision 1.9  2001/11/22 01:42:26  dbelan2
# Made the API more precise by adding what happens
# when object not found, already there, etc.
#
# Revision 1.8  2001/11/20 20:33:31  dbelan2
# Fixed syntax errors.
# Still not working though.
#
# Revision 1.7  2001/11/17 00:09:46  dbelan2
# Several methods added for QueueVar.
#
# Revision 1.6  2001/11/16 23:11:31  dbelan2
# Fix syntax error.
#
# Revision 1.5  2001/11/16 23:08:08  dbelan2
# Added $avg for QueueVar.
#
# Revision 1.4  2001/11/16 21:06:47  hchen19
# State class is implemented.
#
# Revision 1.3  2001/11/16 20:29:39  dbelan2
# Add getName() to StateVar and fix a few bugs.
#
# Revision 1.2  2001/11/16 04:28:08  dbelan2
# API first draft written, no implementation.
#
# Revision 1.1  2001/11/16 02:54:17  dbelan2
# API started.
#
#
#

import types

"""
The state module contains all classes related to model
state.

Note: The behaviour of the methods should be consistent
with the python programming language.

Ex: A state is a dictionary data structure (it may or
may not be implemented as a python dictionary though).
Therefore, it should behave the same way as a python
dictionary when an element is not found, when an
element is already there etc.
"""

class State:
    """
    The container of all state variables.

    A state is a dictionary data structures.
    """

    def __init__(self):
        """
        Construct an empty state.
        """
        self.vars={}
    
    def add(self, statevar):
        """
        Adds a state variable object to the state.

        If a state variable with the same name already exists,
        the old *StateVar* object is replaced by the new one.
        """
        self.vars[statevar.getName()]=statevar
        statevar.setState(self)


    def remove(self, var):
        """
        Removes a state variable.

        *var* can be either its name (as a string) or a
        *StateVar* object.
        
        If not found, a KeyError is thrown.
        """
        del self.vars[var.getName()]
                       
    def isDef(self, name):
        """
        Returns 1 if there is a state variable with
        the specified name.  Otherwise, returns 0.
        """
        return self.vars.has_key(name)

    def getStateVar(self, name):
        """
        Returns the *StateVar* object with name *name*.

        If not found, a *KeyError* is thrown.
        """
        return self.vars[name]

    def getAllVars(self):
        """
        Returns a list of all *StateVar* objects.
        """
        #return self.vars.keys()
        return self.vars.values()

    # Next 2 are for direct access to values.
    # Do we really need this accessor and modifier.
    def getValue(self, name):
        """
        Returns the value of the variable with the
        specified name.

        If not found, a *KeyError* is thrown.
        """
        return self.vars[name].getValue()

    def setValue(self, name, value):
        """
        Sets the *StateVar* specified by *name* to the
        given *value*.

        If no variables have name *name*, a KeyError is
        thrown.
        """
        self.vars[name].setValue(value)


class StateVar:

    #self.name = None
    """
    The name.  (Should be "protected" if possible)
    """

    #self.value = None
    """
    The value.  (protected)
    """

    def __init__(self, name, value = None):
        """
        Construct a new state variable with the specified
        name.  If a *value* is given, it will be the default
        value of that variable.
        """
        self.name = name
        self.defaultvalue = value
        self.value = value

    def setState(self, state):
        self.state = state

    def getState(self, state):
        return self.state

    def getName(self):
        """
        Returns the variable name.
        """
        return self.name
        
    def getValue(self):
        """
        Returns the value.
        """
        return self.value

    def setValue(self, value):
        """
        Sets the value.  The value can be of any type.
        """
        self.value = value

    def reset(self):
        """
        Resets the value to the default value (provided
        in constructor).
        """
        self.value = self.defaultvalue

class Utilization(StateVar):
    """
    Utilization of one entity is a type of performance metrics.
    value is flag of state of entity. 0 means idle. 1 means busy.
    """
    def __init__(self, name, value, startT=0):
        StateVar.__init__(self, name, value)
        self.prevtime=None
        self.totalusetime=0
        self.starttime=startT
        self.busy=value

    def getFacility(self):
        self.busy=1
        self.prevtime=self.state.getValue("current_time")

    def releaseFacility(self):
        self.busy=0
        currtime=self.state.getValue("current_time")            
        if self.starttime <= currtime:
            if self.starttime >= self.prevtime:
                self.totalusetime=self.totalusetime+(currtime-self.starttime)
            else:
                self.totalusetime=self.totalusetime+(currtime-self.prevtime)

    def isBusy(self):
        return self.busy

    def isIdle(self):
        return self.busy

    def getUtilization(self):
        currtime=self.state.getValue("current_time")
        if(self.starttime<=currtime):
            if (self.busy):
                sum=self.totalusetime+(self.prevtime-self.state.getValue("current_time"))
                return sum/(self.state.getValue("current_time")-self.starttime)
            else:
                return self.totalusetime/(self.state.getValue("current_time")-self.starttime)
        else:
            return 0

class AvgUseNOccupancy(StateVar):
    """
    Average Use and Occupancy is a type of performance metrics.
    """
    def __init__(self, name, value=0, startT=0):
        StateVar.__init__(self, name, value)
        self.starttime=startT
        self.prevtime=startT
        self.totaluse=0
        self.maxoccupancysofar=0;
        self.value=value
        
    def setValue(self, value):
        currtime=self.state.getValue("current_time")

        #print "----------- curr - prev", currtime, self.prevtime

        if self.starttime <= currtime:
            self.totaluse=self.totaluse+self.value*(currtime - self.prevtime)
            
            if value > self.maxoccupancysofar:
                self.maxoccupancysofar=value

            self.prevtime=currtime

        #print "--------------- totaluse", self.totaluse

        self.value=value;

    def getAvgUse(self):
        currtime=self.state.getValue("current_time")
        if self.starttime<currtime:
            self.totaluse=self.totaluse + self.value * \
                           (currtime - self.prevtime)
            self.prevtime=currtime
            
            #print "--------------- totaluse", self.totaluse
            # +0.0 only to ensure real division (not int division)
            # is performed.
            return self.totaluse/(currtime-self.starttime+0.0)
        else:
            return 0

    def getMaxOccupancy(self):
        return self.maxoccupancysofar

    def getAvgOccupancy(self):
        return (self.totaluse/(self.simulator.getEndTime() - \
                               self.simulator.getStartTime()))/self.maxoccupancysofar

class QueueVar(AvgUseNOccupancy):
    """
    A *QueueVar* automagically keeps track of the average
    queue length.  It should be used for queue variables
    if that performance metric is needed.  If not needed,
    using the regular *QueueVar* will be more efficient.


    Note: It should be an error if queue var is set to
    a negative value or non integer (ex: -2, 3.5)
    """

    def __init__(self, name, value = 0):
        """
        Construct a new queue variable.  An *InvalidValueError*
        is raised if value is invalid (neg. or non-integer value).
        Default value is 0 (Note: it is *None* for the more
        general *StateVar*).
        """
        AvgUseNOccupancy.__init__(self, name, value)

        if value < 0:
            raise InvalidValueError(value)
        
        #self.name = name
        #self.value = value
        

    #def setValue(self, value):
    #    """
    #    Overrides *StateVar.setValue()*.
    #    Sets the value.#

    #    InvalidValueError if negative value used.
    #    """
    #    # implementation note
    #   # need to update metric.
    #    self.setValue(self, value)

    def setValue(self, value):
        if value < 0 or type(value) != types.IntType:
            raise InvalidValueError(value)
        else:
            return AvgUseNOccupancy.setValue(self, value)

    def enqueue(self):
        """
        Increments the queue length by 1.
        Equivalent to *q.setValue(q.getValue()+1)*
        """
        self.setValue(self.getValue() + 1)

    def dequeue(self):
        """
        Decrements the queue length by 1.
        An EmptyQueueError is raised if it is invoked
        on an empty queue.
        """
        # need to code for exception

        if self.isEmpty():
            raise EmptyQueueError
        else:
            self.setValue(self.getValue() - 1)

    def isEmpty(self):
        """
        Check queue emptyness.  Returns 1 if empty, 0 otherwise.
        """
        return self.getValue() == 0

    def getAvgLen(self):
        """
        Returns the average queue length.
        """
        return self.getAvgUse()

    def getMaxLen(self):
        return self.getMaxOccupancy()

    def getDollarAvgLen(self):
        """
        Returns the average length without including the zero
        length in the count.
        """
        pass
    

class ResourceVar(StateVar):
    """
    A *ResourceVar* automagically keeps track of the
    utilization of a resource.
    """

    def setValue(self, value):
        """
        Overrides *StateVar.setValue()*.
        Sets the value.
        """
        # implementation note
        # need to update metric.
        StateVar.setValue(self, value)
        

    def getUsage(self):
        """
        Returns the utilization, a real number between 0.0 and 1.0.
        """
        pass




# exceptions classes

class InvalidValueError:

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return `self.value`



class EmptyQueueError:
    # no implementation required
    pass




