# -*- coding: utf-8 -*-fspacin
# 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.

from elisa.plugins.pigment.widgets.widget import Widget
from elisa.plugins.pigment.widgets.const import *

from elisa.plugins.pigment.graph.image import Image
from elisa.plugins.pigment.animation.animation import LINEAR
from elisa.plugins.pigment.animation.implicit import AnimatedObject

import pgm
import gobject

import logging


class Range(Widget):
    """
    The range widget provides the common interface and implementation to set a
    value in a range of values.

    Emit the signals:
      - index-changed: when the set value has changed

    @ivar items_number: the number of items in the range
    @type items_number: int
    @ivar current_index: the current value, from 0 to (items_number - 1)
    @type current_index: int
    """

    __gsignals__ = {
        'index-changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,
                           (gobject.TYPE_INT, gobject.TYPE_INT)),
        }

    def __init__(self):
        super(Range, self).__init__()

        self.style.spacing_x = 0.0
        self.style.spacing_y = 0.0

        self.drag_threshold = 5
        self._items_number = 1
        self._current_index = 0

        self._orientation = HORIZONTAL

        self._cursor = None
        self._cursor_size = None
        self._cursor_position = None
        self._animated_cursor = None
        self._background = None
        self._animated_background = None

        self._set_background(Image())
        self._background.bg_color = self.style["background-color"]

        self._set_cursor(Image())
        self._cursor.bg_color = self.style["cursor-color"]

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

    def update_style_properties(self, props=None):
        if props is None:
            return

        remaining_props = {}

        for key, value in props.iteritems():
            if key == 'background-color':
                self._background.bg_color = value
            elif key == 'cursor-color':
                self._cursor.bg_color = value
            elif key in ('spacing_x', 'spacing_y'):
                continue
            else:
                remaining_props[key] = value

        if len(remaining_props) > 0:
            return super(Range, self).update_style_properties(remaining_props)

    def _set_background(self, background):
        if self._background:
            self.remove(self._background)

        self._background = background
        self._background.visible = True
        self.add(self._background)
        self._background.position = (0.0, 0.0, 0.0)
        self._background.size = (1.0, 1.0)

        self._background.connect('pressed', self._pressed)
        self._background.connect("drag-begin", self._drag_begin)
        self._background.connect("drag-motion", self._drag_motion)
        self._background.connect("drag-end", self._drag_end)

        self._update_cursor_size()
        self._update_cursor_position()

        self._animated_background = AnimatedObject(self._background)

    def _get_background(self):
        return self._background

    background = property(fget=_get_background, fset=_set_background)

    def _set_cursor(self, cursor):
        if self._cursor:
            self.remove(self._cursor)

        self._cursor = cursor
        self._cursor.visible = True
        self.add(self._cursor, forward_signals=False)
        self._cursor.connect('pressed', self._pressed)
        self._cursor.connect("drag-begin", self._drag_begin)
        self._cursor.connect("drag-motion", self._drag_motion)
        self._cursor.connect("drag-end", self._drag_end)

        self._update_cursor_size()

        attrs = ('current_index', 'position')
        self._animated_cursor = AnimatedObject(self._cursor, attrs)
        settings = {'duration': 300,
                    'transformation': LINEAR}
        self._animated_cursor.setup_next_animations(**settings)

        self._update_cursor_position()

    def _get_cursor(self):
        return self._cursor

    cursor = property(fget=_get_cursor, fset=_set_cursor)

    def items_number__get(self):
        return self._items_number

    def items_number__set(self, nb):
        """
        Set the number of items in the range. Minumum is 1. If the range has
        shrunk too much, reset the curren index

        @param nb: the number of items
        @type nb: int
        """

        if nb <= 0:
            nb = 1

        self._items_number = nb

        if self._current_index > (nb-1):
            self._current_index = nb - 1

        self._update_cursor_size()
        self._update_cursor_position()

    items_number = property(items_number__get, items_number__set)


    def current_index__get(self):
        return self._current_index

    def current_index__set(self, position):
        """
        Set the position of the cursor. The bar is automatically new painted

        @param position: the position to set to
        @type position: int
        """
        if 0 <= position <= self.items_number:
            if position != self._current_index:
                prev = self._current_index
                self._current_index = position
                self.emit('index-changed', position, prev)

    current_index = property(current_index__get, current_index__set)

    def _update_cursor_size(self):
        spacing_x = (self.style.spacing_x * 2)
        spacing_y = (self.style.spacing_y * 2)

        if self._orientation == VERTICAL:
            # TODO: implement me
            raise NotImplementedError
        else:
            width = (1.0 / self.items_number) * (self._current_index)
            height = 1.0 - spacing_y
            width -= spacing_x

        size = (width, height)
        self._cursor_size = size
        if self._cursor:
            self._cursor.size = self._cursor_size

    def _update_cursor_position(self):
        if self._background:
            # FIXME: we should us subclasses for HORIZONTAL/VERTICAL split,
            # like we do in the List
            if self._orientation == VERTICAL:
                # TODO: implement me
                raise NotImplementedError
            else:
                x = self.style.spacing_x
                y = self.style.spacing_y

            self._cursor_position = (x, y, 0)
            if self._cursor:
                self._cursor.position = self._cursor_position

    def _drag_begin(self, widget, x, y, z, button, time, pressure):
        if not self.focus:
            return True

        self._drag_counter = 0

        self._original_position = (x, y, z)
        return True

    def _pressed(self, widget, x, y, z, button, time, pressure ):
        pass

    def _drag_motion(self, widget, x, y, z, button, time, pressure):
        if not self.focus:
            return True

        # avoid too many updates of the current_index
        if self._drag_counter < self.drag_threshold:
            self._drag_counter += 1
            return True
        else:
            self._drag_counter = 0

        self.current_index = self._get_index_from_position(x, y, z)
        return True

    def _drag_end(self, widget, x, y, z, button, time):
        if not self.focus:
            return True

        self._original_position = None
        return True

    def do_index_changed(self, current, previous):
        """Default 'index-changed' signal handler"""
        self._update_cursor_position()

    def do_scrolled(self, x, y, z, direction, time):
        if direction == pgm.SCROLL_UP:
            self.current_index -= 1
        else:
            self.current_index += 1
        return True

    def do_key_press_event(self, viewport, event, widget):
        if event.keyval == pgm.keysyms.Up:
            self.emit('scrolled', 0, 0, 0, pgm.SCROLL_UP, 0)
        elif event.keyval == pgm.keysyms.Down:
            self.emit('scrolled', 0, 0, 0, pgm.SCROLL_DOWN, 0)

    def do_focus(self, value):
        if value:
            self.state = STATE_SELECTED
        else:
            self.state = STATE_NORMAL

    def do_clicked(self, x, y, z, button, time, pressure):
        self.current_index = self._get_index_from_position(x, y, z)
        return True

    def _get_index_from_position(self, x, y, z):
        if self._orientation == VERTICAL:
            new_fraction = (y - self.absolute_y) / self.absolute_height
        else:
            new_fraction = (x - self.absolute_x) / self.absolute_width
        return int(self.items_number * new_fraction)

    @classmethod
    def _demo_widget(cls, *args, **kwargs):
        widget = cls()
        widget.size = (100.0, 100.0)
        widget.items_number = 12
        widget.visible = True

        return widget


if __name__ == "__main__":
    logger = logging.getLogger()
    logger.setLevel(logging.DEBUG)

    range = Range.demo ()
    try:
        __IPYTHON__
    except NameError:
        pgm.main()

