# -*- 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.

from pgm.timing import implicit
from pgm.widgets import list_ng, const

class ScrolledList(list_ng.List):

    def __init__(self, width=1.0, height=2.0,
                 visible_range_size=7, orientation=const.VERTICAL):
        self._hbar = None
        self._vbar = None
        self._hbar_pos = None
        self._vbar_pos = None
        self._vbar_spacing = 0
        self._hbar_spacing = 0
        self._selector = None
        self._selector_spacing = 0
        super(ScrolledList, self).__init__(width, height,
                                           visible_range_size, orientation)
 
    def set_selector(self, selector, spacing=0.05):
        self._selector = selector

        self._selector_spacing = spacing
        self.add(self._selector)
        self._selector.z = - 1.0
        x = self.compute_x(self.selected_item)
        y = self.compute_y(self.selected_item)

        spacing = self._selector_spacing
                
        if self._orientation == const.VERTICAL:
            x -= spacing / 2.
            y -= spacing / 2.
        elif self._orientation == const.HORIZONTAL:
            x -= spacing
            y -= spacing
        self._selector.x = x
        self._selector.y = y

        self._animated_selector = implicit.AnimatedObject(self._selector)
        settings = {'duration': 200,
                    'transformation': implicit.DECELERATE}
        self._animated_selector.setup_next_animations(**settings)
        self._animated_selector.mode = implicit.REPLACE

        self._update_selector()
        self._layout_selector()
        self._selector.visible = True

    def selector__get(self):
        return self._selector
        
    def set_horizontal_scrollbar(self, bar, position, spacing=0.05):
        self._hbar = bar
        self._hbar_pos = position
        self._hbar.items_number = self.visible_range_size
        self._hbar.width = self.width
        self._hbar_spacing = self._hbar.height + spacing
        self._hbar.connect('index-changed', self._bar_index_changed)
        self._update_bars()
        
    def set_vertical_scrollbar(self, bar, position, spacing=0.05):
        self._vbar = bar
        self._vbar_pos = position
        self._vbar.items_number = self.visible_range_size
        self._vbar.height = self.height
        self._vbar_spacing = self._vbar.width + spacing
        self._vbar.connect('index-changed', self._bar_index_changed)
        self.add(self._vbar)
        self._update_bars()

    def _bar_index_changed(self, bar, new_index):
        if new_index != self.selected_item:
            self.selected_item = new_index

    def visible_range_size__set(self, visible_range_size):
        list_ng.List.visible_range_size__set(self, visible_range_size)
        self._update_selector()

    def insert(self, index, widget):
        empty = len(self.widgets) == 0
        try:
            list_ng.List.insert(self, index, widget)
        finally:
            if not empty:
                self._update_bars()
            self._layout_selector()
            
    def pop(self, index=None):
        try:
            item = list_ng.List.pop(self, index)
        finally:
            self._update_bars()
            self._layout_selector()
        return item

    def selected_item__set(self, index):
        # always keep the selected item in [0, len(self.widgets))
        if index < 0:
            self._animated.visible_range_start = 0
            index = 0
        elif index >= len(self.widgets):
            self._animated.visible_range_start = \
                    max(0, len(self.widgets) - self.visible_range_size)
            index = len(self.widgets) - 1

        list_ng.List.selected_item__set(self, index)
        
        if self._vbar:
            self._vbar.cursor_index = index
        if self._hbar:
            self._hbar.cursor_index = index

        self._layout_selector()

    def size__set(self, size):
        list_ng.List.size__set(self, size)
        self._update_bars()
        self._update_selector()

    def width__set(self, width):
        list_ng.List.width__set(self, width)
        self._update_bars()
        self._update_selector()

    def height__set(self, height):
        list_ng.List.height__set(self, height)
        self._update_bars()
        self._update_selector()

    def position__set(self, position):
        list_ng.List.position__set(self, position)
        self._update_bars()

    def compute_x(self, index):
        x = list_ng.List.compute_x(self, index)
        if self._vbar:
            if self._vbar_pos == const.LEFT:
                x += self._vbar_spacing
            else:
                x -= self._vbar_spacing

        return x

    def compute_y(self, index):
        y = list_ng.List.compute_y(self, index)
        if self._hbar:
            if self._hbar_pos == const.TOP:
                y += self._hbar_spacing
            else:
                y -= self._hbar_spacing
        return y
        
    def _update_bars(self):
        x, y, z = self.position

        # toggle scrollbars when no scroll needed
        visible = len(self.widgets) > self.visible_range_size
        if self._vbar:
            self._vbar.visible = visible
        if self._hbar:
            self._hbar.visible = visible
        
        if self._vbar:
            self._vbar.items_number = len(self.widgets)
            self._vbar.height = self.height
            
            if self._vbar_pos == const.LEFT:
                self._vbar.position = (0, 0, z)
            elif self._vbar_pos == const.RIGHT:
                x = self.width - self._vbar_spacing
                self._vbar.position = (x, 0, z)
            
        if self._hbar:
            self._hbar.items_number = len(self.widgets)
            self._hbar.width = self.width
            
            if self._hbar_pos == const.TOP:
                self._hbar.position = (0, 0, z)
            elif self._hbar_pos == const.BOTTOM:
                y = self.height - self._hbar_spacing
                self._hbar.position = (0, y, z)

    def _layout_selector(self):
        if self._selector:
            if len(self.widgets) > 0:
                selected_item = self.selected_item

                # FIXME: copied from Grid... Might be better to factorize in
                #        list_ng directly.
                half_size = (self.visible_range_size - 1.0) / 2.0
                position = -self._animated.visible_range_start + \
                            int(self.selected_item)

                lower_limit = half_size
                upper_limit = -self._animated.visible_range_start + \
                              len(self.widgets) - half_size

                if (position <= lower_limit) or (position >= upper_limit):
                    selected_item = position

                x = self.compute_x(selected_item)
                y = self.compute_y(selected_item)

                spacing = self._selector_spacing
                
                if self._orientation == const.VERTICAL:
                    x -= spacing / 2.
                    y -= spacing / 2.
                elif self._orientation == const.HORIZONTAL:
                    x -= spacing
                    y -= spacing

                if self._selector.visible and self._selector.opacity != 0:
                    self._animated_selector.position = (x, y, -1.0)
                else:
                    self._selector.position = (x, y, -1.0)
            else:
                self._selector.visible = False

    def _update_selector(self):
        if self._selector:
            width, height = self._widget_width*1.1, self._widget_height
            height += self._selector_spacing
            width += self._selector_spacing * 2.5
 
            self._selector.size = (width, height)

    def do_drag_begin(self, x, y, z, button, time):
        super(ScrolledList, self).do_drag_begin(x, y, z, button, time)
        self._animated_selector.opacity = 0

    def _stop_deceleration(self):
        super(ScrolledList, self)._stop_deceleration()
        self._animated_selector.opacity = 255

