""" Event.py

Module for dispatching Straw-related events.
"""
__copyright__ = "Copyright (c) 2002-2005 Free Software Foundation, Inc."
__license__ = """ GNU General Public License

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., 59 Temple
Place - Suite 330, Boston, MA 02111-1307, USA. """

import traceback
import sys
import error

_base_cache = dict()

def find_bases(klass):
    def _find_bases(klass, d = None):
        if d is None:
            d = dict()
        for b in klass.__bases__:
            if issubclass(b, BaseSignal):
                d[b] = 1
                _find_bases(b, d)
        return tuple(d.keys())
    bases = _base_cache.get(klass, None)
    if bases is not None:
        return bases
    bases = _find_bases(klass)
    _base_cache[klass] = bases
    return bases

class BaseSignal:
    def __init__(self, sender):
        self.sender = sender
        return

class SignalEmitter:
    recorder = None

    """Base class for signal emitters"""
    def __init__(self):
        self._slots = dict()

    def initialize_slots(self, *signals):
        """Initialize signal slots.
        signals should be a list of signals this emitter is going to send."""
        for s in signals:
            self._slots[s] = dict()
            bs = find_bases(s)
            for k in bs:
                self._slots[k] = dict()

    def signal_connect(self, signal, handler, data=None):
        """Connect a handler to a signal in this emitter, and associate
        data with the callback. This will override previous bindings of
        signal to handler."""
        self._slots[signal][handler] = data
        return

    def signal_disconnect(self, signal, handler):
        """Disconnect handler from signal."""
        self._slots[signal][handler] = None
        del self._slots[signal][handler]
        return

    def emit_signal(self, signal):
        """Emit a signal object. Signal should be an instance of BaseSignal or
        a subclass."""
        sclasses = find_bases(signal.__class__)
        for sc in sclasses + (signal.__class__,):
            for handler, data in self._slots.get(sc, {}).items():
                if data is not None:
                    args = (signal, data)
                else:
                    args = (signal,)
                try:
                    #if self.recorder is not None:
                    #    self.recorder.record(sc, handler)
                    apply(handler, args)
                except:
                    error.log_exc("Caught an exception when trying to "
                                  "call a signal handler for "
                                  "signal %s\n" %
                                  str(signal))
                    traceback.print_exc()

    """
    def start_recording(klass, recorder):
        klass.recorder = recorder
    start_recording = classmethod(start_recording)

    def stop_recording(klass):
        if klass.recorder is not None:
            klass.recorder.stop()
            klass.recorder = None
    stop_recording = classmethod(stop_recording)
    """

# Feed creation and feed list management events

class FeedsChangedSignal(BaseSignal):
    def __init__(self, sender, feed=None):
        BaseSignal.__init__(self, sender)
        self.feed = feed

class FeedsEmptySignal(BaseSignal):
    pass

class FeedsImportedSignal(FeedsChangedSignal):
    """
    A signal for importing huge amount of feeds from OPML.
    """
    def __init__(self, sender, feeds,
                 parent=None, from_sub=False):
        FeedsChangedSignal.__init__(self, sender)
        self.feeds = feeds
        self.category = parent
        self.from_sub = from_sub

class FeedDeletedSignal(FeedsChangedSignal):
    def __init__(self, sender, feed):
        FeedsChangedSignal.__init__(self, sender, feed)
        self.feed = feed

class FeedCreatedSignal(FeedsChangedSignal):
    def __init__(self, sender, feed, category=None, index=None):
        FeedsChangedSignal.__init__(self, sender)
        self.feed = feed
        self.category = category
        self.index = index

# Feed detail Specific
class FeedDetailChangedSignal(BaseSignal):
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)

class ItemOrderChangedSignal(FeedDetailChangedSignal):
    pass

class FeedPollFrequencyChangedSignal(FeedDetailChangedSignal):
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)

class FeedNumOfItemsStoredChangedSignal(FeedDetailChangedSignal):
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)

class FeedErrorStatusChangedSignal(FeedDetailChangedSignal):
    pass

class FeedInfoUpdatedSignal(FeedDetailChangedSignal):
    """
    Feed information like title, description, copyright, etc...
    """
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)

class FeedUnreadChangedSignal(FeedDetailChangedSignal):
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)


# Feed category specific

