"""$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/history.py $
$Id: history.py 25472 2004-10-27 14:43:51Z dbinger $
"""

from datetime import datetime
from dulcinea.base import DulcineaBase, DulcineaPersistent
from dulcinea.spec import any
from dulcinea.typeutils import typecheck
from dulcinea.user import DulcineaUser

class Event (DulcineaBase):
    """A single event in a history log.

    Instance attributes:
      timestamp : datetime
      user : DulcineaUser
      event_code : string
      message : string

    Event objects should never be created by outside code:
    always go through the 'add_event()' method of History.
    """

    timestamp_is = datetime
    user_is = any(DulcineaUser, None)
    event_code_is = str
    message_is = str

    def __init__(self, user, event_code, message):
        typecheck(user, DulcineaUser, allow_none=1)
        self.timestamp = datetime.now()
        self.user = user
        self.event_code = event_code
        self.message = message

    def __str__(self):
        return "%s %s" % (self.timestamp, self.event_code)

    def get_timestamp(self):
        return self.timestamp

    def get_user(self):
        return self.user

    def get_event_code(self):
        return self.event_code

    def get_message(self):
        return self.message


class History (DulcineaPersistent):
    """A list of history events attached to a single object.

    Instance attributes:
      events : [Event]
        the list of events that comprise the object's history
    """

    events_is = [Event]

    def __init__(self):
        self.events = []

    def __str__(self):
        return "history (%d events)" % len(self.events)

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

    def __getitem__(self, i):
        return self.events[i]

    def add_event(self, user, event_code, message):
        """(user : DulcineaUser, event_code : string, message : string)

        Create and append a new Event instance. 
        """
        new_event = Event(user, event_code, message)
        self.events.append(new_event)
        self._p_changed = 1

    def update_last_event(self, user, event_code, message):
        """(user : DulcineaUser, event_code : string, message : string)

        Create a new Event instance. If the last event on the list had
        the same event code and user, replace the last event with the
        new one.
        """
        if self.events:
            if (self.events[-1].event_code == event_code and
                self.events[-1].user == user):
                self.events = self.events[:-1]
        self.events.append(Event(user, event_code, message))
        self._p_changed = 1

    def find_event(self, user=None, event_code=None, event_codes=None,
                   latest=False, message=None):
        """(user : DulcineaUser = None,
            event_code : string = None,
            latest : bool = False,
            message : string = None) -> Event

        Find the earliest(or latest, if latest is true) event
        matching 'user' and/or 'event_code'.  If either search criterion
        is not supplied or None, it does not affect the search, thus the
        default behaviour is to return the first event in the history
        list.  Return None if no events match.
        """
        typecheck(user, DulcineaUser, allow_none=1)
        if latest:
            indices = xrange(len(self.events)-1, -1, -1)
        else:
            indices = xrange(len(self.events))
        for i in indices:
            event = self.events[i]
            if user and event.user is not user:
                continue
            if event_code is not None and event.event_code != event_code:
                continue
            if event_codes is not None and event.event_code not in event_codes:
                continue
            if message is not None and event.message != message:
                continue
            return event
        else:
            return None

    def find_events_after(self, time, event_code=None):
        return [event
                for event in self.events
                if event.get_timestamp() > time and (
                    event_code is None or
                    event.get_event_code() == event_code)]

    def find_last_date(self, event_code):
        """(event_code:str) -> datetime | None"""
        event = self.find_event(event_code=event_code, latest=True)
        return event and event.get_timestamp()


class Historical:
    """maintains a list of history events.

    Instance attributes:
      history : History
        the list of history events that comprise the object's history

    """

    history_is = History

    def __init__(self):
        self.history = History()

    def get_history(self):
        return self.history
