# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 Fluendo Embedded S.L. (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 Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.


__maintainer__ = 'Lionel Martin <lionel@fluendo.com>'
__maintainer2__ = 'Florian Boucault <florian@fluendo.com>'


from elisa.core.utils import classinit
from elisa.core import log

class Backend(log.Loggable):
    """
    A backend holds all the data that has to be rendered in order to display a
    complete Elisa user interface. It does so by storing a tree of
    L{elisa.base_components.controller.Controller}s and keeping track of the
    focus amongst them.

    Multiple L{elisa.core.frontend.Frontend}s can be connected to a backend
    through the L{elisa.core.interface_controller.InterfaceController} and
    render the content of the tree of controllers.

    L{elisa.base_components.input_provider.InputProvider}s can be associated
    with a backend so that all the L{elisa.core.input_event.InputEvent}s are
    passed on to the backend's tree of controllers.
    Input events are forwarded to the controller that has the focus:
    L{focused_controller}.


    @ivar root_controller:    root controller associated to the backend
    @type root_controller:    L{elisa.base_components.controller.Controller}
    @ivar focused_controller: controller which currently has the focus
    @type focused_controller: L{elisa.base_components.controller.Controller}
    @ivar mvc_config:         MVC associations config
    @type mvc_config:         L{elisa.core.config.Config}
    """

    __metaclass__ = classinit.ClassInitMeta
    __classinit__ = classinit.build_properties

    def __init__(self, mvc_config=None):
        log.Loggable.__init__(self)
        self.root_controller = None
        self.mvc_config = mvc_config
        self.frontends = []

    def root_controller__set(self, controller):
        self.debug("Setting controller %s as root controller" % controller)
        self._root_controller = controller
        self._focused_controller = controller

    def root_controller__get(self):
        return self._root_controller

    def focused_controller__get(self):
        return self._focused_controller

    def focused_controller__set(self, controller):
        controllers_to_unfocus = self._get_branch(self._focused_controller)
        controllers_to_focus = self._get_branch(controller)

        self._focused_controller = controller

        # notify focus removal to all the branch of controllers previously
        # focused
        for controller in controllers_to_unfocus:
            if controller not in controllers_to_focus:
                controller.focused = False

        # notify all the branch of controllers above 'controller' that they
        # are now focused
        for controller in controllers_to_focus:
            controller.focused = True

    def _get_branch(self, controller):
        """
        Return all the parents of a controller and the controller itself. The
        list is ordered starting with the deepest controller in the branch and
        going to the root.
        """
        branch = []
        current_controller = controller

        while current_controller != None:
            branch.append(current_controller)
            current_controller = current_controller.parent
            
        return branch


    def dispatch_input(self, input_event):
        """
        Process an input event by passing it to the focused controller.

        @param input_event: input event to be processed
        @type input_event:  L{elisa.core.input_event.InputEvent}
        """
        self.debug("Dispatching %s" % input_event)
        controller = self.focused_controller
        event_processed = False

        while controller != None:
            self.debug("Asking %s to handle %s" % (controller, input_event))
            event_processed = controller.handle_input(input_event)
            if event_processed == True:
                break
            controller = controller.parent

        if event_processed:
            self.debug("%s got processed by %s" % (input_event, controller))
        else:
            self.debug("No controller processed %s" % input_event)

    def get_controller_path(self, model_path, content_type=None):
        """
        Retrieve the path of the controller associated with given
        model's path.

        @param model_path:               path of the Model for which we need a
                                         Controller
        @type model_path:                string
        @keyword content_type:           content-type stored in the model
        @type content_type:              None or string
        @raises UndefinedMVCAssociation: if the backend's MVC mappings don't
                                         define any association for the given
                                         model
        """
        config = self.mvc_config
        self.debug("Looking in %r for a controller for %r with content_type %r",
                    config.get_filename(), model_path, content_type)

        controller_path = None
        if content_type:
            section = "%s/%s" % (model_path, content_type)
            controller_path = config.get_option('controller', section=section)

        if controller_path == None:
            section = model_path 
            controller_path = config.get_option('controller', section=section)

        if controller_path == None:
            from elisa.core.interface_controller import UndefinedMVCAssociation
            msg = "No controller path for %r" % model_path
            raise UndefinedMVCAssociation(msg)
        else:
            self.debug("Found %r", controller_path)
            return controller_path