class FeedCategoriesChangedSignal(BaseSignal):
    pass

class FeedCategoryListChangedSignal(FeedCategoriesChangedSignal):
    pass

class FeedCategoryListLoadedSignal(FeedCategoriesChangedSignal):
    pass

class FeedCategoryAddedSignal(FeedCategoryListChangedSignal):
    def __init__(self, sender, category):
        FeedCategoryListChangedSignal.__init__(self, sender)
        self.category = category

class FeedCategoryRemovedSignal(FeedCategoryListChangedSignal):
    def __init__(self, sender, category):
        FeedCategoryListChangedSignal.__init__(self, sender)
        self.category = category

class FeedCategoryChangedSignal(FeedCategoriesChangedSignal):
    """ Called when a Feed Category was modified (i.e. feed added, feed
    removed, category title changed, etc...
    """
    def __init__(self, sender, feed=None):
        FeedCategoriesChangedSignal.__init__(self, sender)
        self.feed = feed

class FeedCategorySortedSignal(FeedCategoryChangedSignal):
    """ Called when a Feed Category has been sorted. """
    def __init__(self, sender, reverse=False):
        FeedCategoryChangedSignal.__init__(self, sender)
        self.descending = reverse

class SubscriptionContentsUpdatedSignal(BaseSignal):
    pass

# Item specific
class ItemsAddedSignal(BaseSignal):
    def __init__(self, sender, items):
        BaseSignal.__init__(self, sender)
        self.items = items

class ItemDeletedSignal(BaseSignal):
    def __init__(self, sender, item):
        BaseSignal.__init__(self, sender)
        self.item = item

class NewItemsSignal(BaseSignal):
    def __init__(self, sender, items):
        BaseSignal.__init__(self, sender)
        self.items = items

class ItemStickySignal(BaseSignal):
    def __init__(self, sender, item=None):
        BaseSignal.__init__(self, sender)
        if item is None:
            item = sender
        self.item = item

class AllItemsReadSignal(BaseSignal):
    def __init__(self, sender, changed):
        BaseSignal.__init__(self, sender)
        self.changed = changed


# Other events ...

class FeedLastPollChangedSignal(BaseSignal):
    pass

class PollChangedSignal(BaseSignal):
    def __init__(self, sender):
        BaseSignal.__init__(self, sender)

class FeedPolledSignal(BaseSignal):
    pass

class FeedStatusChangedSignal(BaseSignal):
    """
    Provides a signal for denoting that the polling process
    has begun or finished.
    """
    pass

class ImageUpdatedSignal(BaseSignal):
    def __init__(self, sender, url, data):
        BaseSignal.__init__(self, sender)
        self.url = url
        self.data = data

class NumberOfItemsStoredChangedSignal(BaseSignal):
    pass

class PollFrequencyChangedSignal(PollChangedSignal):
    """ Global poll frequency """
    def __init__(self, sender, value):
        PollChangedSignal.__init__(self, sender)
        self.value = value

class PollingStoppedSignal(BaseSignal):
    pass

class FeedSelectionChangedSignal(BaseSignal):
    """
    Emitted when a feed has been selected in the feeds tree view.
    """
    def __init__(self, sender, old, new):
        BaseSignal.__init__(self, sender)
        self.old = old
        self.new = new

class ItemSelectionChangedSignal(BaseSignal):
    """ Called when an item has been selected in the item list.
    Normally this should display the content of the item
    """
    def __init__(self, sender, item):
        BaseSignal.__init__(self, sender)
        self.item = item

class ItemReadSignal(BaseSignal):
    """ Called when an item's 'seen' property has been changed
    (i.e. marked as read)
    """
    def __init__(self, sender, item=None):
        BaseSignal.__init__(self, sender)
        if item is None:
            item = sender
        self.item = item

class CategorySelectionChangedSignal(BaseSignal):
    """
    Emitted when a the user changes categories
    """
    def __init__(self, sender, newcat):
        BaseSignal.__init__(self, sender)
        self.current = newcat

class OfflineModeChangedSignal(BaseSignal):
    pass

class RefreshFeedDisplaySignal(BaseSignal):
    pass

class StatusDisplaySignal(BaseSignal):
    pass

class FindInterruptSignal(BaseSignal):
    pass


if __name__ == '__main__':
    print find_bases(ItemReadSignal)
