#!/usr/bin/env python3
# Copyright (c) 2014-2016 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
# This program 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.
# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.

import sys
# Make sure we'll find the pygobject module, even in JHBuild
sys.path.insert(1, '/usr/lib/python3.6/site-packages')
# Make sure we'll find the lollypop modules, even in JHBuild
sys.path.insert(1, '/usr/lib/python3.6/site-packages')

import gi
gi.require_version('Gst', '1.0')
from gi.repository import Gio, Gst, GLib

from re import findall, DOTALL

TAG_EDITORS = ['kid3-qt', 'exfalso', 'easytag', 'picard', 'puddletag']


class Server:
    def __init__(self, con, path):
        method_outargs = {}
        method_inargs = {}
        for interface in Gio.DBusNodeInfo.new_for_xml(self.__doc__).interfaces:

            for method in interface.methods:
                method_outargs[method.name] = '(' + ''.join(
                              [arg.signature for arg in method.out_args]) + ')'
                method_inargs[method.name] = tuple(
                                       arg.signature for arg in method.in_args)

            con.register_object(object_path=path,
                                interface_info=interface,
                                method_call_closure=self.on_method_call)

        self.method_inargs = method_inargs
        self.method_outargs = method_outargs

    def on_method_call(self,
                       connection,
                       sender,
                       object_path,
                       interface_name,
                       method_name,
                       parameters,
                       invocation):

        args = list(parameters.unpack())
        for i, sig in enumerate(self.method_inargs[method_name]):
            if sig is 'h':
                msg = invocation.get_message()
                fd_list = msg.get_unix_fd_list()
                args[i] = fd_list.get(args[i])

        try:
            result = getattr(self, method_name)(*args)

            # out_args is atleast (signature1).
            # We therefore always wrap the result as a tuple.
            # Refer to https://bugzilla.gnome.org/show_bug.cgi?id=765603
            result = (result,)

            out_args = self.method_outargs[method_name]
            if out_args != '()':
                variant = GLib.Variant(out_args, result)
                invocation.return_value(variant)
            else:
                invocation.return_value(None)
        except Exception as e:
            pass


class PortalLollypopService(Server, Gio.Application):
    '''
    <!DOCTYPE node PUBLIC
    '-//freedesktop//DTD D-BUS Object Introspection 1.0//EN'
    'http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd'>
    <node>
    <interface name="org.gnome.Lollypop.Portal">

    <method name="PaListSinks">
      <arg type="aas" name="results" direction="out" />
    </method>
    <method name="CanSetCover">
      <arg type="b" name="can" direction="out" />
    </method>
    <method name="SetCover">
      <arg type="s" name="audio" direction="in" />
      <arg type="s" name="cover" direction="in" />
    </method>
    <method name="CanLaunchTagEditor">
      <arg type="b" name="can" direction="out" />
    </method>
    <method name="LaunchTagEditor">
      <arg type="s" name="f" direction="in" />
    </method>
    <method name="SetPopularity">
      <arg type="i" name="pop" direction="in" />
      <arg type="s" name="f" direction="in" />
    </method>
    </interface>
    </node>
    '''
    __LOLLYPOP_BUS = 'org.gnome.Lollypop.Portal'
    __SEARCH_BUS = 'org.gnome.Lollypop.Portal'
    __PATH_BUS = '/org/gnome/LollypopPortal'

    def __init__(self):
        Gio.Application.__init__(
                            self,
                            application_id='org.gnome.Lollypop.Portal',
                            flags=Gio.ApplicationFlags.IS_SERVICE)
        self.settings = Gio.Settings.new('org.gnome.Lollypop')
        self.__bus = Gio.bus_get_sync(Gio.BusType.SESSION, None)
        Gio.bus_own_name_on_connection(self.__bus,
                                       self.__SEARCH_BUS,
                                       Gio.BusNameOwnerFlags.NONE,
                                       None,
                                       None)
        Server.__init__(self, self.__bus, self.__PATH_BUS)

    def CanSetCover(self):
        return GLib.find_program_in_path("kid3-cli") is not None

    def PaListSinks(self):
        ret = []
        argv = ["pacmd", "list-sinks", None]
        try:
            (s, out, err, e) = GLib.spawn_sync(None, argv, None,
                                               GLib.SpawnFlags.SEARCH_PATH,
                                               None)
            string = out.decode('utf-8')
            devices = findall('name: <([^>]*)>', string, DOTALL)
            names = findall('device.description = "([^"]*)"', string, DOTALL)
            for name in names:
                ret.append([name, devices.pop(0)])
        except Exception as e:
            print("PortalLollypopService::PaListSinks()", e)
        return ret

    def SetCover(self, audio, cover):
        argv = ["kid3-cli", "-c", "select all", "-c",
                    "set picture:'%s' ''" % cover, audio, None]
        GLib.spawn_sync(None, argv, None,
                        GLib.SpawnFlags.SEARCH_PATH, None)

    def CanLaunchTagEditor(self):
        favorite = self.settings.get_value('tag-editor').get_string()
        for tag_editor in [favorite] + TAG_EDITORS:
            if GLib.find_program_in_path(tag_editor) is not None:
                return True
        return False

    def LaunchTagEditor(self, f):
        favorite = self.settings.get_value('tag-editor').get_string()
        editor = None
        for tag_editor in [favorite] + TAG_EDITORS:
            if GLib.find_program_in_path(tag_editor) is not None:
                editor = tag_editor
                break
        if editor is not None:
            argv = [editor, f, None]
            GLib.spawn_sync(None, argv, None,
                            GLib.SpawnFlags.SEARCH_PATH, None)

    def SetPopularity(self, pop, f):
        argv = ["kid3-cli", "-c", "set pop: %s" % pop, f, None]
        GLib.spawn_sync(None, argv, None,
                        GLib.SpawnFlags.SEARCH_PATH, None)


def main():
    Gst.init(None)
    service = PortalLollypopService()
    service.hold()
    service.run()

if __name__ == '__main__':
    main()

