# -*- coding: utf-8 -*-
# Elisa - Home multimedia server
# Copyright (C) 2006,2007 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__ = 'Benjamin Kampmann <benjamin@fluendo.com>'

import os

from elisa.core import common, player
from elisa.core.input_event import *
from elisa.base_components.controller import Controller
 
plugin_registry = common.application.plugin_registry
ListController = plugin_registry.get_component_class('raval:list_controller')

class PlayerController(ListController):
    """
    @ivar playing:          the user playing state
    @type playing:          bool
    @ivar state:            current state of the player
    @type state:            L{elisa.core.player.STATES}
    @ivar volume:           volume level; between 0 and 10
    @type volume:           float
    @ivar muted:            whether or not the player is muted
    @type muted:            boolean
    @ivar position:         current position in the media (in nanoseconds);
                            READ ONLY; -1 if unknown
    @type position:         int
    @ivar duration:         duration of the current media (in nanoseconds);
                            READ ONLY; -1 if unknown
    @type duration:         int
    @ivar index:            the currently selected file, that should get
                            played
    @type index:            int
    """

    supported_models = ('base:list_model',)

    # FIXME: add a documentation for the configuration options
    default_config = {'volume_increment_step': '0.02',
                      'volume_decrement_step': '0.02',
                      'seek_forward_step': '30',
                      'seek_backward_step': '15',
                      'show_status_on_ok': '0',
                      'visualisation' : 'libvisual_jess'
                      }

    def __init__(self):
        Controller.__init__(self)
        # set the initial values
        self._state = player.STATES.STOPPED
        self._current_index = 0

    def initialize(self):
        Controller.initialize(self)

        # player controlling is done in the controller!
        self.player = common.application.player_registry.create_player()
        
        # set the configuration values
        config = self.config
        self._volume_increment_step = float(config['volume_increment_step'])
        self._volume_decrement_step = float(config['volume_decrement_step'])
        forward_step = int(config['seek_forward_step'])
        backward_step = int(config['seek_backward_step'])
        self._seek_forward_step = forward_step * 1000000000
        self._seek_backward_step = backward_step * 1000000000
        self._delayed_call = None

        # connect to the messages asynchronous send by the player
        self.debug("Connecting to player messages sent over the bus")
        bus = common.application.bus
        bus.register(self._player_loading, player.PlayerLoading)
        bus.register(self._player_buffering, player.PlayerBuffering)
        bus.register(self._player_starting, player.PlayerPlaying)
        bus.register(self._player_paused, player.PlayerPausing)
        bus.register(self._player_stopped, player.PlayerStopping)
        bus.register(self._player_error, player.PlayerError)

        self.playing = False
        self.visualisation = None
        self.volume = 2.

    def _player_loading(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return

        self.state = player.STATES.LOADING

    def _player_error(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return
        
        self.current_index += 1


    def _player_buffering(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return
    
        self.state = player.STATES.LOADING

    def _player_starting(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return

        self.state = player.STATES.PLAYING

    def _player_paused(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return

        self.state = player.STATES.PAUSED

    def _player_stopped(self, msg, sender):
        if sender is not self.player:
            # react only, if it is OUR player
            return
        
        self.current_index += 1

    def state__set(self, value):
        self._state = value
        if value == player.STATES.STOPPED:
            self.parent.focus()

    def state__get(self):
        return self._state

    def handle_input(self, input_event):
        self.debug("Handling input event with action %r", input_event.action)
       
        if input_event.action in (EventAction.GO_UP, EventAction.VOL_UP):
            self.volume = min(2.0, self.volume + \
                                         self._volume_increment_step)
            
        elif input_event.action in (EventAction.GO_DOWN,
                                    EventAction.VOL_DOWN):
            self.volume = max(0.0, self.volume - \
                                         self._volume_decrement_step)
            
        elif input_event.action in (EventAction.GO_RIGHT,
                                    EventAction.SEEK_FORWARD):
            new_position = self.player.position + \
                                        self._seek_forward_step
            if self.duration <= 0 or new_position > self.duration:
                self.current_index += 1
            else:
                self.player.position = new_position

        elif input_event.action == EventAction.MUTE:
            self.muted = not self.muted
            
        elif input_event.action == EventAction.NEXT:
            self.current_index += 1

        elif input_event.action == EventAction.PREVIOUS:
            self.current_index -= 1

        elif input_event.action in (EventAction.GO_LEFT,
                                    EventAction.SEEK_BACKWARD):
            new_position = self.player.position - \
                                    self._seek_backward_step

            if self.duration <= 0 or new_position < 0:
                self.current_index -= 1
            else:
                self.player.position = new_position

        elif input_event.action == EventAction.OK:
            self.playing = not self.playing

        elif input_event.action == EventAction.STOP:
            self.playing = not self.playing
            self.state = player.STATES.STOPPED

        return False

    def playing__set(self, value):
        if value and self.state in (player.STATES.PAUSED,
                                    player.STATES.STOPPED):
            self.player.play()
        elif value:
            # do nothing
            pass
        else:
            self.player.pause()
        self._playing = value

    def position__get(self):
        return self.player.position

    def position__set(self, value):
        self.player.position = value

    def duration__get(self):
        return self.player.duration

    def volume__set(self, value):
        if value <= 0:
            self.player.volume = 0.0
        elif value >= 2.0:
            self.player.volume = 2.0
        else:
            self.player.volume = value

    def volume__get(self):
        return self.player.volume

    def playing__get(self):
        return self._playing

    def current_index__set(self, value):
        if not self.model:
            return

        if value >= len(self.model) or value < 0:
            # stop the playback: we got a wrong index
            self.player.stop()
            return
        try:
            new_uri = self.model[value].uri
            self.player.uri = new_uri
            self._current_index = value
            self._update_subtitle(new_uri)
            self.player.play()
        except AttributeError:
            # error because the uri was not found...
            pass

    def model__set(self, model):
        if self.model and model is None:
            self.state = player.STATES.STOPPED
        super(PlayerController, self).model__set(model)
        # set the visualisation later then at the beginning
        if not self.visualisation:
            # FIXME: initialize libvisual later to work-a-round a dead-lock
            # with pigment and libvisual init at the same time
            self.visualisation = self.config['visualisation']

    def current_model__get(self):
        return self.model[self._current_index]

    def current_index__get(self):
        return self._current_index


    def _update_subtitle(self, uri):
        """
        We only support text
        """
        self.player.subtitle_uri = None

        SUBTITLE_FORMATS = ("asc", "txt", "srt", "smi", "ssa")

        if uri and self.current_model.file_type == 'video':
            self.debug("Searching subtitles for %s", uri)
            
            media_manager = common.application.media_manager
            filename = uri.filename
            try:
                ext_index = filename.index(uri.extension)
            except ValueError:
                # extension given but not found
                self.debug("Extension of %r not found, skipping subtitles "\
                           " detection", uri)
            else:
                basename = uri.filename[:ext_index-1]
                parent = uri.parent

                def got_children(children, uri):
                    found = False
                    for child_uri, metadata in children:
                        child_basename, dummy = os.path.splitext(child_uri.filename)
                        if child_basename == basename and \
                               child_uri.extension in SUBTITLE_FORMATS:
                            self.player.subtitle_uri = child_uri
                            self.info("Found subtitles in file: %s", child_uri)
                            found = True
                            break
                    if not found:
                        self.info("No subtitles found for %r", uri)

                dfr = media_manager.get_direct_children(uri.parent, [])
                dfr.addCallback(got_children, uri.parent)
