# -*- 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: Florian Boucault <florian@fluendo.com>

from twisted.internet import reactor
from twisted.internet.task import LoopingCall
from elisa.plugins.pigment.animation.implicit import AnimatedObject, SMOOTH

from elisa.core.utils.i18n import install_translation

from elisa.plugins.pigment.graph.image import Image
from elisa.plugins.pigment.graph.text import Text
from elisa.plugins.pigment.widgets.widget import Widget
from elisa.plugins.pigment.widgets.box import HBox, VBox, ALIGNMENT
from elisa.plugins.pigment.widgets.theme import Theme

_ = install_translation('poblesec')


class Dots(VBox):
    """
    Infinite sequential animation of three dots laid out vertically. They fade
    in one at a time starting from the bottom one. Once they are all faded in,
    they all fade out and the cycle restarts.
    """

    def __init__(self):
        super(Dots, self).__init__()
        self._looping_animation_call = LoopingCall(self._animate_loading)
        self.alignment = ALIGNMENT.END

        self._count = 0
        self._dots = []
        for i in xrange(3):
            dot = self._create_dot()
            self.pack_end(dot, expand=True)
            animated = AnimatedObject(dot)
            animated.setup_next_animations(duration=150,
                                           transformation=SMOOTH)
            self._dots.append(animated)

        self.update_style_properties(self.style.get_items())

    def clean(self):
        #FIXME: we probably leave a circular reference between dots and their
        # animated objects.
        super(Dots, self).clean()
        self.stop()
        self._background = None
        self._foreground = None
        self._animated = None

    def _create_dot(self):
        dot = Image()
        dot.bg_a = 0
        dot.opacity = 0
        dot.visible = True
        resource = 'elisa.plugins.poblesec.widgets.player.dot'
        path = Theme.get_default().get_resource(resource)
        dot.set_from_file(path)
        return dot

    def _hide_dots(self):
        for dot in self._dots:
            dot.opacity = 0
        self._count = 0

    def _animate_loading(self):
        if self._count == 3:
            self._hide_dots()
        else:
            opacities = [255, 153, 77]
            dot = self._dots[self._count]
            dot.opacity = opacities[self._count]
            self._count += 1

    def start(self):
        """
        Start animation.
        """
        if not self._looping_animation_call.running:
            self._hide_dots()
            self._looping_animation_call.start(0.3)

    def stop(self):
        """
        Stop the animation.
        """
        if self._looping_animation_call.running:
            self._looping_animation_call.stop()

class Counter(Widget):
    """
    Display a loading feedback containing:
     - a "Loading Photo" label
     - a "Photo x of y" label
     - a subtle loading animation
     - a tranversal gradient below these to allow readability

    It also provides facilities to fade in and fade out on its own.

    @ivar background: gradient allowing readability
    @type background: L{elisa.plugins.pigment.graph.image.Image}
    @ivar captions_and_dots: container for L{captions} and L{dots}
    @type captions_and_dots: L{elisa.plugins.pigment.widgets.box.HBox}
    @ivar dots: subtle loading animation
    @type dots: L{elisa.plugins.poblesec.widgets.player.counter.Dots}
    @ivar captions: container for L{caption} and L{subcaption}
    @type captions: L{elisa.plugins.pigment.widgets.box.VBox}
    @ivar caption: label with "Loading Photo"
    @type caption: L{elisa.plugins.pigment.graph.text.Text}
    @ivar subcaption: label with "Photo x of y"
    @type subcaption: L{elisa.plugins.pigment.graph.text.Text}
    """

    def __init__(self):
        super(Counter, self).__init__()
        self.animated.setup_next_animations(transformation=SMOOTH,
                                            duration=300)

        self.loading = False
        self._delayed_show = None
        self._index = 0
        self._max = 0

        self._create_widgets()
        self.update_style_properties(self.style.get_items())

    def clean(self):
        super(Counter, self).clean()
        self.dots = None
        self.caption = None
        self.subcaption = None

    def _create_widgets(self):
        self.background = Image()
        self.background.visible = True
        self.add(self.background)
        resource = 'elisa.plugins.poblesec.widgets.player.gradient'
        path = Theme.get_default().get_resource(resource)
        self.background.set_from_file(path)

        self.captions_and_dots = HBox()
        self.captions_and_dots.visible = True
        self.add(self.captions_and_dots)

        self.dots = Dots()
        self.dots.visible = True
        self.captions_and_dots.pack_start(self.dots)

        self.captions = Widget()
        self.captions.visible = True
        self.captions_and_dots.pack_start(self.captions)

        self.caption = Text()
        self.caption.label = _("Loading Photo")
        self.caption.visible = True
        self.captions.add(self.caption)

        self.subcaption = Text()
        self.subcaption.visible = True
        self.captions.add(self.subcaption)

    def _update_subcaption(self):
        markup = "Photo <b>%s</b> of <b>%s</b>" % (self._index+1, self._max)
        self.subcaption.markup = markup

    def _cancel_delayed_show(self):
        if self._delayed_show != None and self._delayed_show.active():
            self._delayed_show.cancel()
            self._delayed_show = None

    def _do_show(self):
        self._delayed_show = None
        self.animated.opacity = 255

    def set_loading(self, value):
        """
        Start/stop the subtle loading animation.

        @param value: whether it should start or stop the loading animation
        @type value:  bool
        """
        old_value = self.loading
        self.loading = value

        if value == old_value:
            return

        if value:
            self.dots.start()
        else:
            self.dots.stop()

    def show(self, delay=0.0):
        """
        Fade in the widget after L{delay} seconds (0 seconds by default).
        If a fade in was already scheduled, nothing is done to change it.

        @param delay: time in seconds after which the fade in should happen
        @type delay:  float
        """
        if delay == 0.0:
            self._cancel_delayed_show()
            self._do_show()
        elif self._delayed_show == None:
            self._delayed_show = reactor.callLater(delay, self._do_show)

    def hide(self):
        """
        Fade out the widget.
        """
        self._cancel_delayed_show()
        self.animated.opacity = 0

    def set_index(self, index):
        """
        Set the 'x' value in "Photo x of y".
        """
        self._index = index
        self._update_subcaption()

    def set_max(self, max):
        """
        Set the 'y' value in "Photo x of y".
        """
        self._max = max
        self._update_subcaption()