if __name__ == "__main__":
    import pgm
    import gobject
    import operator, os, sys
    from pgm.graph.image import Image
    from pgm.graph.text import Text
    from pgm.graph.group import Group
    from pgm.timing import implicit

    from pgm.widgets.sliced_image import SlicedImage
    from pgm.widgets.scrollbar import Scrollbar
    import selector

    class ListItem(Group):

        def __init__(self, thumbnail=None, description=None, arrow=None):
            Group.__init__(self)
            
            self.thumbnail = Image()
            self.thumbnail.bg_color = (0, 255, 0, 255)
            #self.thumbnail.bg_color = (0, 0, 0, 0)
            self.thumbnail.layout = pgm.IMAGE_SCALED
            self.thumbnail.visible = True
            
            self._description = self._create_text()
            #self._description.bg_color = (255, 0, 0, 255)
            
            self.add(self.thumbnail)
            self.add(self._description)

            if thumbnail is not None:
                self.thumbnail.set_from_file(thumbnail)

            self.description = description

            if self.canvas:
                self._layout()

            # Arrow support disabled until some decision is taken.
            """
            arrow_x = text_x + text_width
            arrow_y = 0.4
            arrow_width = 0.25
            arrow_height = 0.8

            self.arrow = Image()
            self.arrow.bg_color = (0, 0, 0, 0)
            #self.arrow.bg_color = (0, 0, 255, 255)
            self.arrow.layout = pgm.IMAGE_SCALED
            self.arrow.size = (arrow_width, arrow_height)
            self.add(self.arrow)
            self.arrow.position = (arrow_x, arrow_y, 0)

            if arrow:
                self.arrow.set_from_file(arrow)
            """

        def _layout(self):
            canvas = self.canvas
            
            vspacing = canvas.width / 35.
            thumbnail_width = canvas.width / 13.
            thumbnail_height = canvas.height / 8.
            thumbnail_x = canvas.width / 70.
            thumbnail_y = canvas.height / 60.

            self.thumbnail.position = (thumbnail_x, thumbnail_y, 0)
            self.thumbnail.size = (thumbnail_width, thumbnail_height)

            text_x = thumbnail_width + vspacing
            text_y = canvas.height / 45.
            text_width = canvas.width / 1.9
            #text_height = canvas.height / 6.
            #print self.height
            text_height = self.height

            self._description.position = (text_x, text_y, 0)
            self._description.size = (text_width, text_height)

        def canvas__set(self, canvas):
            Group.canvas__set(self, canvas)
            self._layout()

        def _create_text(self):
            txt = Text()
            txt.font_family = "Nimbus Sans L"
            txt.bg_color = (0, 0, 0, 0)
            txt.alignment = pgm.TEXT_ALIGN_LEFT
            txt.ellipsize = pgm.TEXT_ELLIPSIZE_MIDDLE
            txt.opacity = 255
            return txt

        def description__set(self, text):
            self._description.markup = u'<b>%s</b>' % text
            self._description.visible = len(text) > 0

        def description__get(self):
            return self._description


    class DetailedListItem(ListItem):

        def __init__(self, thumbnail=None, description=None,
                     arrow=None, sub_description=None):
            super(DetailedListItem, self).__init__(thumbnail=thumbnail,
                                                   description=description,
                                                   arrow=arrow)

            self._sub_description = self._create_text()
            self.add(self._sub_description)
            self._sub_description.fg_color = (150, 150, 150, 255)
            #self._sub_description.bg_color = (0, 0, 255, 255)
            self._sub_description.visible = True

            if sub_description:
                self.sub_description = sub_description

            text_x = self.description.x
            text_y = 0.21
            text_width = self.description.width
            text_height = .25

            self._description.y = 0.01

            self._sub_description.position = (text_x, text_y, 0)
            self._sub_description.size = (text_width, text_height)

        def sub_description__set(self, text):
            self._sub_description.markup = '<b>%s</b>' % text


    text_idx = 0

    def create_text(label):
        global text_idx
        txt = Text()
        txt.label = "%s %s" % (label, text_idx)
        text_idx += 1
        txt.font_family = "Bitstream DejaVu"
        txt.alignment = pgm.TEXT_ALIGN_CENTER
        txt.bg_color = (255, 0, 0, 123)
        #txt.bg_color = (0, 0, 0, 0)
        txt.ellipsize = pgm.TEXT_ELLIPSIZE_END
        txt.visible = True
        return txt
    
    def create_img(img_file):
        img = Image()
        img.set_from_file(img_file)
        img.fg_color = (255, 255, 255, 255)
        #img.bg_color = (100, 200, 100, 155)
        img.bg_color = (0, 0, 0, 0)
        img.opacity = 0
        img.visible = True
        return img

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

        elif event.keyval == pgm.keysyms.a:
            canvas.regenerate()

        if event.keyval in (pgm.keysyms.Down, pgm.keysyms.Right):
            for l in lists:
                l.selected_item += 1
        elif event.keyval in (pgm.keysyms.Up, pgm.keysyms.Left):
            for l in lists:
                l.selected_item -= 1
        elif event.keyval == pgm.keysyms.r:
            for l in lists:
                l.pop()

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

    # OpenGL viewport creation
    factory = pgm.ViewportFactory('opengl')
    gl = factory.create()
    gl.title = 'TextList widget-test'

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

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

    top_left = ScrolledList(3.0, 1.0, orientation=const.VERTICAL)
    top_left.position = (0.04, 0.5, 0)
    top_left.width = canvas.width * 0.5
    top_left.height = canvas.height * 0.8
