# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006,2007 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 Elisa with Fluendo's plugins.
#
# The GPL part of Elisa is also available under a commercial licensing
# agreement from Fluendo.
# See "LICENSE.Elisa" in the root directory of this distribution package
# for details on that license.

__maintainer__ = 'Benjamin Kampmann <benjamin@fluendo.com>'

"""
A View for playback the video (with visualisation support)
"""

from elisa.core import common, player

from pgm.graph.image import Image
from pgm.graph.group import Group
from pgm.graph.text import Text
from pgm.timing import implicit

import pgm, gst, math

from twisted.internet import reactor

from elisa.plugins.pigment.pigment_view import PigmentView

from elisa.extern.translation import gettexter, N_, Translatable
T_ = gettexter('elisa-player')

class VisualisationError(Exception):
    pass

# The Visualisation Fake
class ElisaVisualisationBin(gst.Bin):

    def __init__(self, visualisation, size, pgm_image):
        """
        The ElisaVisualisationBin is a wrapper for the different visualisation
        elements of GStreamer.

        @param visualisation:   the GStreamer Element to use
        @type visualisation:    string
        @param size:            a size in pixel to calculate the visualisation
                                size for
        @type size:             int
        @param pgm_image:       the pgm_image that is used for sinking
        @type pgm_image:         L{pgm.graph.image.Image}
        """

        gst.Bin.__init__(self)

        self._visualisation = visualisation
        self._pgm_image = pgm_image

        self._capsfilter = gst.element_factory_make('capsfilter')
        self.add(self._capsfilter)

        caps_pad = self._capsfilter.get_pad('src')

        self._visu = gst.element_factory_make(self._visualisation)
        self.add(self._visu)

        height = int(round(size * pgm_image.height / pgm_image.width))
        height = (height + 3) & ~3

        visu_sink = self._visu.get_pad('sink')
        visu_src = self._visu.get_pad('src')

        caps = visu_src.get_pad_template().caps

        if caps == None:
            raise VisualisationError("The Visualisation element %s does not" \
                                     " have any Caps." % self._visualisation)

        caps = caps.copy()

        if caps.is_fixed() == False:
            caps.make_writable()
            for i in xrange(0, caps.get_size()):
                cur_struct = caps.get_structure(i)

                cur_struct.fixate_field_nearest_int('width', size)

                cur_struct.fixate_field_nearest_int('height', height)
        else:
            raise VisualisationError("The Caps of the GstElement are fixed.")

        self._capsfilter.set_property('caps', caps)

        self._ghost_sink = gst.GhostPad('sink', visu_sink )
        self._ghost_src = gst.GhostPad('src', caps_pad)

        self._visu.link(self._capsfilter)
        self.add_pad(self._ghost_sink)
        self.add_pad(self._ghost_src)


