# -*- 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.plugins.poblesec.history import History

from elisa.core.input_event import *
from elisa.core.utils import defer

from elisa.core.utils import defer

from elisa.plugins.poblesec.transitions import SoftFadeFrontIn, SoftFadeFrontOut
from elisa.plugins.poblesec.transitions import SoftFadeBackIn, SoftFadeBackOut
from elisa.plugins.pigment.graph import group

try:
    import dbus
    from elisa.plugins.database.dbus_browser import DBusBrowser
except ImportError:
    dbus = None


class BrowserController(PigmentController):

    default_config = {'home': '/poblesec/sections_menu'}

    def __init__(self):
        super(BrowserController, self).__init__()
        self._showing_controller_dfr = None
        self._hiding_controllers = {}

    def initialize(self, home_path=None):
        dfr = super(BrowserController, self).initialize()

        def initialized(result):

            if home_path is not None:
                self._home_path = home_path
            else:
                self._home_path = self.config['home']

            # FIXME: hardcoded transitions
            self.transition_in = SoftFadeBackIn()
            self.transition_out = SoftFadeFrontOut()
            self.transition_back_in = SoftFadeFrontIn()
            self.transition_back_out = SoftFadeBackOut()

            self._initialize_dbus()
            return result

        dfr.addCallback(initialized)
        return dfr

    def set_frontend(self, frontend):
        super(BrowserController, self).set_frontend(frontend)

        self.history = History(frontend)
        self.history.connect('push_controller', self._push_controller_cb)
        self.history.connect('pop_controller', self._pop_controller_cb)

    def prepare(self):
        dfr = self.history.append_controller(self._home_path, 'home')

        # FIXME: PigmentController.set_frontend API does not return a deferred
        return dfr

    def _push_controller_cb(self, history, previous, current_dfr):
        current_dfr.addCallback(self._swap_new_controller, previous)
        current_dfr.addErrback(self._new_controller_creation_failed, previous)

    def _new_controller_creation_failed(self, failure, previous):
        try:
            previous.sensitive = True
        except AttributeError:
            pass

        return failure

    def _swap_new_controller(self, current, previous):
        self.debug("push new controller %s", current)
        if current != previous:
            # set the focus and sensitive to the new one
            self._show_controller(current, self.transition_in.apply)
            self.widget.set_focus_proxy(current.widget)
            if self.widget.focus:
                current.widget.set_focus()
            current.sensitive = True

            if previous != None:
                # and remove the old one, if still there
                previous.sensitive = False
                self._hide_controller(previous, self.transition_out.apply)
        return current

    def _pop_controller_cb(self, history, previous, current):
        self.debug("pop controller %s", previous)
        if current != previous:
            self._show_controller(current, self.transition_back_in.apply)
            self.widget.set_focus_proxy(current.widget)
            if self.widget.focus:
                current.widget.set_focus()
            self._hide_controller(previous, self.transition_back_out.apply)

    def _hide_controller(self, controller, transition):
        def remove_controller(controller):
            # the widget may have been already removed by
            # controller.widget.clean()
            if controller.widget in self.widget.get_children():
                self.widget.remove(controller.widget)
            controller.removed()
            del self._hiding_controllers[controller]

        def on_error(failure):
            del self._hiding_controllers[controller]
            failure.trap(defer.CancelledError)

        controller.sensitive = False
        dfr = transition(controller)
        self._hiding_controllers[controller] = dfr
        dfr.addCallback(remove_controller)
        dfr.addErrback(on_error)

        return dfr

    def _show_controller(self, controller, transition):
        if controller in self._hiding_controllers:
            self._hiding_controllers[controller].cancel()
        if self._showing_controller_dfr != None and \
           not self._showing_controller_dfr.called:
            self._showing_controller_dfr.cancel()

        if controller.widget not in self.widget.get_children():
            controller.widget.visible = False
            self._place_controller(controller)
            self.widget.add(controller.widget)
            controller.prepare()

        def on_error(failure):
            failure.trap(defer.CancelledError)

        def set_sensitive(controller):
            controller.sensitive = True

        self._showing_controller_dfr = transition(controller)
        self._showing_controller_dfr.addCallback(set_sensitive)
        self._showing_controller_dfr.addErrback(on_error)

        return self._showing_controller_dfr

    def _place_controller(self, controller):
        pass

    def handle_input(self, manager, input_event):
        if input_event.value == EventValue.KEY_GO_LEFT:
            if self.history != None:
                self.history.go_back()
            return True

        if not self.history.current:
            self.warning("No current frontend is set.")
            return

        return self.history.current.handle_input(manager, input_event)

    def _initialize_dbus(self):
        if dbus is None:
            # no dbus support
            return

        bus = dbus.SessionBus()
        self.bus_name = dbus.service.BusName('com.fluendo.Elisa', bus)

        self.dbus_browser = DBusBrowser(self, bus,
                '/com/fluendo/Elisa/Plugins/Poblesec/Browser', self.bus_name)

    def _clean_dbus(self):
        if dbus is None:
            # no dbus support
            return

        bus = dbus.SessionBus()
        self.dbus_browser.remove_from_connection(bus,
                '/com/fluendo/Elisa/Plugins/Poblesec/Browser')
        # BusName implements __del__, eew
        del self.bus_name

        # remove the reference cycle
        self.dbus_browser = None

    def clean(self):
        self._clean_dbus()

        def parent_clean(result):
            return super(BrowserController, self).clean()

        deferreds = list(self._hiding_controllers.values())

        if self._showing_controller_dfr is not None and \
                not self._showing_controller_dfr.called:
            deferreds.append(self._showing_controller_dfr)

        if len(deferreds) == 0:
            return parent_clean(None)

        dfr = defer.DeferredList(deferreds)
        dfr.addCallback(parent_clean)
        return dfr

