# -*- mode: python; coding: utf-8 -*-
#
# Pigment Python tools
#
# Copyright © 2006-2008 Fluendo Embedded S.L.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

import math
import time as mod_time

import pgm
from pgm.graph.group import Group
from pgm.graph.image import Image
from pgm.timing import implicit
from pgm.utils import maths
from pgm.widgets.list_ng import List
from pgm.widgets import const

class FiniteCircularList(List):

    def __init__(self):
        super(FiniteCircularList, self).__init__()
        self.orientation = const.HORIZONTAL
        self.drag_motion_resolution = 0.01

    def compute_width(self, index):
        # dividing by 2.0 because the items we insert are groups with an image
        # and its reflection below, ugly!
        return self.compute_height(index)/2.0

    def compute_height(self, index):
        return self._height / self._visible_range_size

    def compute_x(self, index):
        offset = math.pi/2.0
        a = index/self._visible_range_size*math.pi*2.0 + offset
        min_x = 0.0
        max_x = self._width - self._widget_width
        x = (1.0-math.cos(a))/2.0*(max_x-min_x) + min_x
        return x

    def compute_y(self, index):
        offset = math.pi/2.0
        a = index/self._visible_range_size*math.pi*2.0 + offset
        min_y = 0.0
        max_y = self._height/2.0
        y = (1.0+math.sin(a))/2.0*(max_y-min_y) + min_y
        return y

    def compute_z(self, index):
        offset = math.pi/2.0
        a = index/self._visible_range_size*math.pi*2.0 + offset
        min_z = -400.0
        max_z = 0.0
        z = (1.0+math.sin(a))/2.0*(max_z-min_z) + min_z
        return z

    def compute_opacity(self, index):
        offset = math.pi/2.0
        a = index/self._visible_range_size*math.pi*2.0 + offset
        min_opacity = 40
        max_opacity = 255
        opacity = (1.0+math.sin(a))/2.0*(max_opacity-min_opacity) + min_opacity
        return opacity

    def compute_zoom(self, index):
        offset = math.pi/2.0
        a = index/self._visible_range_size*math.pi*2.0 + offset
        min_zoom = 2.0
        max_zoom = 4.0
        zoom = (1.0+math.sin(a))/2.0*(max_zoom-min_zoom) + min_zoom
        return zoom

    def layout_widget(self, widget, position):
        zoom = compute_zoom(position)
        width = self._widget_width*zoom
        height = self._widget_height*zoom
        x = self.compute_x(position)-(width-self._widget_width)/2.0
        y = self.compute_y(position)-(height-self._widget_height)/2.0
        z = self.compute_z(position)
        opacity = self.compute_opacity(position)

        # update widget properties
        widget.position = (x, y, z)
        widget.size = (width, height)
        widget.opacity = opacity

    def selected_item__set(self, index):
        if len(self.widgets) == 0:
            return

        prev_selected = self._selected_item

        def positive_equivalent(index):
           index = index % len(self.widgets)
           if index < 0:
                index = len(self.widgets) - index
           return index

        def shortest_path(start, end):
            delta = end - start
            if abs(delta) > self.visible_range_size/2.0:
                if delta > 0:
                    delta -= self.visible_range_size
                else:
                    delta += self.visible_range_size
            return delta

        # new selected item clamped to [0, len(self)-1]
        self._selected_item = positive_equivalent(index)

        # saving the target of the animation and stopping it
        previous_target = self._animated.visible_range_start
        self._animated.stop_animations()

        # previous animation delta
        previous_delta = previous_target - self.visible_range_start
        # new animation delta
        new_delta = previous_delta + \
                    shortest_path(positive_equivalent(previous_target),
                                  self._selected_item)

        # reset the current visible_range_start with no visual change
        self.visible_range_start = positive_equivalent(self.visible_range_start)

        visible_range_start = self.visible_range_start + new_delta

        if self.animated:
            self._animated.visible_range_start = visible_range_start
        else:
            self.visible_range_start = visible_range_start

        if prev_selected != self._selected_item:
            self.emit('selected-item-changed', self._selected_item)

    def range_start_to_selected(self):
        selected = self.visible_range_start
        selected = int(round(selected))
        return selected

    def _layout(self):
        for i, widget in enumerate(self.widgets):
            position = -self._visible_range_start + i

            zoom = self.compute_zoom(position)
            width = self._widget_width*zoom
            height = self._widget_height*zoom

            # update widget properties
            widget.x = self.compute_x(position)-(width-self._widget_width)/2.0
            widget.y = self.compute_y(position)-(height-self._widget_height)/2.0
            widget.z = self.compute_z(position)
            widget.opacity = self.compute_opacity(position)
            widget.size = (width, height)

    def insert(self, index, widget):
        widget.connect('clicked', self._fcl_child_clicked)
        super(FiniteCircularList, self).insert(index, widget)
        Group.add(self, widget)
        self.visible_range_size = max(1.0, len(self))
        self._layout()

    def pop(self, index):
        widget = super(FiniteCircularList, self).pop(index)
        self.visible_range_size = max(1.0, len(self))
        self._layout()
        return widget

    def _fcl_child_clicked(self, drawable, x, y, z, button, time):
        index = self.widgets.index(drawable)
        self.emit('child-clicked', index)

        return True

    def do_clicked(self, x, y, z, button, time):
        # always let the children handle the clicks
        return False

    def do_drag_motion(self, x, y, z, button, time):
        # FIXME: copied from pgm.widgets.list_ng only to change the
        # sensitivity
        motion = 0

        if self._orientation == const.VERTICAL:
            motion = y - self._initial[1]
            self.visible_range_start -= motion / self._widget_height
        elif self._orientation == const.HORIZONTAL:
            motion = x - self._initial[0]
            self.visible_range_start -= motion / self._widget_width / 6.0

        time_delta = time - self._initial[2]
        if time_delta != 0:
            self.speed = motion/time_delta*1000.0
            self.speed = maths.clamp(self.speed, -14.0, 14.0)

        self._initial = (x, y, time)
        self._drag_accum += abs(motion) 

        return True

    deceleration = 8.0
    def _decelerate(self):
        self._previous_time = self._current_time
        self._current_time = mod_time.time()
        delta = self._current_time - self._previous_time

        if self.speed > 0.0:
            self.speed -= self.deceleration*delta
            if self.speed > 0.0:
                self.visible_range_start -= self.speed*delta
                return True

        elif self.speed < 0.0:
            self.speed += self.deceleration*delta
            if self.speed < 0.0:
                self.visible_range_start -= self.speed*delta
                return True

        self.selected_item = self.range_start_to_selected()
        return False

