# PyBridge -- online contract bridge made easy.
# Copyright (C) 2004-2007 PyBridge Project.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.


from twisted.internet import reactor
from twisted.spread import pb
from zope.interface import implements

from pybridge.interfaces.observer import ISubject, IListener




class Observable(object):  # object
    """A generic implementation of ISubject."""

    implements(ISubject)


    def __init__(self):
        super(Observable, self).__init__()
        print "observable"
        self.listeners = []


    def attach(self, listener):
        self.listeners.append(listener)


    def detach(self, listener):
        self.listeners.remove(listener)


    def notify(self, event, *args, **kwargs):
        for listener in self.listeners:
            listener.update(event, *args, **kwargs)




class LocalObservable(Observable, pb.Cacheable):
    """An extension of Observable, which relays its notifications to clients
    by means of communication with registered RemoteObservable objects.
    
    Subclasses of LocalObservable may provide the 'master copy' of a state
    machine, generating notifications to reflect state changes.
    """


    def __init__(self):
        super(LocalObservable, self).__init__()
        print "local observable"
        self.observers = {}  # Mapping from perspectives to observers.


    def getState(self):
        """Return an object representing the state of the observable.
        
        @return: a state object.
        """
        raise NotImplementedError, "Subclass must override getState()"


    def getStateToCacheAndObserveFor(self, perspective, observer):
        self.observers[perspective] = observer
        return self.getState()  # Return state to RemoteObservable.


    def stoppedObserving(self, perspective, observer):
        del self.observers[perspective]


    def notify(self, event, *args, **kwargs):
        #Observable.notify(self, event, *args, **kwargs)
        super(LocalObservable, self).notify(event, *args, **kwargs)

        # Override to provide event notification for remote observers.
        for observer in self.observers.values():
            # Event handlers are called on the next iteration of the reactor,
            # to allow the caller of this method to return a result.
            reactor.callLater(0, observer.callRemote, event, *args, **kwargs)




class RemoteObservable(Observable, pb.RemoteCache):
    """An extension of Observable, which receives the notifications received
    from a LocalObservable object and relays them to its own listeners.
    
    Subclasses of RemoteObservable may provide a local 'mirror copy' of a state
    machine encapsulated by a remote LocalObservable.
    """

    implements(IListener)


    def setState(self, state):
        raise NotImplementedError, "Subclass must override setState()"


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


    def update(self, event, *args, **kwargs):
        # This method is only expected to be invoked by observe_update().
        self.notify(self, event, *args, **kwargs)


    def observe_update(self, event, *args, **kwargs):
        """Receives remote notifications and relays them to local update()."""
        self.update(event, *args, **kwargs)

