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

from twisted.internet import defer

from elisa.core import common
from elisa.core.utils.misc import read_mappings
from elisa.plugins.base.models.plugin import PluginModel
from elisa.plugins.pigment.pigment_controller import PigmentController
from elisa.plugins.pigment.widgets.list_horizontal import ListHorizontal

import pgm
from elisa.plugins.pigment.graph.image import Image
from elisa.plugins.pigment.graph.text import Text
from elisa.plugins.pigment.widgets.const import *
from elisa.plugins.pigment.widgets.widget import Widget
from elisa.plugins.pigment import maths
from pgm.utils import image as image_utils
from elisa.core.input_event import *

class PluginWidget(Widget):
    def __init__(self):
        super(PluginWidget, self).__init__()

        self.name = Text()
        self.add(self.name)
        self.name.weight = pgm.TEXT_WEIGHT_BOLD
        self.name.alignment = pgm.TEXT_ALIGN_CENTER
        self.name.ellipsize = pgm.TEXT_ELLIPSIZE_END
        self.name.bg_color = 255, 0, 0, 255
        self.name.size = (0.95, 0.062)
        self.name.position = ((1.0-self.name.width)/2.0, 0.0, 0.0)
        self.name.visible = True

        self.icon = Image()
        self.add(self.icon)
        self.icon.bg_a = 0
        self.icon.size = (0.76, 0.443)
        x = (1.0-self.icon.width)/2.0
        y = self.name.y + self.name.height + 0.06
        self.icon.position = (x, y, 0.0)
        self.icon.alignment = pgm.IMAGE_BOTTOM
        self.icon.visible = True

        self.icon_reflect = Image()
        self.add(self.icon_reflect)
        self.icon_reflect.bg_a = 0
        self.icon_reflect.size = self.icon.size
        x = self.icon.x
        y = self.icon.y + self.icon.height
        self.icon_reflect.position = (x, y, 0.0)
        # make it a icon_reflect
        flip_matrix = pgm.mat4x4_new_predefined(pgm.MAT4X4_FLIP_VERTICAL)
        self.icon_reflect.mapping_matrix = flip_matrix
        self.icon_reflect.alignment = pgm.IMAGE_TOP
        self.icon_reflect.opacity = 150
        self.icon_reflect.visible = True

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

    def update_style_properties(self, props=None):
        super(PluginWidget, self).update_style_properties(props)

        if props is None:
            return

        for key, value in props.iteritems():
            if key == 'icon-src':
                self.set_icon_from_file(value)

    def set_icon_from_file(self, path):
        self.icon.set_from_file(path)
        image_utils.cairo_gradient(path, self.icon_reflect, 0.6)

class ShelfHeader(Widget):
    def __init__(self):
        super(ShelfHeader, self).__init__()

        alignment = 0.011
        self.icon = Image()
        self.add(self.icon)
        self.icon.bg_a = 0
        self.icon.position = (alignment, 0.054, 0.0)
        self.icon.size = (0.087, 0.403)
        self.icon.visible = True

        alignment = self.icon.x + self.icon.width + 0.013
        self.title = Text()
        self.add(self.title)
        self.title.weight = pgm.TEXT_WEIGHT_BOLD
        self.title.ellipsize = pgm.TEXT_ELLIPSIZE_END
        self.title.bg_a = 0
        self.title.position = (alignment, 0.136, 0.0)
        self.title.size = (0.5, 0.178)
        self.title.visible = True

        self.subtitle = Text()
        self.add(self.subtitle)
        self.subtitle.weight = pgm.TEXT_WEIGHT_BOLD
        self.subtitle.ellipsize = pgm.TEXT_ELLIPSIZE_END
        self.subtitle.bg_a = 0
        self.subtitle.fg_color = (162, 162, 162, 255)
        x = alignment
        y = self.title.y + self.title.height + 0.0
        self.subtitle.position = (x, y, 0.0)
        self.subtitle.size = (0.5, 0.136)
        self.subtitle.visible = True

        self.separator = Image()
        self.add(self.separator)
        self.separator.bg_a = 0
        self.separator.layout = pgm.IMAGE_FILLED
        x = 0.007
        y = self.subtitle.y + self.subtitle.height + 0.043
        self.separator.position = (x, y, 0.0)
        self.separator.size = (0.889, 0.02)
        self.separator.visible = True

    def set_icon_from_file(self, path):
        self.icon.set_from_file(path)

