# -*- 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__ = 'Benjamin Kampmann <benjamin@fluendo.com>'


import os, weakref

from elisa.core.component import InitializeFailure
from elisa.base_components.activity import Activity
from elisa.core import common

from elisa.extern.coherence.et import parse_xml

from twisted.internet import defer

class XmltreemenuActivity(Activity):
    """
    Parses an XML description of a tree of media and creates the corresponding
    tree of Models.

    DOCME MORE
    """

    # FIXME: retrieving ~/.elisa should be a facility provided by Elisa core
    default_config = {'xml_menu' : '%s/.elisa/elisa_menu.xml' % \
                                   os.path.expanduser('~'),
                      'menu_builders' : ['xmlmenu:activity_node_builder', \
                                         'xmlmenu:locations_builder', \
                                         'xmlmenu:xdg_entry_builder', \
                                         'xmlmenu:playlist_node_builder', \
                                         'xmlmenu:menu_node_builder', \
                                         'xmlmenu:uri_node_builder']
                      }

    config_doc = {'xml_menu' : 'Local path to the XML file containing' \
                               ' the Elisa menu description',
                  'menu_builders' : 'Components used to build the menu',
                               }

    def initialize(self):
        self._xml_menu_path = self.config.get('xml_menu', '')

        self._model_configs = weakref.WeakKeyDictionary()

        #FIXME: This reading is blocking!
        try:
            self.debug("Trying to load XML file: %s " % self._xml_menu_path)
            file = open(self._xml_menu_path, 'r')
            xml = file.read()
        except IOError, error:
            self.info("Could not read %s: %s" % (self._xml_menu_path, error))
            self.info("Using default menu description")
            full_path = self.plugin.get_resource_file('data/default_menu.xml')
            xml = open(full_path, 'r').read()

        try:
            self._xml = parse_xml(xml)
        except Exception, error:
            raise InitializeFailure('XMLTreeActivity', "Invalid XML in '%s:'"\
                                    " %s" % (self._xml_menu_path, error))

        # dictionary with keys being identifiers as returned by
        # menu_entry_identifiers__get and values being builders
        self._menu_builders = {}
        builder_paths = self.config.get('menu_builders' , [])
        registry = common.application.plugin_registry

        def setup_builder(builder):
            builder.activity = self
            builder.model_configs = self._model_configs

            # setup builder to be the one managing its supported
            # menu_entry_identifiers if no other builder is doing it
            for identifier in builder.menu_entry_identifiers:
                if self._menu_builders.has_key(identifier):
                    self.warning('Identifier %s already manager by %s' % \
                                 (identifier, self._menu_builders[identifier]))
                else:
                    self._menu_builders[identifier] = builder

        def builder_failed(failure, builder, builder_paths):
            # inform the user about the failure
            path = common.application.log_failure(failure)
            self.warning("Creating the builder '%s' failed. Full Traceback" \
                         " saved at %s" % (builder, path))
            # carry on building the remaining builders
            create_builders(builder_paths)

        def create_builders(builder_paths):
            """
            Create and setup sequentially the builders identified by their path
            """
            if len(builder_paths) == 0:
                return defer.succeed(None)

            dfr = defer.maybeDeferred(registry.create_component,
                                      builder_paths[0])
            dfr.addCallback(setup_builder)
            dfr.addCallback(lambda dummy, builder_paths:
                            create_builders(builder_paths), builder_paths[1:])
            dfr.addErrback(builder_failed, builder_paths[0], builder_paths[1:])
            return dfr

        dfr = create_builders(builder_paths)

        # FIXME: in the next API, we should return the dfr

    def clean(self):
        Activity.clean(self)
        # FIXME: write down the default config
        for builder in self._menu_builders.values():
            builder.clean()

    def unload(self, model):
        """
        Clear the children of the model
        """
        model[:] = []

    def loadmore(self, model):

        if model.children == None or len(model.children) == 0:
            registry = common.application.plugin_registry
            model.children = registry.create_component('base:list_model')
            model.children.activity = self

        elif len(model.children) > 0:
            return defer.succeed(model.children)

        if self._model_configs.has_key(model):
            # FIXME: could this be faulty?
            config = self._model_configs[model]
            model.children.content_type = config['ContentType']
            if config.has_key('xml_tag'):
                for node in config['xml_tag'].findall('MenuEntry'):
                    self.handle_menu_entry(model, node)
                return defer.succeed(model.children)

        return defer.fail(None)

    def get_model(self):
        registry = common.application.plugin_registry

        menu_model = registry.create_component('base:menu_model')
        menu_model.children = registry.create_component('base:list_model')
        menu_model.children.activity = self
 
        menu_model.text = 'elisa'
        menu_model.activity = self
        menu_model.has_children = True

        # set the root xml_tag
        root = self._xml.getroot()
        menu_config =  {
                    'DefaultDirsIcon' : 'dir_icon',
                    'DefaultFilesIcon' : 'file_icon',
                    'MediaFilter' : 'ANY',
                    'ContentType' : 'sections' }

        config = root.find('Configuration')
        if config != None:
            for key in menu_config.keys():
                new_config = config.find(key)
                if new_config != None:
                    menu_config[key] = new_config.text

        menu_model.children.content_type = menu_config['ContentType']

        menu_config['xml_tag'] = root
        self._model_configs[menu_model] = menu_config

        return menu_model

    # private methods
    def handle_menu_entry(self, parent, menu_node):
        tipo = menu_node.get('type', None)
        if tipo == None:
            self.warning ('%s misses a type value. Skipped' % menu_node)
            return dfr.succeed([])

        if self._menu_builders.has_key(tipo):
            builder = self._menu_builders[tipo]

            return builder.build_menu_entry(parent, menu_node)
        return defer.succeed([]) 
