"""Addins are the primary way of extending FeedPlatform the add
additional aggregator functionality.

You'll find a number of them in the builtin ``feedplatform.lib``
module, although custom addins can easily be created, and
generally, when extended or specialized customization is required,
creating an addin will be a common thing to do.

Addins require an installation process to register their hooks and
other modifiers, so that addins can be loaded and reloaded at any
time. If you change the list of addins at any point afterwards,
use ``reinstall()`` to put it into effect.

It is recommended that addins subclass ``base``, though it is not
required and an addin may in fact be any object that features a
``setup`` method. Addins can also specify a tuple attribute
``depends``, referring to other addin classes that are required
for an addin to function, too. If the user hasn't specified those
addins, they will be added implicitely, so long their constructor
allows parameterless instantiation. Otherwise, an error would be
raised, asking the user to manually add the dependency.
Currently, the ``depends`` tuple may refer to the other addins only
via a class reference.
"""

import types
import inspect
from copy import copy
from feedplatform import hooks
from feedplatform import log
from feedplatform.conf import config


__all__ = ('base', 'install', 'reinstall', 'get_addins')


class base(object):
    """Common base class for addins.

    It's use is optional, addins are not required to use it. However,
    doing so will provide certain helpers:

        * Instead of manually registering your hook callbacks, you can
          simply write them as methods, using the hook name prefixed
          with 'on_*' - e.g. 'on_get_guid'.
          An exception will be raised if the name after 'on_' does not
          refer to a valid hook.

        * self.log provides a namespaced Python logging facility.

        * A ``abstract`` attribute allows addins to be marked as not
          directly usable.
    """

    class __metaclass__(type):
        def __new__(cls, name, bases, attrs):
            # reset the ``abstract`` property for every new class
            # automatically, i.e. does not inherit.
            if not 'abstract' in attrs:
                attrs['abstract'] = False
            return type.__new__(cls, name, bases, attrs)


    def __new__(cls, *args, **kwargs):
        if getattr(cls, 'abstract', False):
            raise TypeError('%s is an abstract addin.' % cls)
        return super(base, cls).__new__(cls, *args, **kwargs)


    def setup(self):
        """Called to have the addin register it's hook callbacks.

        This is also the place for related setup jobs like setting up
        custom models.

        If an addin does not subclass ``addins.base``, it must provide
        this method itself.
        """

        # register new hook that the addin wants to define
        if hasattr(self, 'get_hooks'):
            new_hooks = self.get_hooks()
            if hooks:
                for name in new_hooks:
                    hooks.register(name)

        # auto-register all hook callbacks ('on_*'-pattern)
        for name in dir(self):
            if name.startswith('on_'):
                attr = getattr(self, name)
                if isinstance(attr, types.MethodType):