# -*- 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.
#
# Author: Gernot Klimscha <gernot@fluendo.com>

"""
Gnome Screen Saver ServiceProvider component class

This Component can block the GnomeScreenSaver in different modes. That
is very useful, if you have a remote control or other input device
which is not reconized by the Screensaver on its own.

This Component blocks with this usage also the PowerManager of GNOME,
so that the sleep/Screen procedures shouldn't happen.
"""

from elisa.core.components.service_provider import ServiceProvider
from elisa.core import common

try:
    from elisa.plugins.pigment.message import PigmentFrontendLoadedMessage
except ImportError:
    PigmentFrontendLoadedMessage = None

import dbus
if getattr(dbus, 'version', (0,0,0)) >= (0,41,0):
    import dbus.glib
from dbus.exceptions import DBusException

from twisted.internet import defer


class GnomeScreensaverService(ServiceProvider):
    """
    This class connect to the Gnome Screen Saver and blocks it
    """

    config_doc = {'blocking_mode' : 'Block the Screensaver. Available modes'
                                    ' are: \n * 1 : block on playing only\n'
                                    ' * 2 : block from the start to the end,'
                                    ' use this, if you are only using remotes,'
                                    ' on which the screensaver is not'
                                    ' reacting'
                                    ' * else: do not block!\n'}

    default_config = {'blocking_mode' : 1}

    def initialize(self):
        dfr = super(GnomeScreensaverService, self).initialize()

        def initialized(r):
            self.pm_iface = None
            self._pm_cookie = None
            dfr = defer.succeed(self)

            try:
                self.ses = dbus.SessionBus()
            except DBusException, error:
                msg = "DBus is not running."
                fail_msg = '%s - %s' % (self.name, msg)
                return defer.fail(Exception(fail_msg))

            try:
                # Even if there is also a org.gnome.PowerManager, the
                # ScreenSaver is for our use the better one
                self.pm = self.ses.get_object('org.gnome.ScreenSaver',
                                              '/org/gnome/Screensaver')
                self.pm_iface = dbus.Interface(self.pm, "org.gnome.ScreenSaver")
            except Exception, e:
                msg = "Could not connect to the Gnome ScreenSaver: %s" % e
                fail_msg = '%s - %s' % (self.name, msg)
                return defer.fail(Exception(fail_msg))

            blocking_mode = self.config.get('blocking_mode', 1)
            if blocking_mode == 1:
                self.info("Using the stop-on-playing-only mode")
                if PigmentFrontendLoadedMessage is None:
                    self.warning("You don't seem to run the pigment frontend." \
                         " Can't connect to the player.")
                    return

                bus = common.application.bus
                bus.register(self._frontend_loaded, PigmentFrontendLoadedMessage)
            elif blocking_mode == 2:
                self.info("Using full-block mode")
                dfr = self._inhibit('running')
                dfr.addCallback(lambda x: self)
            return dfr

        dfr.addCallback(initialized)
        return dfr

    def clean(self):
        dfr = super(GnomeScreensaverService, self).clean()

        def cleaned(r):
            common.application.bus.unregister(self._components_loaded)
            if self._pm_cookie:
                d = self._un_inhibit()
                return d
        dfr.addCallback(cleaned)
        return dfr

    def _un_inhibit(self):
        self.debug("Asking for UnInhibition of PowerManager")
        dfr = defer.Deferred()
        cookie, self._pm_cookie = self._pm_cookie, None

        self.pm_iface.UnInhibit(cookie,
                                reply_handler=lambda : dfr.callback(None),
                                error_handler=lambda x: dfr.errback(x))

        return dfr

    def _inhibit(self, reason):
        dfr = defer.Deferred()
        self.debug("Asking for Inhibition of PowerManager")
        self.pm_iface.Inhibit('elisa', reason,
                              reply_handler=lambda x: dfr.callback(x),
                              error_handler=lambda x: dfr.errback(x))

        def got_reply(result):
            self._pm_cookie = result

        dfr.addCallback(got_reply)
        return dfr

    def _frontend_loaded(self, msg, sender):
        poblesec = msg.controller

        poblesec.video_player.player.connect('status-changed',
                                             self._player_status_cb)
        poblesec.music_player.player.connect('status-changed',
                                             self._player_status_cb)
        poblesec.slideshow_player.player.connect('status-changed',
                                                  self._player_status_cb)
        # we don't get any status-change in case we have 'playback-ended
        poblesec.music_player.player.connect('playback-ended',
                                             self._playback_ended)
        poblesec.video_player.player.connect('playback-ended',
                                             self._playback_ended)
        self.debug("connected to players")

    def _playback_ended(self, player):
        # just unInhibit
        self._un_inhibit()

    def _player_status_cb(self, player, status):
        self.debug("%s changed state to %s" % (player, status))
        if status == player.PLAYING and not self._pm_cookie:
            self._inhibit('playing media')
        elif self._pm_cookie:
            self._un_inhibit()
