# -*- 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.core.components.controller import Controller
from elisa.core.utils import defer
from elisa.plugins.pigment.widgets.widget import Widget
from elisa.core import common
from elisa.core.input_event import *

import gobject


class PigmentController(Controller, gobject.GObject):
    """
    Specialised L{elisa.core.components.controller.Controller} for the Pigment
    toolkit.
    A L{PigmentController} creates a user interface in its L{widget}
    widget using other Pigment widgets.

    @ivar frontend:         frontend that created the controller
    @type frontend:         L{elisa.core.components.frontend.Frontend}
    @ivar sensitive:        whether this controller should react on click events
    @type sensitive:        C{bool}
    @ivar path:             path for which that controller was selected
    @type path:             C{str}
    @ivar widget:           Pigment widget containing the UI of the controller
    @type widget:           L{elisa.plugins.pigment.widgets.widget.Widget}
    """

    __gsignals__ = {
        'clean': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_PYOBJECT,
                  (), defer.deferred_accumulator)
    }

    def __init__(self):
        super(PigmentController, self).__init__()
        self.sensitive = False
        self.widget = Widget()
        self.widget.set_name(self.name)
        self.widget.visible = True
        self.pending_decorator_deferreds = []

        self._mapped_handler = self.widget.connect_after('mapped',
                                                         self._widget_mapped)

    def _widget_mapped(self, widget, *args):
        widget.regenerate()

    def set_frontend(self, frontend):
        """
        Set the frontend for the controller.

        It can optionally return a deferred.

        @param frontend: frontend
        @type frontend: L{elisa.core.components.frontend.Frontend}
        @rtype:  None or L{elisa.core.utils.defer.Deferred}
        """
        self.frontend = frontend
        return defer.succeed(None)

    def set_path(self, path):
        """
        Set the path for the controller.

        @param path: path
        @type path:  C{str}
        """
        self.path = path

    def handle_input(self, manager, event):
        """
        Handle an Elisa input event.

        The default implementation forwards the event to its focused descendant
        widget. It may be overridden in specific controllers if needed.

        @param manager: the input manager that emitted the event
        @type manager:  L{elisa.core.input_manager.InputManager}
        @param event:   the input event received
        @type event:    L{elisa.core.input_event.InputEvent}

        @return:        C{True} is the event was handled, C{False} otherwise
        @rtype:         C{bool}
        """
        if event.value == EventValue.KEY_EXIT:
            common.application.stop()
            return True

        if self.widget.focus:
            return Widget._focused.handle_input(manager, event)
        else:
            return self.widget.handle_input(manager, event)

    def has_focus(self):
        return self.widget.focus

    def prepare(self):
        """
        Called after L{widget} is added to the canvas. It is sensible to do
        aspect ratio dependent operations at that point.
        """
        pass

    def removed(self):
        """
        Called after L{widget} is removed from the canvas.
        """
        pass

    def ready(self):
        """
        Called after frontend finished creation of the controller and all 
        decorators are loaded.
        """
        pass

    def clean(self):
        if self._mapped_handler is not None:
            self.widget.disconnect(self._mapped_handler)
            self._mapped_handler = None
        dfr = defer.DeferredList(self.emit('clean') + 
                self.pending_decorator_deferreds)
        self.widget.clean()
        self.widget = None
        
        def call_parent(ignored):
            return super(PigmentController, self).clean()

        dfr.addCallback(call_parent)

        return dfr


class PigmentMainController(PigmentController):

    """
    Specialized pigment controller that's the root of the pigment UI.
    Such a controller should be unique.
    It defines an interface to implement for communication with the frontend.
    """

    def play_files(self, files):
        """
        Enqueue and start the playback of the given list of files.
        This may happen when files are dragged and dropped into the frontend's
        toplevel window.

        @param files: a list of file paths to enqueue and play
        @type files:  C{list} of C{unicode}
        """
        raise NotImplementedError()

    def toplevel_window_shown(self):
        """
        Callback invoked after showing the frontend's toplevel window.
        This may be triggered e.g. via the frontend's DBus API.
        """
        raise NotImplementedError()

    def toplevel_window_hidden(self):
        """
        Callback invoked after hiding the frontend's toplevel window.
        This may be triggered e.g. via the frontend's DBus API.
        """
        raise NotImplementedError()
