#!/usr/bin/env python3
# Copyright (c) 2014-2017 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, '')
# Make sure we'll find the eolie modules, even in JHBuild
sys.path.insert(1, '/usr/lib/python3.9/site-packages/')

import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
gi.require_version('Secret', '1')

from gi.repository import Gio, GLib

from eolie.art import Art
from eolie.settings import Settings
from eolie.sqlcursor import SqlCursor
from eolie.database_bookmarks import DatabaseBookmarks
from eolie.define import ArtSize


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 == '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 SearchEolieService(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.Shell.SearchProvider2">

    <method name="GetInitialResultSet">
      <arg type="as" name="terms" direction="in" />
      <arg type="as" name="results" direction="out" />
    </method>

    <method name="GetSubsearchResultSet">
      <arg type="as" name="previous_results" direction="in" />
      <arg type="as" name="terms" direction="in" />
      <arg type="as" name="results" direction="out" />
    </method>

    <method name="GetResultMetas">
      <arg type="as" name="identifiers" direction="in" />
      <arg type="aa{sv}" name="metas" direction="out" />
    </method>

    <method name="ActivateResult">
      <arg type="s" name="identifier" direction="in" />
      <arg type="as" name="terms" direction="in" />
      <arg type="u" name="timestamp" direction="in" />
    </method>

    <method name="LaunchSearch">
      <arg type="as" name="terms" direction="in" />
      <arg type="u" name="timestamp" direction="in" />
    </method>

    </interface>
    </node>
    '''
    __EOLIE_BUS = 'org.gnome.Eolie.SearchProvider'
    __SEARCH_BUS = 'org.gnome.Shell.SearchProvider2'
    __PATH_BUS = '/org/gnome/EolieSearchProvider'

    def __init__(self):
        Gio.Application.__init__(
                            self,
                            application_id='org.gnome.Eolie.SearchProvider',
                            flags=Gio.ApplicationFlags.IS_SERVICE)
        self.cursors = {}
        self.settings = Settings.new()
        self.bookmarks = DatabaseBookmarks()
        SqlCursor.add(self.bookmarks)
        self.art = Art()
        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 ActivateResult(self, bookmark_id, array, utime):
        try:
            uri = self.bookmarks.get_uri(bookmark_id)
            argv = ["eolie", uri, None]
            GLib.spawn_async_with_pipes(
                                    None, argv, None,
                                    GLib.SpawnFlags.SEARCH_PATH |
                                    GLib.SpawnFlags.DO_NOT_REAP_CHILD, None)
        except Exception as e:
            print(e)

    def GetInitialResultSet(self, terms):
        return self.__search(terms)

    def GetResultMetas(self, ids):
        results = []
        try:
            for bookmark_id in ids:
                title = self.bookmarks.get_title(bookmark_id)
                uri = self.bookmarks.get_uri(bookmark_id)
                art = self.art.get_path(uri, "favicon")
                d = { 'id': GLib.Variant('s', bookmark_id),
                      'description': GLib.Variant('s', uri),
                      'name': GLib.Variant('s', title),
                      'gicon': GLib.Variant('s', art) }
                results.append(d)
        except Exception as e:
            print(e)
            return []
        return results

    def GetSubsearchResultSet(self, previous_results, new_terms):
        return self.__search(new_terms)

    def LaunchSearch(self, terms, utime):
        argv = ["eolie"]
        for bookmark_id in self.__search(terms):
            argv.append(self.bookmarks.get_uri(int(bookmark_id)))
        argv.append(None)
        GLib.spawn_async_with_pipes(
                                    None, argv, None,
                                    GLib.SpawnFlags.SEARCH_PATH |
                                    GLib.SpawnFlags.DO_NOT_REAP_CHILD, None)

    def __search(self, terms):
        ids = []
        search = " ".join(terms)
        try:
            # Search for bookmarks
            for (id, title, uri) in self.bookmarks.search(search, 20):
                ids.append(str(self.bookmarks.get_id(uri)))
        except Exception as e:
            print(e)
        return ids

def main():
    service = SearchEolieService()
    service.hold()
    service.run()

if __name__ == '__main__':
    main()
