# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006-2008 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.

import pgm
from pgm.graph.group import Group
from pgm.graph.image import Image
from pgm.timing import implicit
import threading

class AnimationInProgress(Exception):
    pass

class StatusBar(Group):

    def __init__(self, width=1.0, height=2.0):
        Group.__init__(self)

        # FIXME: Group.size is not overriden
        self._width = width
        self._height = height
        
        self._attributes = []
        self._widgets = []
        self._animation_lock = threading.Lock()
        # FIXME: there can be more than one animated widget
        self._current_animated_widget = None

        # mouse support
        self._drag_zone = Image()
        Group.add(self, self._drag_zone)
        self._drag_zone.bg_color = (255, 0, 0, 0)
        self._drag_zone.size = (width, height)
        self._drag_zone.visible = True

        # background
        self._background = Image()
        Group.add(self, self._background)
        self._background.bg_color = (0, 255, 0, 0)
        self._background.layout = pgm.IMAGE_FILLED
        self._background.interp = pgm.IMAGE_NEAREST 
        self._background.size = (width, height)
        self._background.visible = True

    def _compute_position(self, index):
        x = 0.0
        y = 0.0
        z = 0.2
        
        i = 0
        for widget in self._widgets[:-1]:
            if i >= index:
                break
            x += widget.static_object.width*0.88
            z -= 0.02
            i += 1

        position = (x, y, z)
        return position

    def _compute_opacity(self, index):
        return 255

    def _compute_attributes(self, index):
        return (self._compute_position(index),
                self._compute_opacity(index))

    def _layout(self):
        self._attributes = [ self._compute_attributes(i)
                             for i in xrange(len(self)) ]

    def _update(self):
        i = 0
        for widget in self._widgets:
            widget.opacity  = self._attributes[i][1]
            widget.position = self._attributes[i][0]
            i += 1

    def width__get(self):
        return self._width

    def width__set(self, width):
        self._width = width
        self._update()
        self._drag_zone.width = width
        self._background.width = width

    def height__get(self):
        return self._height

    def height__set(self, height):
        self._height = height
        self._update()
        self._drag_zone.height = height
        self._background.height = height
        for widget in self._widgets:
            widget.height = height*0.95
            widget.regenerate()

    def size__set(self, size):
        self.width = size[0]
        self.height = size[1]

    def size__get(self):
        return (self.width, self.height)

    def __len__(self):
        return len(self._widgets)

    def __getitem__(self, index):
        return self._widgets[index].static_object

    def _is_visible(self, index, widget):
        x, y, z = self._compute_position(index)
        widget_right_corner = x + widget.width
        list_right_corner = self.x + self.width
        visible = widget_right_corner <= list_right_corner
        return visible

    def insert(self, index, widget):
        # proportional resize otherwise it's awful
        k = self._height*0.95 / widget.height
        widget.height *= k
        widget.width *= k
        widget.regenerate()

        if self._animation_lock.locked():
            settings = {'duration': 50}
            self._current_animated_widget.update_animation_settings(**settings)
            self._animation_lock.release()

        self._animation_lock.acquire(False)

        def done(ctrl):
            if self._animation_lock.locked():
                self._animation_lock.release()
            self._current_animated_widget = None
            

        animated_widget = implicit.AnimatedObject(widget)
        animated_widget.setup_next_animations(duration=300,
                                              transformation=implicit.DECELERATE,
                                              end_callback=done)
        self._current_animated_widget = animated_widget
 
        self._widgets.insert(index, animated_widget)
 
        # FIXME: evil HACK
        if self._is_visible(index, widget) or True:
            # FIXME: why adding only here?
            # more efficient is to add it to the Group and then check that its
            # bottom right corner x-coordinate is positive and below self.width
            Group.add(self, widget)

            # FIXME: can be factorised
            prev_position = self._compute_position(index-1)
            if len(self._widgets) == 1:
                prev_position = (-widget.width, prev_position[1],
                                 prev_position[2])

            widget.position = prev_position

            # FIXME: can be factorised
            if len(self._widgets) == 1:
                widget.opacity = 0

            self._attributes.insert(index, self._compute_attributes(index))

            animated_widget.opacity  = self._attributes[index][1]

            if len(self._widgets) == 1:
                # do not animate the first one
                widget.position = self._attributes[index][0]
            else:
                widget.y = self._attributes[index][0][1]
                widget.z = self._attributes[index][0][2]
                animated_widget.x = self._attributes[index][0][0]
        else:
            self._animation_lock.release()

        widget.visible = True

    def append(self, widget):
        self.insert(len(self), widget)

    def remove(self, widget):
        static_widgets =  [ w.static_object for w in self._widgets ]
        index = static_widgets.index(widget)
        self.pop(index)

    # FIXME: default should be -1
    def pop(self, index=None):
        if self._animation_lock.locked():
            settings = {'duration': 50}
            self._current_animated_widget.update_animation_settings(**settings)
            self._animation_lock.release()

        self._animation_lock.acquire(False)

        if index is None:
            # pop last item by default
            index = len(self._widgets) - 1

        def done(ctrl):
            static = animated_widget.static_object
            Group.remove(self, static)
            if self._animation_lock.locked():
                self._animation_lock.release()
            self._current_animated_widget = None

        animated_widget = self._widgets.pop(index)
        self._current_animated_widget = animated_widget

        settings = {'duration': 300,
                    'transformation': implicit.ACCELERATE,
                    'end_callback': done}
        animated_widget.setup_next_animations(**settings)

        try:
            x_offset = self._widgets[index-1].width
        except IndexError:
            x_offset = animated_widget.width

        if len(self._widgets) == 0:
            animated_widget.opacity = 0
        else:
            animated_widget.x -= x_offset
        
        return animated_widget.static_object

    def head(self):
        try:
            widget = self._widgets[-1].static_object
        except IndexError:
            widget = None
        return widget

    def background__set(self, background_file):
        self._background.set_from_file(background_file)

if __name__ == "__main__":
    import os
    import gobject
    import gst
    from pgm.timing import implicit


    def on_key_press(viewport, event, widget):
        if event.type == pgm.KEY_PRESS:
            # quit on q or ESC
            if event.keyval in (pgm.keysyms.q, pgm.keysyms.Escape):
                pgm.main_quit()

            elif event.keyval in (pgm.keysyms.Down, pgm.keysyms.Right):
                path = Image()
                path.visible = True
                widget.append(path)

            elif event.keyval in (pgm.keysyms.Up, pgm.keysyms.Left):
                widget.pop()

    def on_delete(viewport, event):
        pgm.main_quit()
        
    # OpenGL viewport creation
    factory = pgm.ViewportFactory('opengl')
    gl = factory.create()
    gl.title = 'Status bar widget'

    # Canvas and image drawable creation
    canvas = pgm.Canvas()
    
    # Bind the canvas to the OpenGL viewport
    gl.set_canvas(canvas)
    gl.show()
    
    status = StatusBar()
    status.canvas = canvas
    status.position = (0.0, 0.0, 0.0)
    status.width = 3.0
    status.height = 0.2
    status.visible = True

    # Let's start a mainloop
    gl.connect('key-press-event', on_key_press, status)
    gl.connect('delete-event', on_delete)
    pgm.main()
