#!/usr/bin/python3
# -*- coding: utf-8 -*-
"""
 Copyright © 2017 Bilal Elmoussaoui <bil.elmoussaoui@gmail.com>
 Copyright (c) 2014-2016 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
 This file is part of Authenticator and originally from Lollypop.

 Authenticator 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.

 Authenticator 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 Authenticator. If not, see <http://www.gnu.org/licenses/>.
"""

import sys
sys.path.insert(1, '/usr/lib/python3.8/site-packages')

from gi import require_version
require_version('Gtk', '3.0')
require_version('Gdk', '3.0')
require_version('Secret', '1')
from gi.repository import Gio, GLib

from Authenticator.models import AccountsManager, Account, Clipboard, Keyring

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 AuthenticatorSearchProvider(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>
    '''

    __AUTHENTICATOR_BUS = 'com.github.bilelmoussaoui.Authenticator.SearchProvider'
    __SEARCH_BUS = 'org.gnome.Shell.SearchProvider2'
    __PATH_BUS = '/com/github/bilelmoussaoui/Authenticator/SearchProvider'

    def __init__(self):
        Gio.Application.__init__(
            self,
            application_id=self.__AUTHENTICATOR_BUS,
            flags=Gio.ApplicationFlags.IS_SERVICE)
        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, account_id, *_):
        account = Account.get_by_id(int(account_id))
        if account and account.otp:
            Clipboard.set(account.otp.pin)

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

    def GetResultMetas(self, ids):
        results = []
        try:
            for search_id in ids:
                account = Account.get_by_id(int(search_id))
                gicon = account.provider.image_path
                description = "{} - {}".format(account.provider.name, account.username)

                d = {'id': GLib.Variant('s', search_id),
                     'description': GLib.Variant('s', description),
                     'name': GLib.Variant('s', account.username),
                     'gicon': GLib.Variant('s', gicon)
                     }
                results.append(d)
        except Exception as e:
            print("AuthenticatorSearchProvider::GetResultMetas():", e)
            return []
        return results

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

    def LaunchSearch(self, *_):
        GLib.spawn_async_with_pipes(
            None, ["authenticator"], None,
            GLib.SpawnFlags.SEARCH_PATH |
            GLib.SpawnFlags.DO_NOT_REAP_CHILD, None)

    def __search(self, terms):
        ids = []
        # Don't allow search if the app is locked with a password.
        if not Keyring.get_default().is_password_enabled():
            try:
                accounts = AccountsManager.get_default().search(terms)
                ids = [str(account.id) for account in accounts]
            except Exception as e:
                print("AuthenticatorSearchProvider::__search():", e)
        return ids


if __name__ == '__main__':
    service = AuthenticatorSearchProvider()
    service.hold()
    service.run()
