# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2006-2009 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 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.plugins.pigment.pigment_controller import PigmentController
from elisa.core.utils import defer

from elisa.plugins.poblesec.base.preview_list import PreviewListController
from elisa.plugins.poblesec.base.coverflow import CoverflowController
from elisa.plugins.poblesec.base.grid import GridController


class ViewModeNotFound(Exception):

    def __init__(self, view_mode_name, modes):
        self._view_mode_name = view_mode_name
        self._modes = modes

    def __str__(self):
        return "View mode %r not found. Available modes: %r" % (self._view_mode_name,
                                                                self._modes)

PREVIEW_LIST_MODE = 'preview_list'
COVERFLOW_MODE = 'coverflow'
GRID_MODE = 'grid'

class ListSwitcherController(PigmentController):

    # list of classes inheriting from
    # elisa.plugins.poblesec.list.BaseListController
    modes = []
    default_mode = None

    def initialize(self, **kwargs):
        dfr = super(ListSwitcherController, self).initialize()
        self.kwargs = kwargs

        self._switch_dfr = None

        def list_created(controller):
            self.list_controller = controller
            self._show_controller(controller)
            return self

        # backward compatibility with default_mode class attribute
        default_mode = self.default_mode
        if default_mode is None:
            default_mode = self.modes[0]

        # retrieve view_mode name from config
        config = self.config
        if config is None:
            config = {}
        try:
            view_mode_name = config['view_mode']
        except KeyError:
            view_mode = default_mode
        else:
            mode_types = {PREVIEW_LIST_MODE: PreviewListController,
                          COVERFLOW_MODE: CoverflowController,
                          GRID_MODE: GridController}
            if view_mode_name not in mode_types:
                raise ValueError("Invalid view_mode. Must be one of %r" % mode_types.keys())
            mode_type = mode_types[view_mode_name]
            try:
                view_mode = [mode_class for mode_class in self.modes
                             if issubclass(mode_class, mode_type)][0]
            except IndexError, error:
                raise ViewModeNotFound(view_mode_name, self.modes)

        dfr.addCallback(view_mode.create, **kwargs)
        dfr.addCallback(list_created)
        return dfr

    def set_frontend(self, frontend):
        super(ListSwitcherController, self).set_frontend(frontend)
        self.list_controller.set_frontend(frontend)

    def set_path(self, path):
        super(ListSwitcherController, self).set_path(path)
        self.list_controller.path = self.path
        
    def ready(self):
        super(ListSwitcherController, self).ready()
        self.list_controller.ready()

    def _show_controller(self, controller):
        self.widget.add(controller.widget)
        self.widget.set_focus_proxy(controller.widget)
        if self.widget.focus:
            controller.widget.set_focus()

    def _hide_controller(self, controller):
        self.widget.remove(controller.widget)

    def switch_mode(self, new_class):
        if self._switch_dfr is not None:
            return False

        if isinstance(self.list_controller, new_class):
            return False

        def list_created(new_list, sensitive):
            new_list.path = self.path
            self._hide_controller(self.list_controller)
            self.list_controller.removed()
            new_list.set_frontend(self.frontend)
            new_list.widget.visible = False
            self._show_controller(new_list)
            new_list.prepare()
            new_list.widget.visible = True
            new_list.sensitive = sensitive

            # pass the model and the current index across
            index = self.list_controller.nodes.selected_item_index
            model = self.list_controller.model
            new_list.nodes.selected_item_index = index

            old_list = self.list_controller
            self.list_controller = new_list

            def clean_old_list(value):
                old_list.clean()

            # FIXME: states are lost in the mode switching (notably
            # self.list_controller.model and self.list_controller.actions) and
            # all data is reloaded; it misses all extra data added by the
            # various decorators. This hack calls the decorators once again.
            # FIXME: call to a private method of PigmentFrontend
            dfr = self.frontend._decorate(self)
            dfr.addCallback(clean_old_list)
            return dfr

        dfr = new_class.create(**self.kwargs)
        self._switch_dfr = dfr
        dfr.addCallback(list_created, self._sensitive)

        def forget_deferred(value):
            self._switch_dfr = None

        dfr.addBoth(forget_deferred)

    def handle_input(self, manager, input_event):
        return self.list_controller.handle_input(manager, input_event)

    def sensitive_set(self, value):
        self._sensitive = value
        try:
            self.list_controller.sensitive = value
        except AttributeError:
            # happens on the first load
            pass

    def sensitive_get(self, value):
        return self._sensitive

    sensitive = property(sensitive_get, sensitive_set)

    def prepare(self):
        self.list_controller.prepare()

    def removed(self):
        if self.list_controller:
            self.list_controller.removed()

    def clean(self):
        if self._switch_dfr is not None:
            dfr = self._switch_dfr
        else:
            dfr = defer.succeed(self)

        def clean_list_controller(value):
            return self.list_controller.clean()

        dfr.addCallback(clean_list_controller)

        def list_controller_cleaned(result):
            self.list_controller = None
            return super(ListSwitcherController, self).clean()

        dfr.addCallback(list_controller_cleaned)

        return dfr
