# -*- coding: utf-8 -*-
# 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.
#
# Authors: Anna Wojdel <awojdel@fluendo.com>
#          Florian Boucault <florian@fluendo.com>


from elisa.core.utils import locale_helper
from elisa.core.utils.cancellable_defer import CancelledError
from elisa.core.utils import defer
from elisa.core.input_event import EventValue

from elisa.plugins.pigment.graph.text import Text
from elisa.plugins.pigment.graph.image import Image
from elisa.plugins.pigment.graph.group import NodeNotInGroup
from elisa.plugins.pigment.widgets.grid_horizontal import GridHorizontal
from elisa.plugins.pigment.widgets.list_horizontal import ListHorizontal
from elisa.plugins.pigment.widgets.button import AbstractButton
from elisa.plugins.pigment.widgets.box import VBox, ALIGNMENT

from elisa.plugins.poblesec.widgets.long_loading_image import LongLoadingImage
from elisa.plugins.poblesec.widgets.button import RelookSmoothPanel
from elisa.plugins.poblesec.widgets.widget_with_actions import WidgetWithActions
from elisa.plugins.poblesec.widgets.artwork_box import ArtworkBox
from elisa.plugins.poblesec.base.preview_list import Shortcut
from elisa.plugins.poblesec.base.list import BaseListController

WIDGET_TO_PICTURE_RATIO = 1.25

class GridItemWidget(AbstractButton):
    """
    DOCME
    """

    def __init__(self):
        super(GridItemWidget, self).__init__()
        self._create_widgets()
        self.update_style_properties(self.style.get_items())

    def _create_widgets(self):
        # mouse-drawable occupying whole widget, used for catching mouse events
        self.mouse_drawable = Image()
        self.add(self.mouse_drawable, forward_signals=False)
        self.mouse_drawable.visible = True
        self._connect_mouse_events(self.mouse_drawable)

        self.background = self._create_background()
        self.artwork_box = self._create_artwork_box()
        self.caption = self._create_caption()

    def _create_background(self):
        background = RelookSmoothPanel()
        self.add(background, forward_signals=False)
        background.visible = True
        return background

    def do_state_changed(self, previous_state):
        super(GridItemWidget, self).do_state_changed(previous_state)
        self.background.state = self.state

    def _create_artwork_box(self):
        artwork_box = ArtworkBox()
        self.add(artwork_box, forward_signals=False)
        artwork_box.visible = True
        return artwork_box

    def _create_caption(self):
        caption = Text()
        self.add(caption, forward_signals=False)
        caption.visible = True
        return caption


class GridItemWidgetWithActions(WidgetWithActions):
    """
    DOCME
    """

    def __init__(self, *args, **kwargs):
        self.upper_row = None
        super(GridItemWidgetWithActions, self).__init__(*args, **kwargs)

    def _create_widgets(self, *args, **kwargs):
        super(GridItemWidgetWithActions, self)._create_widgets(*args, **kwargs)

        self.container = VBox()
        self.container.navigable = True
        self.container.visible = True
        self.add(self.container, forward_signals=False)
        self.set_focus_proxy(self.container)

        self.item_widget.visible = True

    def show_actions(self, actions_widget):
        if self.upper_row:
            self.container.pack_start(actions_widget, expand=True)
        else:
            self.container.pack_start(actions_widget, expand=True)

    def hide_actions(self, actions_widget):
        self.container.remove(actions_widget)

    def set_upper_row(self, upper_row):
        if self.upper_row == upper_row:
            return
        self.upper_row = upper_row

        try:
            self.container.remove(self.item_widget)
        except NodeNotInGroup:
            pass

        if self.upper_row:
            self.container.alignment = ALIGNMENT.END
            self.container.pack_end(self.item_widget)
        else:
            self.container.alignment = ALIGNMENT.START
            self.container.pack_start(self.item_widget)


class DoubleRowGridHorizontal(GridHorizontal):
    """
    DOCME
    """

    def __init__(self, widget_class, columns=5, widget_args=[], widget_kwargs={}):
        super(DoubleRowGridHorizontal, self).__init__(widget_class, rows=2,
                                                      columns=columns,
                                                      widget_args=widget_args,
                                                      widget_kwargs=widget_kwargs)

    def _layout_widget(self, widget, row, column):
        widget.set_upper_row(row is 0)
        super(DoubleRowGridHorizontal, self)._layout_widget(widget, row, column)


