from __future__ import print_function
import importlib
from collections import OrderedDict

from ..models.TreeModel import TreeModel
from .. import plugins as pgns
from .. import pawstools
from .PawsPlugin import PawsPlugin

class PluginManager(TreeModel):
    """Tree for storing, browsing, and managing PawsPlugins"""

    def __init__(self):
        flag_dict = OrderedDict()
        flag_dict['select'] = False
        super(PluginManager,self).__init__(flag_dict)
        self.plugins = self._root_dict
        self.message_callback = print 

    def add_plugin(self,plugin_name,plugin_module):
        """Import, name, and add a plugin.

        After a plugin is added to a plugin_manager,
        it is available as plugin_manager.plugins[plugin_name].

        Parameters
        ----------
        plugin_name : str
            Name that will be used to refer to this plugin after it is added.
        plugin_module : str
            Name of the plugin module.
            Example: If class MyPlugin is in the CATEGORY.MyPlugin module,
            retrieve it with `plugin_module` = 'CATEGORY.MyPlugin'.
        """
        p = self.get_plugin(plugin_module)
        if not self.is_tag_valid(plugin_name): 
            raise pawstools.PluginNameError(self.tag_error_message(plugin_name))
        self.set_item(plugin_name,p)

    def get_plugin(self,plugin_module): 
        """Import, instantiate, return a PawsPlugin from its module.

        This can also be used to test the Python environment 
        for compatibility with a plugin.

        Parameters
        ----------
        plugin_module : str
            Name of the plugin module.
            See add_plugin().

        Returns
        -------
        PawsPlugin 
            An instance of the PawsPlugin subclass defined in `plugin_module`. 
        """
        mod = importlib.import_module('.'+plugin_module,pgns.__name__)
        return mod.__dict__[plugin_module]()

    def set_input(self,plugin_name,input_name,val=None):
        """Set a plugin input to the provided value.

        Parameters
        ----------
        plugin_name : str
            Name that will be used to refer to this plugin after it is added.
        input_name : str
            name of the input to be set
        val : object
            the data to be used as plugin input
        """
        self.set_item(plugin_name+'.inputs.'+input_name,val)

    def load_plugin(self,plugin_name,plugin_setup_dict):
        """Load, set up, and start() a PawsPlugin, given its setup_dict().

        Parameters
        ----------
        plugin_name : str
            Name that will be used to refer to this plugin after it is added.
        plugin_setup_dict : dict 
            Dict specifying plugin setup,
            probably generated by calling the plugin's own setup_dict() method.
        """
        plugin_mod = plugin_setup_dict['plugin_module']
        p = self.get_plugin(plugin_mod)
        self.set_item(plugin_name,p)
        for inp_name in p.inputs.keys():
            if inp_name in plugin_setup_dict['inputs'].keys():
                self.set_input(plugin_name,inp_name,plugin_setup_dict['inputs'][inp_name])
        p.start()

    def n_plugins(self):
        """Return number of plugins currently loaded."""
        return len(self.plugins) 

    def build_tree(self,x):
        """Return a dict describing a tree-like structure of this object.

        This is a reimplemention of TreeModel.build_tree() 
        to define this object's child tree structure.
        For a PluginManager, a dict is provided for each PawsPlugin,
        where the dict contains the results of calling
        self.build_tree(plugin.inputs) and self.build_tree(plugin.content()). 
        """
        if isinstance(x,PawsPlugin):
            d = OrderedDict()
            d['inputs'] = self.build_tree(x.inputs)
            d['content'] = self.build_tree(x.content())
        else:
            return super(PluginManager,self).build_tree(x) 
        return d


