# -*- 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__ = 'Benjamin Kampmann <benjamin@fluendo.com>'

from elisa.base_components.player_engine import PlayerEngine
from elisa.core.player import PlayerLoading, \
                              PlayerPlaying, \
                              PlayerPausing, \
                              PlayerStopping, \
                              PlayerError, \
                              PlayerBuffering, \
                              NewBaseTime, \
                              NewClock, \
                              STATES

from elisa.core.media_uri import MediaUri

import pygst
pygst.require('0.10')
import gst
if gst.pygst_version >= (0, 10, 10):
    import gst.pbutils

import urllib

class PlaybinEngine(PlayerEngine):
    """ 
    This class implements a player using playbin to access the uris
    """

    uri_schemes = {'http' : 150, 'smb' : 150, 'file': 150, 'ftp' : 150,
                    'sftp' : 150, 'cdda' : 150, 'mms': 150, 'daap': 150}

    def __init__(self):
        PlayerEngine.__init__(self)
        self.embedding_id = 0
        self.log_category = 'playbin_engine'

        self._pipeline = None
        self._current_uri = None

        self._audiosink = None
        self._videosink = None
        self._visualisation = None

        # A gst.STATE
        self._loading = False
        self._state = STATES.STOPPED

        self._installing_plugins = False

    # FIXME: make elisa able to handle methods for uri_schemes__get also!
    def _uri_schemes__get(self):
        schemes = {}
        reg = gst.registry_get_default()
        plugins = reg.get_plugin_list()
        try:
            for plugin in plugins:
                features = reg.get_feature_list_by_plugin(plugin.get_name())
                for feature in features:
                    if isinstance(feature, gst.ElementFactory) and \
                           feature.get_uri_type():
                        protocols = feature.get_uri_protocols()
                        for protocol in protocols:
                            schemes[str(protocol)] = 150
        except AttributeError:
            # FIXME: URI-Schemes bug..
            # There is a problem in getting the uri_protocols of factories.
            # depending on error #385841. 
            # The problem is fixed in the CVS only.
            schemes = {'http' : 150, 'smb' : 150, 'file': 150, 'ftp' : 150,
                    'sftp' : 150, 'cdda' : 150,}
                    
            self.warning("Could not find out all uris which are supported by"
                            " playbin. Using statically file, smb, ftp,"
                            " sftp, cdda and http only! \n If you have"
                            " problems with playing this uri-schemes, you"
                            " might install the latest gstreamer and python-"
                            "bindings from cvs.")
        return schemes


    def audio_sink__get(self):
        return self._audiosink

    def audio_sink__set(self, sink):

        if sink == None and self._pipeline:
            self._pipeline.set_state(gst.STATE_NULL)
            del self._pipeline
            self._pipeline = None

        self._audiosink = sink

        if self._pipeline:
            self._pipeline.set_property('audio-sink', self._audiosink)

        if self._pipeline:
            self._pipeline.set_property('audio-sink', self._audiosink)

    def visualisation__set(self, new_visualisation):
        self._visualisation = new_visualisation
        if self._pipeline:
            self._pipeline.set_property('vis-plugin', self._visualisation)

    def visualisation__get(self):
        return self._visualisation

    def video_sink__get(self):
        return self._videosink

    def video_sink__set(self, sink):
        """
        Set the videosink to sink.
        If the sink none and there was a video-sink before, destroy the
        pipeline as well and make a new one!
        """
        if sink == None:
            self._pipeline.set_state(gst.STATE_NULL)
            del self._pipeline
            self._pipeline = None

        self._videosink = sink
        if self._pipeline:
            self._pipeline.set_property('video-sink', self._video_sink)


    def uri__set(self, uri):
        if not self._pipeline:
            self._create_pipeline()

        self._current_uri = uri
        self._pipeline.set_state(gst.STATE_READY)
        self._state = STATES.STOPPED
        states = self._pipeline.get_state(3000 * gst.MSECOND)

        if states[1] == gst.STATE_READY:
           
            # quote the uri before passing it to playbin
            quoted_uri = MediaUri(uri)
            quoted_uri.path = urllib.quote(uri.path.encode('utf8'))
    
            self.debug("Loading %r", str(quoted_uri))
    
            self._pipeline.set_property('uri', str(quoted_uri))
    
            self.debug("Ready to play %r", str(quoted_uri))

    def volume__set(self, volume):
        self._pipeline.set_property('volume', volume)

    def volume__get(self):
        return self._pipeline.get_property('volume')


    # Internal methods

    def _create_pipeline(self):
        self._pipeline = gst.element_factory_make('playbin', 'playbin')
        pbus = self._pipeline.get_bus()
        pbus.connect('message::eos', self._bus_message_eos_cb)
        pbus.connect('message::error', self._bus_message_error_cb)
        pbus.connect('message::state-changed',
                self._bus_message_state_changed_cb)
        pbus.connect('message::buffering', self._bus_message_buffering_cb) 
        pbus.connect('message::new-clock', self._bus_message_new_clock_cb) 
        pbus.connect('message::element', self._bus_message_element_cb) 
        pbus.add_signal_watch()

        self._pipeline.set_property('video-sink', self._videosink)
        self._pipeline.set_property('audio-sink', self._audiosink)
        self._pipeline.set_property('vis-plugin', self._visualisation)
        self._pipeline.set_state(gst.STATE_READY)

    def _bus_message_eos_cb(self, bus, message):
        if self.state != STATES.STOPPED:
            self._state = STATES.STOPPED
            self._send_msg(PlayerStopping())
        self.info("End of stream reached")
        
    def _bus_message_buffering_cb(self, bus, message):
        try:
            percent = message.parse_buffering()
        except AttributeError:
            self.info("You won't get a buffering message. This is only"
                      " working with the python bindings >= 0.10.8!")
            return

        self._send_msg(PlayerBuffering(percent))

    def _bus_message_error_cb(self, bus, message):
        if  not self._installing_plugins:
            err, msg = message.parse_error()
            self.warning("Gstreamer %s:%s" % (err, msg))
            self._send_msg(PlayerError((err, msg)))
        self._pipeline.set_state(gst.STATE_READY)

    def _bus_message_state_changed_cb(self, bus, message):
        old_state, new_state, pending = message.parse_state_changed()
        if new_state == gst.STATE_PLAYING and message.src == self._pipeline:
            # We have a new Basetime
            base_time = self._pipeline.get_base_time()
            self._send_msg(NewBaseTime(base_time))
            if self._state != STATES.PLAYING:
                self._state = STATES.PLAYING
                self._send_msg(PlayerPlaying())
        elif new_state == gst.STATE_NULL or \
                    new_state == gst.STATE_READY:
            self._state = STATES.STOPPED
        elif new_state == gst.STATE_PAUSED:
            self._state = STATES.PAUSED    

    def _bus_message_new_clock_cb(self, bus, message):
        # Clock was updated
        clock = message.parse_new_clock()
        self._send_msg(NewClock(clock))
    
    def _bus_message_element_cb(self, bus, message):
        structure = message.structure
        if structure.get_name().startswith('missing-'):
            self._pbutils_message(message)

    def _pbutils_message(self, message):
        # pbutils was wrapped in 0.10.10
        if gst.pygst_version < (0, 10, 10):
            return

        self._installing_plugins = True
        
        detail = gst.pbutils.missing_plugin_message_get_installer_detail(message)
        ctx = gst.pbutils.InstallPluginsContext()

        if self.embedding_id:
            ctx.set_x_id(self.embedding_id)
        
        ret = gst.pbutils.install_plugins_async([detail], ctx,
                self._pbutils_plugin_installed_cb)

    def _pbutils_plugin_installed_cb(self, result):
        self._installing_plugins = False
        if result == gst.pbutils.INSTALL_PLUGINS_SUCCESS:
            gst.update_registry()
            self.play()
            return
       
        # FIXME: send a better error
        msg = "failed to install plugins: %s" % result
        self._send_msg(PlayerError((result, msg)))
