# -*- 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>'


from elisa.core.utils import classinit, signal
from elisa.core import log
from elisa.base_components.theme import Theme
from elisa.core import common
from elisa.extern.translation import Translatable

from twisted.internet import defer


class Frontend(log.Loggable):
    """
    A frontend can be connected to a L{elisa.core.backend.Backend} in order to
    render its content. It does so by holding the root of a tree
    L{elisa.base_components.view.View}s that render the
    L{elisa.base_components.controller.Controller}s hold by the backend. The
    rendering is done in a context which is unique to a frontend.

    A L{elisa.base_components.theme.Theme} can be used in order to change the
    set of icons used by the views for their rendering.

    @ivar view:         root view associated to the frontend
    @type view:         L{elisa.base_components.view.View}
    @ivar context:      context in which the root view is rendered
    @type context:      L{elisa.base_components.context.Context}
    @ivar theme:        theme used by the frontend at rendering
    @type theme:        L{elisa.base_components.theme.Theme}
    @ivar languages:    the translation to use
    @type languages:    list
    @ivar mvc_config:   MVC associations config
    @type mvc_config:   L{elisa.core.config.Config}

    DOCME: ivar name; see below.
    """

    __metaclass__ = classinit.ClassInitMeta
    __classinit__ = classinit.build_properties
    
    theme_changed = signal.Signal('theme_changed', Theme)

    # FIXME: what is name used for? It needs to be documented.
    name = None

    def __init__(self, mvc_config):
        log.Loggable.__init__(self)
        self.view = None
        self.context = None
        self._theme = None
        self._languages = []
        self.mvc_config = mvc_config

    def backend__set(self, backend):
        backend.frontends.append(self)
        self._backend = backend

    def backend__get(self):
        return self._backend
    
    def theme__get(self):
        return self._theme

    def theme__set(self, new_theme):
        old_theme = self._theme
        self._theme = new_theme

        self.context.theme_changed(old_theme, new_theme)
        self.theme_changed.emit(new_theme)

        # save theme path in config
        application = common.application
        application.config.set_option('theme', new_theme.path, section=self.name)
       
    def languages__get(self):
        return self._languages

    def languages__set(self, languages):
        self.debug("Setting languages to %r", languages)
        if isinstance(languages, list):
            self._languages = languages
        elif isinstance(languages, base_string):
            self._languages = [languages]
        else:
            self.warning("Could not set the languages to %r. Please use a"
                         " list of strings instead", languages)

    def translate_text(self, text):
        """
        Convert a translatable text to a text in the languages
        supported by the frontend.

        @param text: text to translate
        @type text:  string or L{elisa.extern.translator.Translatable}
        @rtype:      unicode
        """
        self.info("Translating text %r", text)
        translated = text
        trans = common.application.translator
        if isinstance(text, Translatable):
            translated = trans.translateTranslatable(text, self._languages)
        else:
            self.debug("%r is not a Translatable", text)
        return translated

    def clean(self):
        """
        Cleanup the frontend. Once done, no rendering will be possible.

        @rtype: L{twisted.internet.defer.Deferred}
        """
        # the cleanups have to be chained (in a sequence)
        dfr = defer.Deferred()
        dfr.addCallback(lambda x: self.view.clean())
        dfr.addCallback(lambda x: self._theme.clean())
        dfr.addCallback(lambda x: self.context.clean())
        dfr.callback(None)
        return dfr

    def get_view_path(self, model_path, content_type=None):
        """
        Retrieve the path of the view associated with given model

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

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

        if view_path == None:
            section = model_path 
            view_path = config.get_option('view', section=section)

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