class GridController(BaseListController):

    list_widget = DoubleRowGridHorizontal

    def nodes_setup(self):
        item_widget_name = self._view_mode.name
        widget_kwargs = {'contextual_actions': self._contextual_actions,
                         'item_widget_cls': self.item_widget_cls,
                         'item_widget_name': item_widget_name}
        self.nodes = self.list_widget(self.node_widget, widget_kwargs=widget_kwargs)

        self.widget.set_focus_proxy(self.nodes)

    def prepare(self):
        aspect_ratio = self.nodes.absolute_width/self.nodes.absolute_height
        columns = self.nodes.rows*aspect_ratio * WIDGET_TO_PICTURE_RATIO
        self.nodes.visible_range_size = columns
        self.nodes.visible = True

    def fastscroller_setup(self):
        self.fastscroller = ListHorizontal(Shortcut, visible_range_size=9)
        self.fastscroller.start_offset = 1
        self.fastscroller.render_empty_items = True
        self.fastscroller.set_name("gridfastscroller")
        self.fastscroller.visible = True

    def layout_components(self):
        self.widget.add(self.nodes)
        if self.fastscroller is not None:
            self.widget.add(self.fastscroller)
            self.widget.add_navigation_rule(self.nodes, EventValue.KEY_GO_DOWN,
                                            self.fastscroller)
            self.widget.add_navigation_rule(self.fastscroller,
                                            EventValue.KEY_GO_UP, self.nodes)
            # swallow events to create a 'dead end'
            self.widget.add_navigation_rule(self.fastscroller,
                                            EventValue.KEY_GO_RIGHT, None)
            self.widget.add_navigation_rule(self.fastscroller,
                                            EventValue.KEY_GO_LEFT, None)

        # swallow events to create a 'dead end'
        self.widget.add_navigation_rule(self.nodes,
                                        EventValue.KEY_GO_RIGHT, None)
        self.widget.add_navigation_rule(self.nodes,
                                        EventValue.KEY_GO_LEFT, None)

    def shortcut_renderer(self, shortcut, widget):
        widget.text.label = shortcut


class GridItemGridController(GridController):
    """
    Grid controller tied to the grid item widget.
    """

    node_widget = GridItemWidgetWithActions
    item_widget_cls = GridItemWidget

    def node_renderer(self, item, widget):
        """
        Render a node using the common API methods defined by the
        L{elisa.plugins.poblesec.base.list.GenericListViewMode} class.
        """
        # Cancel previous deferred calls for this widget
        self.cancel_deferreds(widget)

        def _failure(failure):
            # Swallow errbacks only when the deferred has been cancelled
            failure.trap(CancelledError)

        # Pass the item to the widget so that contextual actions can use it.
        widget.item = item

        # Set the default image of the widget
        artwork_box = widget.item_widget.artwork_box
        artwork_box.icon.clear()
        artwork_box.thumbnail.clear()
        default_image = self._view_mode.get_default_image(item)
        if default_image:
            self.frontend.load_from_theme(default_image, artwork_box.icon)

        # Set the real image of the widget
        theme = self.frontend.get_theme()
        image_deferred = self._view_mode.get_image(item, theme)
        if image_deferred is not None:
            def got_thumbnail(thumbnail_file):
                if thumbnail_file is not None:
                    # Pigment supports unicode, let's decode the
                    # thumbnail path if it is not unicode
                    if not isinstance(thumbnail_file, unicode):
                        thumbnail_file = thumbnail_file.decode(locale_helper.system_encoding())
                    artwork_box.icon.clear()
                    artwork_box.thumbnail.set_from_file(thumbnail_file, 256)

            self.register_deferred(widget, image_deferred)
            image_deferred.addCallbacks(got_thumbnail, _failure)

        # Set caption of the image
        self._set_caption(widget.item_widget, item)

    def _set_caption(self, widget, item):
        def got_label(label):
            widget.caption.label = label

        dfr = self._view_mode.get_label(item)
        dfr.addCallback(got_label)

    def clean(self):
        if self._view_mode is not None:
            dfr = self._view_mode.clean()
        else:
            dfr = defer.succeed(self)

        def view_mode_cleaned(value):
            self._view_mode = None
            return super(GridItemGridController, self).clean()

        dfr.addCallback(view_mode_cleaned)
        return dfr
