# -*- coding: utf-8 -*-
# Moovida - Home multimedia server
# Copyright (C) 2007-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.
#
# Author: Guido Amoruso <guidonte@fluendo.com>

from twisted.trial.unittest import TestCase
from twisted.internet import defer

from elisa.core import common
from elisa.core.config import Config
from elisa.core.input_event import EventValue

from elisa.plugins.pigment.pigment_frontend import PigmentFrontend
from elisa.plugins.pigment.pigment_controller import PigmentController

from elisa.plugins.poblesec.browser_controller import BrowserController
from elisa.plugins.poblesec.transitions import Transition

import pkg_resources


class FakeController(PigmentController):
    def handle_input(self, manager, input_event):
        pass

class DummyController(PigmentController):
    model = []


class FakePluginRegistry(object):
    def create_component(self, *args, **kwargs):
        return defer.succeed(DummyController())

    def get_enabled_plugins(self):
        return ['elisa-plugin-poblesec']


class FakeBus(object):
    def register(self, callback, message_type):
        pass


class FakeApplication(object):
    config = Config()
    bus = FakeBus()
    plugin_registry = FakePluginRegistry()

    def log_failure(self, failure):
        pass


class BrokenFrontend(object):
    pass


class FakeTransition(object):
    def __init__(self, broken=False):
        self._broken = broken

    def __call__(self, controller):
        if self._broken:
            return defer.fail(controller)
        else:
            return defer.succeed(controller)


class FakeManager(object):
    pass


class FakeInputEvent(object):
    value = None


class TestBrowserController(TestCase):

    def setUp(self):
        self._patch_application()
        self._patch_dbus()

        self.frontend = PigmentFrontend()

        # Load the controller and decorator mappings for poblesec
        requirement = pkg_resources.Requirement.parse('elisa-plugin-poblesec')
        poblesec = pkg_resources.working_set.find(requirement)
        self.frontend.plugin_status_changed_cb(poblesec, True)

        self.frontend.add_controller("/fake/controller", FakeController)
        self.frontend.add_controller("/dummy/controller", DummyController)
        dfr = BrowserController.create()

        def created(controller):
            controller.transition_in       = Transition()
            controller.transition_out      = Transition()
            controller.transition_back_in  = Transition()
            controller.transition_back_out = Transition()
            self.browser = controller

        dfr.addCallback(created)
        return dfr

    def tearDown(self):
        def cleaned(result):
            self._unpatch_dbus()
            self._unpatch_application()
            return result

        return self.browser.clean().addCallback(cleaned)

    def _patch_application(self):
        self._old_application = common.application
        common.application = FakeApplication()

    def _unpatch_application(self):
        common.application = self._old_application

    def _patch_dbus(self):
        # overwrite the DBus initialize
        self._dbus_init = BrowserController._initialize_dbus
        self._dbus_clean = BrowserController._clean_dbus
        BrowserController._initialize_dbus = lambda *args: None
        BrowserController._clean_dbus = lambda *args: None

    def _unpatch_dbus(self):
        BrowserController._initialize_dbus = self._dbus_init
        BrowserController._clean_dbus = self._dbus_clean

    def test_initialize(self):
        self.failUnlessEqual(self.browser._home_path, self.browser.default_config['home'])

        home_path = '/dummy/controller'
        self.browser.initialize(home_path)
        self.failUnlessEqual(self.browser._home_path, home_path)

    def test_set_frontend(self):

        self.browser.set_frontend(self.frontend)
        dfr = self.browser.prepare()

        def on_append(result):
            self.failUnlessEqual(result.path, self.browser._home_path)

            self.failUnlessIdentical(self.browser.history.current, result)
            self.failUnlessEqual(self.browser.history.index, 0)
            self.failUnlessEqual(self.browser.history._controllers, [result])
            self.failUnlessEqual(len(self.browser.history._controllers), 1)

        dfr.addCallback(on_append)

        return dfr

    def test_set_frontend_broken(self):

        frontend = BrokenFrontend()
        self.browser.set_frontend(frontend)
        dfr = self.browser.prepare()
        self.failUnlessFailure(dfr, AttributeError)

        return dfr

    def test__push_controller_cb(self):

        previous = FakeController()
        current = FakeController()

        self.browser._push_controller_cb(None, previous, defer.succeed(current))
        self.failUnlessEqual(current.widget.focus, True)

    def test__pop_controller_cb(self):

        previous = FakeController()
        current = FakeController()

        self.browser._pop_controller_cb(None, previous, current)
        self.failUnlessEqual(current.widget.focus, True)

    def test__hide_controller(self):
        #self.browser.initialize()

        controller = FakeController()
        transition = FakeTransition()

        self.browser.widget.add(controller.widget)

        dfr = self.browser._hide_controller(controller, transition)

        def removed_controller(controller):
            self.failUnlessEqual(self.browser.widget.get_children(), [])
            self.failUnlessEqual(self.browser._hiding_controllers, {})

        dfr.addCallback(removed_controller)

        return dfr

    def test__hide_controller_broken(self):

        controller = FakeController()
        transition = FakeTransition(broken=True)

        self.browser.widget.add(controller.widget)

        dfr = self.browser._hide_controller(controller, transition)

        def error_removing(err):
            self.failUnlessEqual(self.browser.widget.get_children(), [controller.widget])
            self.failUnlessEqual(self.browser._hiding_controllers, {})

        dfr.addErrback(error_removing)

        return dfr

    def test__show_controller(self):

        controller = FakeController()
        transition = FakeTransition()

        dfr = self.browser._show_controller(controller, transition)

        self.failUnlessEqual(self.browser.widget.get_children(), [controller.widget])
        x, y = (controller.widget.x, controller.widget.y)
        width, height = (controller.widget.width, controller.widget.height)
        self.failUnlessEqual((x, y, width, height), (0, 0, 1.0, 1.0))

        return dfr

    def test__show_controller_broken(self):

        controller = FakeController()
        transition = FakeTransition(broken=True)

        dfr = self.browser._show_controller(controller, transition)

        self.failUnlessEqual(self.browser.widget.get_children(), [controller.widget])
        x, y = (controller.widget.x, controller.widget.y)
        width, height = (controller.widget.width, controller.widget.height)
        self.failUnlessEqual((x, y, width, height), (0, 0, 1.0, 1.0))

        def error_showing(err):
            self.failUnlessEqual(self.browser.widget.get_children(), [controller.widget])
            self.failUnlessEqual(self.browser._hiding_controllers, {})

        dfr.addErrback(error_showing)

        return dfr

    def test_handle_input(self):
        self.browser.set_frontend(self.frontend)

        manager = FakeManager()
        event = FakeInputEvent()
        self.browser.handle_input(manager, event)

        event.value = EventValue.KEY_GO_LEFT
        self.browser.handle_input(manager, event)

#        dfr = self.browser.history.append_controller("/fake/controller", "Fake")
#        dfr.addCallback(lambda res: self.browser.history.append_controller("/fake/controller", "Dummy"))
#
#        def handle_input(result):
#            self.browser.handle_input(manager, event)
#
#        dfr.addCallback(handle_input)
#
#        return dfr
