# Moovida - Home multimedia server
# Copyright (C) 2006-2009 Fluendo, S.A. (www.fluendo.com).
# All rights reserved.
#
# This file is available under one of two license agreements.
#
# This file is licensed under the GPL version 3.
# See "LICENSE.GPL" in the root of this distribution including a special
# exception to use Moovida with Fluendo's plugins.
#
# The GPL part of Moovida is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Moovida" in the root directory of this distribution package
# for details on that license.


from elisa.core import log
from elisa.core import common

from twisted.internet import defer, task
from twisted.python.failure import Failure

class FrontendOptionMissing(Exception):
    """
    see L{InterfaceController.initialize}
    """
    pass

class InterfaceController(log.Loggable):
    """
    The InterfaceController is responsible for creating, managing and storing
    the different L{elisa.core.components.frontend.Frontend}s the user has
    defined in the configuration file.

    @ivar   frontends: maps the name of a frontend to the instance  
    @type   frontends: dict
    """

    def __init__(self):
        log.Loggable.__init__(self)
        self.debug("Creating")
        self.frontends = {}

    def initialize(self):
        """
        Read the configuration of the application and create the frontends
        that are specified in it. The configuration could for example look
        like this::

            [general]
                ...
            frontends = ['opengl', 'statusicon', 'lcd']

            [opengl]
            frontend = 'pigment.pigment_frontend:PigmentFrontend'

            [statusicon]
            frontend = 'gtk.status_frontend:StatusFrontend'

            [lcd]
            frontend = 'lcg.lcd_frontend:LCDFrontend'

        If the frontend-option in such a section is missing, the
        failure-Exception for that certain frontend is the
        L{FrontendOptionMissing}.

        @rtype:  L{twisted.internet.defer.Deferred}
        @return: resulting in a list with a tuple of values where the first
                 value is True/False to identify if it worked, the second one
                 is the name of the frontend and the third one is either the
                 frontend object if it worked or the Failure if it failed.
        """

        self.info("Initializing")
        application = common.application
        plugin_registry = application.plugin_registry

        def load_frontends_iter(frontend_names, result_list):
            
            def frontend_created(frontend, name):
                self.debug("Loaded %s: %r" % (name, frontend))
                self.frontends[name] = frontend
                return (True, name, frontend)

            def frontend_creation_failed(failure, name):
                application.log_failure(failure)
                self.warning("creating frontend %s failed. A full traceback" \
                             " can be found at %s" % (name, failure))
                return (False, name, failure)

            for frontend_name in frontend_names:

                frontend_section = application.config.get_section(frontend_name, {})
                frontend = frontend_section.get('frontend', None)

                if frontend is None:
                    self.warning('%s is missing the frontend option. Skipping.' %
                                                                frontend_name)
                    result_list.append((False, frontend_name,
                                        Failure(FrontendOptionMissing())))
                    continue

                frontend_dfr = plugin_registry.create_component(frontend,
                                                                frontend_section)

                frontend_dfr.addCallback(frontend_created, frontend_name)
                frontend_dfr.addErrback(frontend_creation_failed, frontend_name)
                frontend_dfr.addCallback(result_list.append)
                yield frontend_dfr

        # a list of frontends we should load
        frontend_names = application.config.get_option('frontends',
                                                       section='general',
                                                       default=[])
        result_list = []
        res_dfr = task.coiterate(load_frontends_iter(frontend_names,
                                                                result_list))
        def return_list(result, result_list):
            for status, name, result in result_list:
                if status == True:
                    break
            else:
                self.warning("Could not load any frontend")
            return result_list

        res_dfr.addCallback(return_list, result_list)

        return res_dfr

    def stop(self):
        """
        Clean up the given frontends.

        @rtype:     L{twisted.internet.defer.DeferredList}
        @return:    that fires when the clean up of all frontends is done
        """
        dfrs = []
        self.info("Stopping")

        for frontend in self.frontends.values():
            dfr = frontend.clean()
            dfrs.append(dfr)

        dfr = defer.DeferredList(dfrs)
        return dfr
