# -*- coding: utf-8 -*-
# vim: autoindent shiftwidth=4 expandtab textwidth=80 tabstop=4 softtabstop=4

###############################################################################
# OpenLP - Open Source Lyrics Projection                                      #
# --------------------------------------------------------------------------- #
# Copyright (c) 2008-2014 Raoul Snyman                                        #
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan      #
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub,      #
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer.   #
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru,          #
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith,             #
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock,              #
# Frode Woldsund, Martin Zibricky                                             #
# --------------------------------------------------------------------------- #
# 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; version 2 of the License.                              #
#                                                                             #
# 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                          #
###############################################################################
"""
Provide plugin management
"""
import os
import sys
import logging

from openlp.core.lib import Plugin, PluginStatus

log = logging.getLogger(__name__)

class PluginManager(object):
    """
    This is the Plugin manager, which loads all the plugins,
    and executes all the hooks, as and when necessary.
    """
    log.info(u'Plugin manager loaded')
    __instance__ = None
    @staticmethod
    def get_instance():
        """
        Obtain a single instance of class.
        """
        return PluginManager.__instance__

    def __init__(self, plugin_dir):
        """
        The constructor for the plugin manager. Passes the controllers on to
        the plugins for them to interact with via their ServiceItems.

        ``plugin_dir``
            The directory to search for plugins.
        """
        log.info(u'Plugin manager Initialising')
        PluginManager.__instance__ = self
        if not plugin_dir in sys.path:
            log.debug(u'Inserting %s into sys.path', plugin_dir)
            sys.path.insert(0, plugin_dir)
        self.basepath = os.path.abspath(plugin_dir)
        log.debug(u'Base path %s ', self.basepath)
        self.plugins = []
        log.info(u'Plugin manager Initialised')

    def find_plugins(self, plugin_dir, plugin_helpers):
        """
        Scan the directory ``plugin_dir`` for objects inheriting from the
        ``Plugin`` class.

        ``plugin_dir``
            The directory to scan.

        ``plugin_helpers``
            A list of helper objects to pass to the plugins.

        """
        log.info(u'Finding plugins')
        startdepth = len(os.path.abspath(plugin_dir).split(os.sep))
        log.debug(u'finding plugins in %s at depth %d',
            unicode(plugin_dir), startdepth)
        for root, dirs, files in os.walk(plugin_dir):
            # TODO Presentation plugin is not yet working on Mac OS X.
            # For now just ignore it. The following code will hide it
            # in settings dialog.
            if sys.platform == 'darwin':
                present_plugin_dir = os.path.join(plugin_dir, 'presentations')
                # Ignore files from the presentation plugin directory.
                if root.startswith(present_plugin_dir):
                    continue
            for name in files:
                if name.endswith(u'.py') and not name.startswith(u'__'):
                    path = os.path.abspath(os.path.join(root, name))
                    thisdepth = len(path.split(os.sep))
                    if thisdepth - startdepth > 2:
                        # skip anything lower down
                        break
                    modulename = os.path.splitext(path)[0]
                    prefix = os.path.commonprefix([self.basepath, path])
                    # hack off the plugin base path
                    modulename = modulename[len(prefix) + 1:]
                    modulename = modulename.replace(os.path.sep, '.')
                    # import the modules
                    log.debug(u'Importing %s from %s. Depth %d',
                        modulename, path, thisdepth)
                    try:
                        __import__(modulename, globals(), locals(), [])
                    except ImportError, e:
                        log.exception(u'Failed to import module %s on path %s '
                            'for reason %s', modulename, path, e.args[0])
        plugin_classes = Plugin.__subclasses__()
        plugin_objects = []
        for p in plugin_classes:
            try:
                plugin = p(plugin_helpers)
                log.debug(u'Loaded plugin %s', unicode(p))
                plugin_objects.append(plugin)
            except TypeError:
                log.exception(u'Failed to load plugin %s', unicode(p))
        plugins_list = sorted(plugin_objects, key=lambda plugin: plugin.weight)
        for plugin in plugins_list:
            if plugin.checkPreConditions():
                log.debug(u'Plugin %s active', unicode(plugin.name))
                plugin.setStatus()
            else:
                plugin.status = PluginStatus.Disabled
            self.plugins.append(plugin)

    def hook_media_manager(self):
        """
        Create the plugins' media manager items.
        """
        for plugin in self.plugins:
            if plugin.status is not PluginStatus.Disabled:
                plugin.createMediaManagerItem()

    def hook_settings_tabs(self, settings_form=None):
        """
        Loop through all the plugins. If a plugin has a valid settings tab
        item, add it to the settings tab.
        Tabs are set for all plugins not just Active ones

        ``settings_form``
            Defaults to *None*. The settings form to add tabs to.
        """
        for plugin in self.plugins:
            if plugin.status is not PluginStatus.Disabled:
                plugin.createSettingsTab(settings_form)
        settings_form.plugins = self.plugins

    def hook_import_menu(self, import_menu):
        """
        Loop through all the plugins and give them an opportunity to add an
        item to the import menu.

        ``import_menu``
            The Import menu.
        """
        for plugin in self.plugins:
            if plugin.status is not PluginStatus.Disabled:
                plugin.addImportMenuItem(import_menu)

    def hook_export_menu(self, export_menu):
        """
        Loop through all the plugins and give them an opportunity to add an
        item to the export menu.

        ``export_menu``
            The Export menu.
        """
        for plugin in self.plugins:
            if plugin.status is not PluginStatus.Disabled:
                plugin.addExportMenuItem(export_menu)

    def hook_tools_menu(self, tools_menu):
        """
        Loop through all the plugins and give them an opportunity to add an
        item to the tools menu.

        ``tools_menu``
            The Tools menu.
        """
        for plugin in self.plugins:
            if plugin.status is not PluginStatus.Disabled:
                plugin.addToolsMenuItem(tools_menu)

    def initialise_plugins(self):
        """
        Loop through all the plugins and give them an opportunity to
        initialise themselves.
        """
        log.info(u'Initialise Plugins - Started')
        for plugin in self.plugins:
            log.info(u'initialising plugins %s in a %s state'
                % (plugin.name, plugin.isActive()))
            if plugin.isActive():
                plugin.initialise()
                log.info(u'Initialisation Complete for %s ' % plugin.name)
        log.info(u'Initialise Plugins - Finished')

    def finalise_plugins(self):
        """
        Loop through all the plugins and give them an opportunity to
        clean themselves up
        """
        log.info(u'finalising plugins')
        for plugin in self.plugins:
            if plugin.isActive():
                plugin.finalise()
                log.info(u'Finalisation Complete for %s ' % plugin.name)

    def get_plugin_by_name(self, name):
        """
        Return the plugin which has a name with value ``name``.
        """
        for plugin in self.plugins:
            if plugin.name == name:
                return plugin
        return None