class PlaybackView(PigmentView):

    bindings = (("current_index", "current_index"),
                ("state", "state"),
                ("visualisation", "visualisation"),
                ('hidden', 'hidden'),
                ("focused", "focused"))

    supported_controllers = ('player:player_controller',)

    config_doc = {'subtitle_font_size' : 'The size the subtitle font' \
                                         ' should have (in pgm-size)',
                   'visualisation_size' : 'the size the visualisation should'\
                                             ' use for calculating the real size'
                                         }
    default_config = {'subtitle_font_size' : '0.2',
                      'visualisation_size' : 400,
                    }

    def create_widgets(self):
        self.create_background()
        self.create_subtitles()
        self.create_interaction()

    def controller__set(self, new_controller):
        super(PigmentView, self).controller__set(new_controller)
        new_controller.player.video_sink = self.video_sink
        new_controller.player.subtitle_callback = self._update_subtitle_buffer
        
    def initialize(self):
        super(PigmentView, self).initialize()
        self._subtitle_font_size = float(self.config.get('subtitle_font_size',
                                                         '0.24'))

        # The id of the reactor.callLater, for the time, when the subtitle
        # should be cleared (because the duration of the buffer wants that)
        self._sub_cleaner = None


    def visualisation__set(self, value):
        if not value:
           return 
        try:
           self._visualisation = ElisaVisualisationBin(
                                    value,
                                    400,
                                    self._background)
        except:
            # changing didn't work
            path = common.application.log_traceback()
            self.warning("Chaning the visualisation to '%s' failed. Details" \
                         " can be found at %s" % (value, path))
            # return, so that in fact the old one is used
            return

        if self.controller and self._visualisation:
            self.controller.player.visualisation = self._visualisation 

    def state__set(self, value):
        if value == player.STATES.LOADING:
            self._enable_loading_animation(True)
        elif value == player.STATES.STOPPED:
            self._enable_loading_animation(False)
            self._background.clear()
        else:
            self._enable_loading_animation(False)
        return True

    def hidden__set(self, value):
        self._background.visible = not value
        if not value:
            self._background.clear()

    def focused__set(self, value):
        self._animated_background.opacity = value and 255 or 125
 
    def _background_clicked(self, drawable, x, y, z, button, time):
        if self.context_handle.visible == False or self.context_handle.opacity == 0.0:
            return False
        
        if (not self.controller.focused) or \
           (self._background.storage_type == pgm.IMAGE_EMPTY):
            return False
        
        if self.controller.state == player.STATES.STOPPED:
            return False

        return True
    
    def _enable_loading_animation(self, enabled):
        if enabled and self.frontend:
            if self._loading:
                return
            self._interaction.visible = True
            self.load_from_theme('waiting_icon', self._interaction)
            self._interaction.set_name('wait icon')
            self._loading = True
            reactor.callLater(0.023, self._transform_mapping_matrix_cb)
        else:
            self._loading = False
            self._interaction.clear()
            self._interaction.visible = False
                    
    def _transform_mapping_matrix_cb(self):
        if self._loading:
            self._interaction.mapping_matrix *= self._rotation_matrix
            reactor.callLater(0.017, self._transform_mapping_matrix_cb)

    def _update_subtitle_buffer(self, input_buffer):
        def clean():
            self._subtitles.label = ''
        
        if self._sub_cleaner and self._sub_cleaner.active():
            self._sub_cleaner.cancel()

        self._subtitles.markup = str(input_buffer)
        duration = input_buffer.duration
        if duration > 0:
            self._sub_cleaner = reactor.callLater(duration / gst.SECOND, clean)


    # creating the widgets
    def create_background(self):
        canvas = self.frontend.context.viewport_handle.get_canvas()
        
        self._background = Image()
        self._background.position = (0.0, 0.0, 0)
        self._background.size = canvas.size

        self._background.bg_color = (0, 0, 0, 0)
        self._background.opacity = 255
        self._background.visible = True
        self._background.connect("clicked", self._background_clicked)
        # FIXME: HACK to be sure that the background is really in the background!
        canvas.add(pgm.DRAWABLE_FAR, self._background)
        
        self._animated_background = implicit.AnimatedObject(self._background)
        self._animated_background.setup_next_animations(duration = 500,
                                                   transformation = implicit.DECELERATE)

        # create the output video sink and link it to the player
        video_sink = gst.element_factory_make('pgmimagesink')
        video_sink.set_property('image', self._background)
        self.video_sink = video_sink

    def create_subtitles(self):
        canvas = self.frontend.context.viewport_handle.get_canvas()
        
        # create the subtitles overlay
        self._subtitles = Text()
        # 2 lines of subtitles should fit
        self._subtitles.font_height = 0.5
        self._subtitles.weight = pgm.TEXT_WEIGHT_BOLD

        height = self._subtitle_font_size * 2.0
        self._subtitles.position = (0.0, canvas.height - height - 0.1, 1.1)
        self._subtitles.size = (canvas.width, height)
        
        self._subtitles.alignment = pgm.TEXT_ALIGN_CENTER
        self._subtitles.bg_color = (0, 0, 0, 0)
        self._subtitles.fg_color = (255, 255, 255, 255)
        self._subtitles.outline_width = self._subtitles.height * 0.015
        self._subtitles.font_family = "Nimbus Sans L Bold"
        self._subtitles.outline_color = (0, 0, 0, 255)
        self._subtitles.opacity = 255
        self._subtitles.visible = True
        self.context_handle.add(self._subtitles)

    def create_interaction(self):
        canvas = self.frontend.context.viewport_handle.get_canvas()

        # Rotating for the loading animation
        self._interaction = None
        self._loading = False
        self._rotation_matrix = pgm.mat4x4_new_identity()
        self._rotation_matrix.translate(0.5, 0.5, 0.0)
        self._rotation_matrix.rotate_z(math.pi / 30.0)
        self._rotation_matrix.translate(-0.5, -0.5, 0.0)

        self._interaction = Image()
        self._interaction.position = (0.0, 0.0, 0.0)
        width = canvas.width / 2.0
        height = canvas.height / 2.0
        self._interaction.width = width
        self._interaction.height = height
        self._interaction.x = width / 2.0
        self._interaction.y = height / 2.0

        # downscaling
        scale = pgm.mat4x4_new_identity()
        scale.translate(0.5, 0.5, 0.0)
        factor = 2.0
        scale.scale(factor, factor, 0.0)
        scale.translate(-0.5, -0.5, 0.0)

        self._interaction.bg_color = (0, 0, 0, 0)
        self._interaction.opacity = 255
        self._interaction.visible = True
        self._interaction.connect("clicked", self._background_clicked)
        self.context_handle.add(self._interaction)

    def element_attribute_set(self, position, key, old_value, new_value):
        # we need to have it?
        pass

