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


__maintainer__ = 'Florian Boucault <florian@fluendo.com>'

from elisa.base_components.controller import Controller
from elisa.core.observers.list import ListObserver
from elisa.core.input_event import *
from elisa.core import common, player

from twisted.internet import reactor

plugin_registry = common.application.plugin_registry

class ListController(Controller, ListObserver):
    """
    DOCME

    @ivar current_index:  index of the currently selected item in the list
    @type current_index:  int
    """

    supported_models = ('base:list_model',)
    
    action_called = False
    
    def __init__(self):
        super(ListController, self).__init__()
        self.current_index = 0
        self._moves = 0
        self._set_current_index_call = None

    def __repr__(self):
        return "<%s current_index=%r %s items>" % (self.__class__.__name__,
                                                   self.current_index,
                                                   len(self.model or []))

    def activate_item(self, origin):
        model = self.model[int(self.current_index)]

        if hasattr(model, "file_type") and model.file_type == "image":
            # FIXME: it is aware of where the slideshow controller is located
            root = self.backend.root_controller

            def is_image(model):
                return model.file_type == "image"

            images = plugin_registry.create_component("base:list_model")
            images.extend(filter(is_image, self.model))

            root.slideshow.model = None
            root.slideshow.current_index = images.index(self.model[self.current_index])
            root.slideshow.model = images
            root.slideshow.focus()

        elif hasattr(model, 'file_type') and model.file_type in ('audio', 'video'):
            # FIXME: it is aware of where the player controller is located
            root = self.backend.root_controller

            def is_audio_or_video(model):
                return model.file_type == "audio" or model.file_type == "video"

            media = plugin_registry.create_component("base:list_model")
            media.extend(filter(is_audio_or_video, self.model))
    
            if root.player.model != media:
                root.player.model = media

            root.player.current_index = media.index(self.model[self.current_index])
            root.player.playing = True
            root.player.focus()

        elif callable(model.activate_action):
            # Hack to look, if we are going to do something with media
            if hasattr(model.activate_action, 'player_model') or \
                hasattr(model.activate_action, 'uri'):
                
                self.action_called = True
            model.activate_action(self, origin)
            self.action_called = False
        else:
            self.parent.enter_node(model)
        
    # compress the number of next_item and previous_items so that we don't
    # destroy the cpu
    def next_item(self, step=1):
        result = self.current_index + self._moves + step
        if not self.model or result >= len(self.model):
            return

        self._moves += step

        if self._set_current_index_call is None:
            self._set_current_index_call = \
                    reactor.callLater(0.05, self.set_current_index)
    
    def previous_item(self, step=1):
        result = self.current_index + self._moves - step
        if result < 0:
            return

        self._moves -= step
        if self._set_current_index_call is None:
            self._set_current_index_call = \
                    reactor.callLater(0.05, self.set_current_index)

    def set_current_index(self):
        self._set_current_index_call = None
        moves = self._moves
        self._moves = 0
        self.current_index = self.current_index + moves

    def handle_input(self, input_event):
        if input_event.action == EventAction.GO_UP:
            self.previous_item()
            return True
        
        elif input_event.action == EventAction.GO_DOWN:
            self.next_item()
            return True

        elif input_event.action == EventAction.GO_LEFT:
            # We don't want, that the parent is doing something
            return True

        elif input_event.action == EventAction.OK:
            self.activate_item(input_event.origin)
            return True
        
        elif input_event.action == EventAction.NONE:
            # it is a simple other key:
            value = unicode(input_event.value)[-1:]
            try:
                # is it a usuabel for us
                idx = int(value)
            except ValueError:
                pass
            else:
                length = len(self.model)
                if length < 10:
                    # adress directly in a human friendly way
                    idx = max(0, idx-1)
                    if len(self.model) >= idx:
                        self.current_index = idx
                    # or don't if to the value is too big
                else:
                    new_idx = int(length / 10. * idx)
                    self.current_index = new_idx

                return True
                

        return False

    # FIXME: should be abstracted away
    def inserted(self, elements, position):
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.inserted(elements, position)

    def removed(self, elements, position):
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.removed(elements, position)
   
    def modified(self, position, value):
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.modified(position, value)
  
    def dirtied(self):
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.dirtied()

    def element_attribute_set(self, position, key, old_value, new_value):
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.element_attribute_set(position, key, old_value, new_value)

    def model_changed(self, old_model, new_model):
        # FIXME: hack in order to warn the connected views that the model has
        # been changed. It relies on the internals of _observers.
        # This should be generalised to all controllers (cf. ticket #875)
        for weak_view in self._observers:
            view = weak_view()
            if view == None:
                continue
            else:
                view.controller_changed(self, self)
