R"""An timed counter class

   Author          : Anders Andersen
\\ Created On      : Thu Apr 22 12:02:06 1999
\\ Last Modified By: Anders Andersen
\\ Last Modified On: Thu Apr 22 14:49:36 1999
\\ Status          : Unknown, Use with caution!

Copyright {\copyright} 1999 Lancaster University, UK and NORUT
Information Technology Ltd., Norway.  See COPYING for details.

This module implements an timed counter class {\aacodefont
TimedCounter}.  This example tries to illustrate how it works (a closer
description is found in the documentation of the class):

\begin{quote}
{\aacodefont >>> c = TimedCounter(30, 3)}\\
{\aacodefont >>> print c.val}\\
{\aacodefont 3}\\
{\aacodefont >>> c.add(2)}\\
{\aacodefont >>> print c.val}\\
{\aacodefont 5}\\
{\aacodefont >>> print c.val~~~~\# 30 seconds later}\\
{\aacodefont 3}
\end{quote}

"""

# We need to do some timing
import time

class TimedCounter:
    R"""A class for timed counters

    This class implements a special type of counters where added and
    subtracted values only are valid in a given period.  A timed
    counter has its period set to 30 (seconds) and 1 is added to it.
    30 seconds later will 1 be subtracted from the counter.  Instances
    of this class have two (public) attributes {\aacodefont val} and
    {\aacodefont period}, the value of the counter and the valid
    period of added and subtracted values respectively.  Two methods
    {\aacodefont add} and {\aacodefont sub} are provided to
    respectively add a value to the counter and to subtract a value
    from the counter.

    """

    def __init__(self, period, val=0):
        R"""Initialise a timed counter

        Set the initial state of a timed counter.  This includes its
        valid period (the {\aacodefont period} argument) and its
        initial value (the optional {\aacodefont val} argument).

        """
        self.__dict__["period"] = period
        self.__dict__["_val"] = val
        self.__dict__["_addlist"] = []

    def add(self, v):
        R"""Add a value to the counter

        Add the value {\aacodefont v} to the counter.  This value will
        automatically be subtracted from the counter after
        {\aacodefont period} seconds.

        """
        self.__dict__["_val"] = self.val + v
        self._addlist.append((time.time()+self.period, v))
        
    def sub(self, v):
        R"""Subtract a value to the counter

        Subtract the value {\aacodefont v} from the counter.  This
        value will automatically be added to the counter after
        {\aacodefont period} seconds.

        """
        self.add(-v)

    def __getattr__(self, key):
        R"""Fetching the counter value

        The counter value is stored in the attribute {\aacodefont
        val}.  The trick is that this attribute doesn't exists in the
        object and every attempt to read it will end up calling this
        method.  This method returns the value, but it also removes
        any additions and subtractions that are not valid any more.

        """
        if key == "val":
            c = time.time(); n = 0
            for (t,v) in self._addlist:
                if t > c:
                    break
                else:
                    n = n + 1
                    self.__dict__["_val"] = self._val - v
            self.__dict__["_addlist"] = self._addlist[n:]
            return self._val
        else:
            raise AttributeError, key

    def __setattr__(self, key, v):
        R"""Change attribute value

        You can change the value of {\aacodefont val} and {\aacodefont
        period}, but any attempt to change the value of other
        attributes will generate an {\aacodefont AttributeError}.

        """
        if key == "val":
            self.__dict__["_val"] = v
        elif key == "period":
            self.__dict__["period"] = v
        else:
            raise AttributeError, key