##     top_left.position = (0.5, 0.2, 0.0)
    top_left.visible_range_size = 9
##     top_left.width = canvas.width * 0.6
##     top_left.height = canvas.height * 0.8
    top_left.visible = True
    top_left.canvas = canvas

    bg = SlicedImage()
    bg.top_file = "theme/scrollbar-top.png"
    bg.bottom_file = "theme/scrollbar-bottom.png"
    bg.body_file = "theme/scrollbar-body.png"

    cursor = SlicedImage()
    cursor.top_file = "theme/cursor-top.png"
    cursor.bottom_file = "theme/cursor-bottom.png"
    cursor.body_file = "theme/cursor-body.png"

    bar = Scrollbar(items_number=9, background=bg, cursor=cursor, spacing=0.01)
    #bar.visible = False
    
    #thickness=0.08,
    top_left.set_vertical_scrollbar(bar, const.LEFT)

    pictos = ("theme/selector-left.png",
              "theme/selector-right.png",
              "theme/selector-body.png",
              "theme/back-selector-left.png",
              "theme/back-selector-right.png",
              "theme/back-selector-body.png",
              )
    list_selector = selector.Selector(orientation=const.HORIZONTAL, *pictos)
    top_left.set_selector(list_selector)

    lists = [top_left, ]

    if len(sys.argv[1:]) >= 1:
        cover = sys.argv[1]
    else:
        cover = '/tmp/cover.png'
    if not os.path.exists(cover):
        cover = None

    txt = "This is a very long text blah blah blah!"
    for l in lists:
        for i in xrange(1):
            d = ListItem(cover, txt,
                         "theme/arrow.png")
##             d = DetailedListItem(cover, txt, "theme/arrow.png",
##                                  sub_description="this is the sub text quoi")
            l.append(d)

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