# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 Fluendo Embedded S.L. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.


__maintainer__ = 'Florian Boucault <florian@fluendo.com>'

import weakref

from elisa.core import log

class Observable(log.Loggable):
    """Observable objects can be observed by multiple
    L{elisa.core.observers.observer.Observer} instances, each of them
    getting notified of any changes occuring to the former.
    """

    # weak references to the observers
    _observers = []

    def __init__(self):
        super(Observable, self).__init__()
        self._observers = []

    def add_observer(self, observer):
        """Attach an observer which will then be notified of all the changes
        applied to the observable.

        @param observer: observer to attach
        @type observer:  L{elisa.core.observers.observer.Observer}
        """
        self.debug("Adding observer %r" % observer)
        real_observers = [weak_observer() for weak_observer in self._observers]
        if observer not in real_observers:
            self._observers.append(weakref.ref(observer))
        else:
            self.debug("Observer %r was already observing" % observer)

    def remove_observer(self, observer):
        """Detach an observer which will not be notified anymore of changes
        applied to the observable.

        @param observer: observer to detach
        @type observer:  L{elisa.core.observers.observer.Observer}
        """
        self.debug("Removing observer %r", observer)
        refs = weakref.getweakrefs(observer)
        for ref in refs:
            try:
                self._observers.remove(ref)
            except ValueError:
                pass
        """
        # ignore the call if the observer was not known
        self.debug("Observer %r was not observing", observer)
        """

    def __setattr__(self, key, new_value):
        try:
            old_value = getattr(self, key)
        except:
            old_value = None

        # setattr() not usable because it would result in a recursion loop
        super(Observable, self).__setattr__(key, new_value)

        # ignore private attributes (those starting with '_')
        if key[0] != '_':
            # notifies all the attached observers
            self._send_message_to_observers("attribute_set", self, key,
                                            old_value, new_value)

    def _send_message_to_observers(self, message, *args):
        for weak_observer in self._observers:
            observer = weak_observer()
            if observer == None:
                continue
            else:
                getattr(observer, message)(*args)