class PluginsList(ListHorizontal):
    def _layout_widget(self, widget, position):
        x = self.compute_x(position)
        y = 0.0
        z = self.compute_z(position)
        grey = self.compute_name_greyiness(position)

        # update widget properties
        widget.opacity = 255
        widget.position = (x, y, z)
        widget.name.fg_color = (grey, grey, grey, 255)

    def _piecewise_interpolation(self, x, y, factor):
        factor += 0.5
        t = self._visible_range_size
        x = map(lambda a: t*a, x)

        # clamp after lower and upper limits
        if factor < x[0]:
            return y[0]
        elif factor > x[-1]:
            return y[-1]
        else:
            # general case: looking for the segment where factor belongs
            i = 0
            while factor > x[i+1]:
                i += 1

            # factor must be between 0.0 and 1.0
            new_factor = (factor - x[i]) / (x[i+1] - x[i])
            return maths.lerp(y[i], y[i+1], new_factor)

    def compute_z(self, index):
        x = [0.0, 0.4, 0.5, 0.6, 1.0]
        y = [0.0, 0.0, 50.0, 0.0, 0.0]

        return self._piecewise_interpolation(x, y, index)

    def compute_name_greyiness(self, index):
        x = [0.0, 0.4, 0.5, 0.6, 1.0]
        y = [150.0, 150.0, 255.0, 150.0, 150.0]

        return self._piecewise_interpolation(x, y, index)

    def selected_item_index__set(self, index):
        # copied/pasted from elisa.plugins.pigment.widgets.list
        # modified to not block on the edges
        self._stop_deceleration()
        index = maths.clamp(index, 0, len(self.model)-1)
        index = int(round(index))

        half_size = (self.visible_range_size-1.0)/2.0
        prev_selected = self._selected_item_index
        self._selected_item_index = index
        visible_range_start = index-half_size

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

        if prev_selected != index:
            try:
                item = self.model[index]
            except IndexError:
                item = None
            try:
                prev_item = self.model[prev_selected]
            except IndexError:
                prev_item = None
            self.emit('selected-item-changed', item, prev_item)

    selected_item_index = property(ListHorizontal.selected_item_index__get,
                             selected_item_index__set)

