""" A plugin manager composed of other plugin managers! """


# Standard library imports.
import logging

# Enthought library imports.
from traits.api import Event, HasTraits, Instance, List, provides
from traits.api import on_trait_change

# Local imports.
from .i_application import IApplication
from .i_plugin import IPlugin
from .i_plugin_manager import IPluginManager
from .plugin_event import PluginEvent
from .plugin_manager import PluginManager


# Logging.
logger = logging.getLogger(__name__)


@provides(IPluginManager)
class CompositePluginManager(HasTraits):
    """ A plugin manager composed of other plugin managers!

    e.g::

        plugin_manager = CompositePluginManager(
             plugin_mangers = [
                 EggBasketPluginManager(...),
                 PackagePluginManager(...),
             ]
        )

    """

    #### 'IPluginManager' protocol #############################################

    #### Events ####

    # Fired when a plugin has been added to the manager.
    plugin_added = Event(PluginEvent)

    # Fired when a plugin has been removed from the manager.
    plugin_removed = Event(PluginEvent)

    #### 'CompositePluginManager' protocol #####################################

    # The application that the plugin manager is part of.
    application = Instance(IApplication)
    def _application_changed(self, trait_name, old, new):
        for plugin_manager in self.plugin_managers:
            plugin_manager.application = new

        return

    # The plugin managers that make up this plugin manager!
    #
    # This is currently a list of 'PluginManager's as opposed to, the more
    # preferable 'IPluginManager' because the interface doesn't currently
    # have an 'application' trait. Should we move 'application' up to
    # 'IPluginManager'?
    plugin_managers = List(PluginManager)
    @on_trait_change('plugin_managers[]')
    def _update_application(self, obj, trait_named, removed, added):
        for plugin_manager in removed:
            plugin_manager.application = self.application

        for plugin_manager in added:
            plugin_manager.application = self.application

    @on_trait_change('plugin_managers:plugin_added')
    def _plugin_added(self, obj, trait_name, old, new):
        self.plugin_added = new

    @on_trait_change('plugin_managers:plugin_removed')
    def _plugin_removed(self, obj, trait_name, old, new):
        self.plugin_removed = new

    #### Private protocol ######################################################

    # The plugins that the manager manages!
    _plugins = List(IPlugin)
    def __plugins_default(self):
        plugins = []
        for plugin_manager in self.plugin_managers:
            for plugin in plugin_manager:
                plugins.append(plugin)

        return plugins

    #### 'object' protocol ####################################################

    def __iter__(self):
        """ Return an iterator over the manager's plugins. """

        plugins = []
        for plugin_manager in self.plugin_managers:
            for plugin in plugin_manager:
                plugins.append(plugin)

        return iter(plugins)

    #### 'IPluginManager' protocol #############################################

    def add_plugin(self, plugin):
        """ Add a plugin to the manager. """

        raise NotImplementedError

    def get_plugin(self, plugin_id):
        """ Return the plugin with the specified Id. """

        for plugin in self:
            if plugin_id == plugin.id:
                break

        else:
            plugin = None

        return plugin

    def remove_plugin(self, plugin):
        """ Remove a plugin from the manager. """

        raise NotImplementedError

    def start(self):
        """ Start the plugin manager. """

        for plugin in self:
            self.start_plugin(plugin)

        return

    def start_plugin(self, plugin=None, plugin_id=None):
        """ Start the specified plugin. """

        plugin = plugin or self.get_plugin(plugin_id)
        if plugin is not None:
            logger.debug('plugin %s starting', plugin.id)
            plugin.activator.start_plugin(plugin)
            logger.debug('plugin %s started', plugin.id)

        else:
            raise SystemError('no such plugin %s' % plugin_id)

        return

    def stop(self):
        """ Stop the plugin manager. """

        # We stop the plugins in the reverse order that they were started.
        stop_order = list(iter(self))
        stop_order.reverse()

        for plugin in stop_order:
            self.stop_plugin(plugin)

        return

    def stop_plugin(self, plugin=None, plugin_id=None):
        """ Stop the specified plugin. """

        plugin = plugin or self.get_plugin(plugin_id)
        if plugin is not None:
            logger.debug('plugin %s stopping', plugin.id)
            plugin.activator.stop_plugin(plugin)
            logger.debug('plugin %s stopped', plugin.id)

        else:
            raise SystemError('no such plugin %s' % plugin_id)

        return

#### EOF ######################################################################
