# -*- 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.
#
# Author: Guillaume Emont <guillaume@fluendo.com>
#

from elisa.plugins.pigment.animation import implicit, animation, ticker

from twisted.trial.unittest import TestCase
from twisted.internet import defer, reactor, task

class SomeObject(object):
    def __init__(self):
        super(SomeObject, self).__init__()
        self.val = 0


class TestAnimationMixin:
    def setUp(self):
        self._ticker = ticker.Ticker()
        animation.Animation.set_ticker(self._ticker)
        self._tick_loop = task.LoopingCall(self._ticker.tick)
        self._tick_loop.start(0.05)
        self._obj = SomeObject()
        self._animated = implicit.AnimatedObject(self._obj)

    def tearDown(self):
        self._tick_loop.stop()


class TestCallback(TestAnimationMixin, TestCase):
    timeout = 1.0

    def setUp(self):
        super(TestCallback, self).setUp()
        self._callback_dfr = defer.Deferred()
    
    def test_simple(self):

        def end_callback(*args):
            self._callback_dfr.callback(None)
            self._callback_dfr = None
            self.assertEquals(self._obj.val, 255)

        self._animated.setup_next_animations(end_callback=end_callback,
                                       duration=int(1000*(self.timeout-0.1)))
        self._animated.val = 255
        return self._callback_dfr


    def _test_order(self, first_animation_duration, setup_delay):
        """
        Check that an end callback set up with setup_next_animations() is
        called at the end of said next animations, and not called at the end
        of a current animation.
        """
        def end_callback(*args):
            self._callback_dfr.callback(None)
            self._callback_dfr = None
            self.failUnlessEqual(self._obj.val, 255)

        def setup_second_animation():
            self._animated.mode = implicit.REPLACE
            self._animated.setup_next_animations(duration=200,
                                                 end_callback=end_callback)
            self._animated.val = 255
            
        self._animated.setup_next_animations(duration=first_animation_duration)
        self._animated.val = 128

        reactor.callLater(setup_delay, setup_second_animation)

        return self._callback_dfr

    def test_order_running_animation(self):
        return self._test_order(200, 0.1)

    def test_order_stopped_animation(self):
        return self._test_order(200, 0.3)

class TestAnimationReuse(TestAnimationMixin, TestCase):
    timeout = 1.0

    def _test_duration(self, offset):
        # a tenth of timeout, expressed in ms
        duration = int((self.timeout * 1000)/10)

        dfr = defer.Deferred()
        
        self._animated.setup_next_animations(duration=duration)
        self._animated.val = 128
        # Don't try that at home, kids, only borderline unit tests are allowed
        # to get private data like that.
        animation = self._animated._animations['val']
        self.failUnlessEqual(animation.duration, duration)

        def end_callback(dummy):
            dfr.callback(None)

        def second_animation():
            self._animated.setup_next_animations(duration=duration*2,
                                                 end_callback=end_callback)
            self._animated.val = 255
            animation = self._animated._animations['val']
            self.failUnlessEqual(animation.duration, duration*2)

        reactor.callLater(duration/1000.0 + offset, second_animation)
        return dfr

    def test_duration_stopped_animation(self):
        return self._test_duration(0.05)

    def test_duration_running_animation(self):
        return self._test_duration(-0.05)

