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

from elisa.core import common

import pgm
from pgm.widgets import const
from pgm.graph.image import Image

from raval_widgets import grid_bar
from raval_widgets.grid_with_selector import GridWithSelector
from raval_widgets.long_loading_image import LongLoadingImage
import constants

plugin_registry = common.application.plugin_registry
BaseListView = plugin_registry.get_component_class('raval:list_view')

class GridView(BaseListView, GridWithSelector):
    supported_controllers = ('raval:grid_controller',)

    bindings = (("current_index", "selected_item"),
                ("focused", "selector_visible"),
                ("ratio" , "ratio"),
                ("lines", "edge_length"),
                ("grid_bar_index", "grid_bar_index"))
    
    def clean(self):
        BaseListView.clean(self)
        self.canvas = None
        if self._grid_bar is not None:
            self._grid_bar.canvas = None

    def edge_length__set(self, length):
        self._animated.lines = length
        new_range = int(length * self.ratio)
        self._animated.visible_range_size = new_range

        # experimental code to keep the selected item at the same place on the
        # canvas whatever the cost.
        # FIXME: this code is copied from Grid.selected_item__set; very ugly.
        half_size = (new_range-1.0)/2.0
        column = int(self.selected_item/length)
        total_columns = math.ceil(float(len(self.widgets))/length)

        if column <= half_size or \
           len(self.widgets) <= new_range*self.lines:
            self._animated.visible_range_start = 0.0
        elif column >= total_columns-half_size:
            start = total_columns-new_range
            self._animated.visible_range_start = start
        else:
            self._animated.visible_range_start = column-half_size

    def grid_bar_index__set(self, index):
        self.selector.visible = index == grid_bar.NONE_FOCUSED
        self._grid_bar.focused_button = index

    def update_details(self):
        if len(self.controller.model) > 0:
            current_model = self.controller.model[int(self.selected_item)]

            txt = self.frontend.translate_text(current_model.text)
            sub_text = self.frontend.translate_text(current_model.sub_text)

            self._grid_bar.main_text = txt
            self._grid_bar.sub_text = sub_text

    def selector_visible__set(self, visible):
        self.selector.visible = visible

    def create_widgets(self):
        BaseListView.create_widgets(self)

        canvas = self.frontend.context.viewport_handle.get_canvas()
        theme = self.frontend.theme

        top_bar_height_px = canvas.height  * (constants.status_bar_height + constants.context_bar_height) / 100. 
        margin_px = canvas.height * constants.list_menu_height_margin / 100.
        top_bar_height_px = canvas.height  * (constants.status_bar_height + constants.context_bar_height) / 100.
        
        y = top_bar_height_px  + margin_px
        # positioning and scaling the grid
        self.orientation = const.HORIZONTAL
        self.width = canvas.width
        self._visible_range_size = 7
        
        bar_height = canvas.height * constants.grid_bottom_bar / 100.
        
        self.height = canvas.height - y - margin_px - bar_height
        self.position = (0.0, y, 0.0)
            
        bar = grid_bar.GridBar()
        bar.visible = True
        self.add(bar)
        bar.position = (0.0, self.height + margin_px, 0.0)
        bar.size = (canvas.width, bar_height)
        bar.connect('focus-changed', self._bar_focus_changed)

        bar.background = theme.get_media('grid_bottom_bar_bg')
        bar.zoom_in_button = theme.get_media('grid_zoom_in_button')
        bar.zoom_out_button = theme.get_media('grid_zoom_out_button')
        bar.back_button = theme.get_media('grid_back_button')
        bar.zoom_in_button_focused = theme.get_media('grid_zoom_in_button_focused')
        bar.zoom_out_button_focused = theme.get_media('grid_zoom_out_button_focused')
        bar.back_button_focused = theme.get_media('grid_back_button_focused')

        self._grid_bar = bar

        # selector
        selector = Image()
        selector.set_from_file(theme.get_media('grid_selector'))
        selector.layout = pgm.IMAGE_FILLED
        #selector.size = (200., 200.)
        selector.interp = pgm.IMAGE_BILINEAR
        selector.bg_color = (0, 0, 0, 0)
        selector.visible = True
        self.selector = selector
        selector.position = (0.0, 0.0, 1.0)

    def _bar_focus_changed(self, bar, value):
        if self.controller.focused:
            self.controller.grid_bar_index = value
            # FIXME: origin of this code was unknown therefore it was removed
            # in changeset 4648; it turns out that it is needed for the click
            # to work: extremely ugly.
            self.controller.activate_item(self.frontend)
        else:
            self.controller.grid_bar_index = grid_bar.NONE_FOCUSED
            
    def create_item(self, model):
        widget = LongLoadingImage()
        widget.time_before_quick = 0
        widget.image.layout = pgm.IMAGE_ZOOMED

        # white borders
        widget.image.bg_color = (255, 255, 255, 0)
        widget.image.border_width = self._widget_width*0.014

        # FIXME: cannot use cloning here: texture animation have trouble with it
        loading_icon = self.frontend.theme.get_media('waiting_icon')
        widget.loading_image_path = loading_icon

        return widget

    def _error_eater(self, error):
        # We don't want to show a warning to the user, if the thumbnailing
        # errors. We simply don't do anything in that case.
        self.debug("Eating error: %s" % error.getBriefTraceback())
        return None

    def load_item(self, index):
        model = self.controller.model[index]
        widget = self[index]

        self.load_from_theme(model.theme_icon, widget.quick_image)
 
        uri = model.thumbnail_source
        if uri != None:
            dfr = self._model_thumbnail_to_image(model, widget.image)
            dfr.addErrback(self._error_eater)
        else:
            self._create_image_stack(model, widget.quick_image)

    def unload_item(self, widget):
        widget.image.clear()

    def element_attribute_set(self, position, key, old_value, new_value):
        super(GridView, self).element_attribute_set(position, key, old_value,
                                                    new_value)
        if key == "thumbnail_source":
            if self.is_widget_visible(position):
                model = self.controller.model[position]
                dfr = self._model_thumbnail_to_image(model, self[position].image)
                dfr.addErrback(self._error_eater)
        
        elif key == "loading":
            self[position].loading = new_value

    def do_selected_item_changed(self, index):
        self.controller.current_index = index

    def do_drag_begin(self, x, y, z, button, time):
        # We can get clicked events after the controller is already moved
        if self.controller is None or not self.controller.focused:
            return False

        if self.controller.grid_bar_index != grid_bar.NONE_FOCUSED:
            self.controller.grid_bar_index = grid_bar.NONE_FOCUSED
            
        return super(GridView, self).do_drag_begin(x, y, z, button, time)

    def do_drag_motion(self, x, y, z, button, time):
        if self.controller is None or not self.controller.focused:
            return False

        return super(GridView, self).do_drag_motion(x, y, z, button, time)
    
    def do_drag_end(self, x, y, z, button, time):
        if self.controller is None or not self.controller.focused:
            return False

        return super(GridView, self).do_drag_end(x, y, z, button, time)

    def do_clicked(self, x, y, z, button, time):
        if self.controller is None or not self.controller.focused:
            return False

        self.controller.grid_bar_index = grid_bar.NONE_FOCUSED
        
        currently_selected_item = self.selected_item

        if not super(GridView, self).do_clicked(x, y, z, button, time):
            # The parent class returns False when there's no widget in the
            # clicked position. In this case we don't do anything but we still
            # want to stop  the clicking event to propagate so we return True
            # here.
            return True
        
        if currently_selected_item == self.selected_item:
            self.controller.activate_item(self.frontend)

        return True

    def do_scrolled(self, x, y, z, direction, time):
        if self.controller == None or not self.controller.focused:
            return False

        if direction == pgm.SCROLL_UP:
            self.controller.previous_item(self.controller.lines)
        else:
            self.controller.next_item(self.controller.lines)

        return True
