#!/usr/bin/python

# =============================================================================
#
#    Remuco - A remote control system for media players.
#    Copyright (C) 2006-2010 by the Remuco team, see AUTHORS.
#
#    This file is part of Remuco.
#
#    Remuco is free software: you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation, either version 3 of the License, or
#    (at your option) any later version.
#
#    Remuco is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with Remuco.  If not, see <http://www.gnu.org/licenses/>.
#
# =============================================================================

"""QuodLibet adapter for Remuco, implemented as an executable script."""

# TODO: This adapter currently is a script. An adapter implemented as a QL
#       plugin probably would provide significantly more features, especially
#       concerning media library integration.

import commands

import dbus
from dbus.exceptions import DBusException
import gobject

import remuco
from remuco import log

class QuodLibetAdapter(remuco.PlayerAdapter):

    def __init__(self):
        remuco.PlayerAdapter.__init__(self,
                                      "QuodLibet",
                                      progress_known=True,
                                      playback_known=True,
                                      repeat_known=True,
                                      shuffle_known=True,
                                      max_rating=4)

        self.__dbus_signal_handler = ()
        self.__ql_dbus = None
        self.__song_len = 0 # need this when polling progress

    def start(self):

        remuco.PlayerAdapter.start(self)

        # set up DBus connection

        try:
            bus = dbus.SessionBus()
            proxy = bus.get_object("net.sacredchao.QuodLibet",
                                   "/net/sacredchao/QuodLibet")
            self.__ql_dbus = dbus.Interface(proxy, "net.sacredchao.QuodLibet")
        except DBusException, e:
            raise StandardError("dbus error: %s" % e)

        # connect to DBus signals

        try:
            self.__dbus_signal_handler = (
                self.__ql_dbus.connect_to_signal("Paused",
                                                 self.__on_paused),
                self.__ql_dbus.connect_to_signal("Unpaused",
                                                 self.__on_unpaused),
                self.__ql_dbus.connect_to_signal("SongStarted",
                                                 self.__on_song_started),
                self.__ql_dbus.connect_to_signal("SongEnded",
                                                 self.__on_song_ended),
            )
        except DBusException, e:
            raise StandardError("dbus error: %s" % e)

        # misc initialization

        self.__song_len = 0

        # initial info retrieval

        self.poll()

        if self.__ql_dbus.IsPlaying():
            self.__on_song_started(self.__ql_dbus.CurrentSong())
            self.update_playback(remuco.PLAYBACK_PLAY)
        else:
            self.update_playback(remuco.PLAYBACK_PAUSE)

    def stop(self):

        remuco.PlayerAdapter.stop(self)

        # disconnect DBus stuff

        for handler in self.__dbus_signal_handler:
            handler.remove()

        self.__dbus_signal_handler = ()

        self.__ql_dbus = None

    def poll(self):

        self.__poll_status()
        self.__poll_progress()

    # =========================================================================
    # control interface
    # =========================================================================

    def ctrl_toggle_playing(self):
        self.__ql_dbus.PlayPause()

    def ctrl_next(self):
        self.__ql_dbus.Next()

    def ctrl_previous(self):
        self.__ql_dbus.Previous()

    def ctrl_toggle_shuffle(self):
        self.__ql_cmd("--order=toggle")
        gobject.idle_add(self.__poll_status)

    def ctrl_toggle_repeat(self):
        self.__ql_cmd("--repeat=t")
        gobject.idle_add(self.__poll_status)

    def ctrl_volume(self, direction):
        if direction == 0:
            self.__ql_cmd("--volume=0")
        elif direction == -1:
            self.__ql_cmd("--volume-down")
        else:
            self.__ql_cmd("--volume-up")
        gobject.idle_add(self.__poll_status)

    def ctrl_rate(self, rating):
        self.__ql_cmd("--set-rating=%1.1f" % (float(rating) / 4))

    # =========================================================================
    # request interface
    # =========================================================================

    # ... not yet supported, see TODO on top

    # =========================================================================
    # helper
    # =========================================================================

    def __ql_cmd(self, action):
        """QL command line interaction."""

        return commands.getoutput("quodlibet " + action)

    def __on_paused(self):
        """DBus signal handler."""

        self.update_playback(remuco.PLAYBACK_PAUSE)

    def __on_unpaused(self):
        """DBus signal handler."""

        self.update_playback(remuco.PLAYBACK_PLAY)

    def __on_song_started(self, song):
        """DBus signal handler."""

        info = {}

        info[remuco.INFO_ALBUM] = song.get("album", None)
        info[remuco.INFO_ARTIST] = song.get("artist", None)
        info[remuco.INFO_GENRE] = song.get("genre", None)
        info[remuco.INFO_LENGTH] = song.get("~#length", 0)
        info[remuco.INFO_RATING] = int(float(song.get("~#rating", 0.5)) * 4)
        info[remuco.INFO_TITLE] = song.get("title", None)
        info[remuco.INFO_YEAR] = song.get("date", 0)
        info[remuco.INFO_BITRATE] = int(song.get("~#bitrate", 0)) / 1000

        self.__song_len = info[remuco.INFO_LENGTH]

        # QL does not provide an ID/URI -> no chance to get local cover art
        self.update_item(None, info, None)

    def __on_song_ended(self, song, skipped):
        """DBus signal handler."""

        self.update_item(None, None, None)
        self.__song_len = 0
        self.update_progress(0, 0)

    def __poll_status(self):
        """Poll volume, repeat and shuffle."""

        status = self.__ql_cmd("--status").split()

        if status and len(status) == 5:
            self.update_volume(float(status[2]) * 100)
            self.update_shuffle(status[3] != "inorder")
            self.update_repeat(status[4] == "on")
        else:
            self.update_volume(50)
            self.update_shuffle(False)
            self.update_repeat(False)

    def __poll_progress(self):
        """Poll playback progress."""

        if self.__ql_dbus.IsPlaying():
            self.update_progress(self.__ql_dbus.GetPosition() / 1000,
                                 self.__song_len)

# =============================================================================
# main
# =============================================================================

if __name__ == '__main__':

    pa = QuodLibetAdapter()
    mg = remuco.Manager(pa, dbus_name="net.sacredchao.QuodLibet")
    mg.run()