if __name__ == "__main__":
    import pgm
    import gobject
    import gst
    import glob, sys
    from pgm.graph.text import Text
    from pgm.graph.image import Image

    def create_text(label):
        txt = Text()
        txt.label = label
        txt.font_family = "Bitstream DejaVu"
        txt.fg_color = (255, 255, 255, 255)
        txt.bg_color = (255, 0, 0, 255)
        txt.ellipsize = pgm.TEXT_ELLIPSIZE_END
        txt.visible = True
        return txt

    def create_img(img_file):
        img = Image()
        img.set_from_file(img_file, 512)
        img.fg_color = (255, 255, 255, 255)
        img.bg_color = (100, 200, 100, 155)
        img.bg_color = (0, 0, 0, 0)
        img.visible = True
        return img

    def create_reflection(master_img):
        img = Image()
        img.set_from_image(master_img)
        img.fg_color = (255, 255, 255, 255)
        img.bg_color = (100, 100, 200, 155)
        img.bg_color = (0, 0, 0, 0)
#        img.width = -master_img.width
        img.height = master_img.height
        img.opacity = 30
#        img.x += master_img.width
        img.layout = pgm.IMAGE_SCALED
        img.y += master_img.height
        img.alignment = pgm.IMAGE_TOP
        img.visible = True
        return img

    def create_video(video_uri):
        img = Image()
        img.fg_color = (255, 255, 255, 255)
        img.bg_color = (0, 0, 0, 0)
        img.alignment = pgm.IMAGE_LEFT
        img.visible = True

        # GStreamer pipeline setup
        pipeline = gst.element_factory_make('playbin')
        sink = gst.element_factory_make('pgmimagesink')
        pipeline.set_property('uri', video_uri)
        pipeline.set_property('video-sink', sink)
        sink.set_property('image', img)
        pipeline.set_state(gst.STATE_PLAYING)

        return img


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

            elif event.keyval == pgm.keysyms.Down or \
                 event.keyval == pgm.keysyms.Right:
#                widget.selected_item = (widget.selected_item + 1) % len(widget)
                widget.selected_item += 1

            elif event.keyval == pgm.keysyms.Up or \
                 event.keyval == pgm.keysyms.Left:
#                widget.selected_item = (widget.selected_item - 1) % len(widget)
                widget.selected_item -= 1

            elif event.keyval == pgm.keysyms.space:
                #widget.insert(0, create_text("T"))
                def test():
#                    img = create_img("/home/kaleo/dev/pigment/examples/pictures/fluendo.png")
                    """
                    list_widget.insert(0, img)
                    list_widget.pop(len(list_widget)-1)
                    """
                    img = list_widget.pop(0)
                    list_widget.append(img)
                    return True
                gobject.timeout_add(1000, test)
#                list_widget.append(img)

            # remove the currently selected item
            elif event.keyval == pgm.keysyms.Return:
                print "popping", widget.selected_item
                widget.pop(widget.selected_item)

    def on_delete(viewport, event):
        pgm.main_quit()


    # OpenGL viewport creation
    factory = pgm.ViewportFactory('opengl')
    gl = factory.create()
    gl.title = 'Finite circular list widget'

    # Canvas and image drawable creation
    canvas = pgm.Canvas()

    # Bind the canvas to the OpenGL viewport
    gl.set_canvas(canvas)
    gl.show()

    list_widget = FiniteCircularList()
    list_widget.position = (0.5, 0.0, 0.0)
    list_widget.width = 3.0
    list_widget.height = 3.0
    list_widget.visible = True
    list_widget.canvas = canvas

    files = sys.argv[1:]
    for file in files[:5]:
        print file
        image = create_img(file)
        list_widget.append(image)
        """
        # reflection code
        image.alignment = pgm.IMAGE_BOTTOM
        reflection = create_reflection(image)
        g = Group(canvas, pgm.DRAWABLE_MIDDLE)
        g.add(image)
        g.add(reflection)
        g.visible = True
        list_widget.append(g)
        """
    
    # Let's start a mainloop
    gl.connect('key-press-event',
               on_key_press,
               list_widget)
    gl.connect('delete-event', on_delete)
    pgm.main()
