#####################################################################
#                                                                   #
# /plugins/__init__.py                                              #
#                                                                   #
# Copyright 2013, Monash University                                 #
#                                                                   #
# This file is part of the program BLACS, in the labscript suite    #
# (see http://labscriptsuite.org), and is licensed under the        #
# Simplified BSD License. See the license.txt file in the root of   #
# the project for the full license.                                 #
#                                                                   #
#####################################################################
import os
import sys
import logging
import importlib
from types import MethodType
from collections import defaultdict
from labscript_utils.labconfig import LabConfig
from blacs import BLACS_DIR
PLUGINS_DIR = os.path.join(BLACS_DIR, 'plugins')

default_plugins = ['connection_table', 'general', 'theme']

logger = logging.getLogger('BLACS.plugins')

DEFAULT_PRIORITY = 10

class Callback(object):
    """Class wrapping a callable. At present only differs from a regular
    function in that it has a "priority" attribute - lower numbers means
    higher priority. If there are multiple callbacks triggered by the same
    event, they will be returned in order of priority by get_callbacks"""
    def __init__(self, func, priority=DEFAULT_PRIORITY):
        self.priority = priority
        self.func = func

    def __get__(self, instance, class_):
        """Make sure our callable binds like an instance method. Otherwise
        __call__ doesn't get the instance argument."""
        if instance is None:
            return self
        else:
            return MethodType(self, instance)

    def __call__(self, *args, **kwargs):
        return self.func(*args, **kwargs)


class callback(object):
    """Decorator to turn a function into a Callback object. Presently
    optional, and only required if the callback needs to have a non-default
    priority set"""
    # Instantiate the decorator:
    def __init__(self, priority=DEFAULT_PRIORITY):
        self.priority = priority
    # Call the decorator
    def __call__(self, func):
        return Callback(func, self.priority)


def get_callbacks(name):
    """Return all the callbacks for a particular name, in order of priority"""
    import __main__
    BLACS = __main__.app
    callbacks = []
    for plugin in BLACS.plugins.values():
        try:
            plugin_callbacks = plugin.get_callbacks()
            if plugin_callbacks is not None:
                if name in plugin_callbacks:
                    callbacks.append(plugin_callbacks[name])
        except Exception:
            logger.exception('Error getting callbacks from %s.' % str(plugin))
            
    # Sort all callbacks by priority:
    callbacks.sort(key=lambda callback: getattr(callback, 'priority', DEFAULT_PRIORITY))
    return callbacks


exp_config = LabConfig()
if not exp_config.has_section('BLACS/plugins'):
    exp_config.add_section('BLACS/plugins')

modules = {}
for module_name in os.listdir(PLUGINS_DIR):
    if os.path.isdir(os.path.join(PLUGINS_DIR, module_name)) and module_name != '__pycache__':
        # is it a new plugin?
        # If so lets add it to the config
        if not module_name in [name for name, val in exp_config.items('BLACS/plugins')]:
            exp_config.set('BLACS/plugins', module_name, str(module_name in default_plugins))

        # only load activated plugins
        if exp_config.getboolean('BLACS/plugins', module_name):
            try:
                module = importlib.import_module('blacs.plugins.'+module_name)
            except Exception:
                logger.exception('Could not import plugin \'%s\'. Skipping.'%module_name)
            else:
                modules[module_name] = module