class ShelfController(PigmentController):

    def initialize(self):
        self.current_controller = None
        return defer.succeed(self)

    def set_frontend(self, frontend):
        super(ShelfController, self).set_frontend(frontend)
        self._create_home_screen()

    def _setup_header_home_screen(self):
        # default values for home screen
        self.header.title.label = "The shelf"
        self.header.subtitle.label = "Available plugins"
        icon_path = self.frontend.get_theme().get_resource('elisa.plugins.shelf.icon')
        self.header.set_icon_from_file(icon_path)

    def _create_home_screen(self):
        # header
        self.header = ShelfHeader()
        self.frontend.load_from_theme('elisa.plugins.shelf.line', self.header.separator)
        self.widget.add(self.header)
        self.header.height = 0.264
        self.header.visible = True
        self._setup_header_home_screen()

        # back button
        self.back = Image()
        self.widget.add(self.back)
        self.back.bg_a = 0
        self.frontend.load_from_theme('elisa.plugins.shelf.back', self.back)
        self.back.size = (0.08, 0.08)
        self.back.position = (1.0-self.back.width, 0.017, 0.0)
        self.back.connect("clicked", self._on_back_button_clicked)

        # list of plugins

        self.plugins = PluginsList(PluginWidget)
        self.plugins.visible_range_size = 3
        self.widget.add(self.plugins)
        self.plugins.size = (1.0, 0.592)
        self.plugins.position = (0.0, 0.365, 0.0)
        self.plugins.visible = True

        plugin_registry = common.application.plugin_registry
        plugins_list = plugin_registry.get_plugins()
        plugins = []

        for plugin_name, enabled in plugins_list:
            dist = plugin_registry.get_plugin_by_name(plugin_name)
            if not dist.has_metadata("controller_mappings.txt"):
                continue
            plugin_registry.get_plugin_metadata(dist)
            try:
                model = PluginModel.from_distribution(dist)
            except AssertionError, e:
                pass
            model.distribution = dist
            plugins.append(model)

        # TODO: filter out the shelf plugin itself

        # add a graphical representation to the shelf for each of them
        def renderer(plugin, widget):
            widget.name.label = plugin.title
            try:
                icon_path = plugin.icons[0].references[-1].path
            except IndexError:
                theme = self.frontend.get_theme()
                icon_path = theme.get_resource('elisa.plugins.shelf.extension')
            widget.set_icon_from_file(icon_path)
            widget.visible = True

        self.plugins.set_renderer(renderer)
        self.plugins.set_model(plugins)

        self.plugins.connect('item-clicked', self._on_plugin_clicked)

    def _on_plugin_clicked(self, widget, item):
        def enter_plugin(dummy):
            self.plugins.visible = False
            self.widget.animated.setup_next_animations(duration=1500,
                                                       end_callback=None)
            self._enter_plugin(item)

        self.widget.animated.setup_next_animations(duration=350,
                                                   end_callback=enter_plugin)
        self.widget.animated.z = -100.0
        self.widget.animated.opacity = 0

    def _on_back_button_clicked(self, *args):
        if self.current_controller == None:
            return

        def load_home_screen(dummy):
            # destroy current controller
            self.widget.remove(self.current_controller.widget)
            self.current_controller = None

            # hide back button
            self.back.visible = False
            # show list of plugins
            self.plugins.visible = True

            self._setup_header_home_screen()
            self.widget.animated.setup_next_animations(duration=1500,
                                                       end_callback=None)
            self.widget.animated.opacity = 255
            self.widget.z = -100.0
            self.widget.animated.setup_next_animations(duration=350)
            self.widget.animated.z = 0.0

        self.widget.animated.setup_next_animations(duration=350,
                                                   end_callback=load_home_screen)
        self.widget.animated.z = 100.0
        self.widget.animated.setup_next_animations(duration=350)
        self.widget.animated.opacity = 0

    def _enter_plugin(self, plugin):
        # destroy current controller if any
        if self.current_controller is not None:
            self.widget.remove(self.current_controller.widget)
            self.current_controller = None

        def controller_created(controller):
            self._load_header(plugin)
            self.back.visible = True
            self.widget.add(controller.widget)
            controller.widget.position = (0.0, 0.264, 0.0)
            controller.widget.size = (1.0, 1.0-controller.widget.y)
            self.current_controller = controller
            self.widget.z = 100.0
            self.widget.animated.setup_next_animations(duration=350)
            self.widget.animated.opacity = 255
            self.widget.animated.z = 0.0

        # create the controller corresponding to the first mapping of the
        # clicked plugin
        lines = plugin.distribution.get_metadata_lines('controller_mappings.txt')
        mappings = read_mappings(lines)
        dfr = self.frontend.create_controller(mappings[0][0])
        dfr.addCallback(controller_created)

    def _load_header(self, plugin):
        self.header.title.label = plugin.title
        self.header.subtitle.label = plugin.description
        try:
            icon_path = plugin.icons[0].references[-1].path
        except IndexError:
            theme = self.frontend.get_theme()
            icon_path = theme.get_resource('elisa.plugins.shelf.extension')
        self.header.set_icon_from_file(icon_path)

    def handle_input(self, manager, input_event):
        if input_event.value == EventValue.KEY_MENU:
            self._on_back_button_clicked(0)
            return True
        elif input_event.value == EventValue.KEY_GO_LEFT:
            self.plugins.selected_item_index -= 1
            return True
        elif input_event.value == EventValue.KEY_GO_RIGHT:
            self.plugins.selected_item_index += 1
            return True
        elif input_event.value == EventValue.KEY_OK:
            index = self.plugins.model[self.plugins.selected_item_index]
            self._on_plugin_clicked(self.plugins, index)
        else:
            return super(ShelfController, self).handle_input(manager, input_event)
