#!/usr/bin/env python
# encoding: utf-8
#
# A simple Telepathy dialler, suitable for use when a nicer UI you're
# developing doesn't work yet.
#
# Copyright © 2012 Collabora Ltd.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

import gi
from gi.repository import GLib
from gi.repository import GObject
from gi.repository import Gio
from gi.repository import Gtk
gi.require_version('TelepathyGLib', '0.12')
from gi.repository import TelepathyGLib as Tp
import sys

GObject.threads_init()

# not available via g-i
USER_ACTION_TIME_NOT_USER_ACTION = 0
ACCOUNT_MANAGER_FEATURE_CORE = GLib.quark_from_string(
    'tp-account-manager-feature-core')

class Dialler(Gtk.Application):

    def __init__(self):
        Gtk.Application.__init__(self,
                application_id='im.telepathy.TpGLib.Example.PythonDialler',
                flags=Gio.ApplicationFlags.NON_UNIQUE)

        self.dbus = Tp.DBusDaemon.dup()
        self.am = Tp.AccountManager.dup()

        self.accounts = Gtk.ListStore(str, object)
        self.potential_handlers = set()
        self.handlers = Gtk.ListStore(str, str)

        self.window = None

    def do_activate(self):
        if self.window is not None:
            self.window.present()
            return

        self.window = Gtk.Window()
        self.grid = Gtk.Grid()
        self.grid.props.margin = 6
        self.grid.props.column_spacing = 6
        self.grid.props.row_spacing = 6
        self.window.add(self.grid)

        row = 0

        self.grid.attach(Gtk.Label('local account:'), 0, row, 1, 1)
        self.account_combo = Gtk.ComboBox.new_with_model(self.accounts)
        renderer = Gtk.CellRendererText()
        self.account_combo.pack_start(renderer, True)
        self.account_combo.add_attribute(renderer, "text", 0)
        self.grid.attach(self.account_combo, 1, row, 1, 1)
        row += 1

        self.grid.attach(Gtk.Label('remote target:'), 0, row, 1, 1)
        self.target_entry = Gtk.Entry()
        self.target_entry.set_text('smcv@example.com')
        self.grid.attach(self.target_entry, 1, row, 1, 1)
        row += 1

        self.grid.attach(Gtk.Label('preferred handler:'), 0, row, 1, 1)
        self.handler_combo = Gtk.ComboBox.new_with_model(self.handlers)
        renderer = Gtk.CellRendererText()
        self.handler_combo.pack_start(renderer, True)
        self.handler_combo.add_attribute(renderer, "text", 0)
        self.grid.attach(self.handler_combo, 1, row, 1, 1)
        row += 1

        self.dial_button = Gtk.Button('dial')
        self.dial_button.connect('clicked', self._dial_clicked)
        self.grid.attach(self.dial_button, 0, row, 2, 1)
        row += 1

        self.dial_button.props.sensitive = False
        self.window.show_all()
        self.add_window(self.window)

        self.am.prepare_async([], self._am_cb, None)
        Gio.bus_get(Gio.BusType.SESSION, None,
                self._session_bus_cb, None)

    def _am_cb(self, am, result, user_data):
        am.prepare_finish(result)

        for account in am.get_valid_accounts():
            assert(account.object_path.startswith(Tp.ACCOUNT_OBJECT_PATH_BASE))

            tail = account.object_path[len(Tp.ACCOUNT_OBJECT_PATH_BASE):]
            self.accounts.append([account.get_display_name() or tail, account])

        self.account_combo.set_active(0)
        self.poll()

    def poll(self):
        if (self.am.is_prepared(ACCOUNT_MANAGER_FEATURE_CORE) and
            len(self.handlers) > 0 and
            len(self.potential_handlers) == 0):
            self.dial_button.props.sensitive = True

    def _session_bus_cb(self, session_bus, result, user_data):
        self.session_bus = Gio.bus_get_finish(result)

        self.session_bus.call('org.freedesktop.DBus',
                '/org/freedesktop/DBus',
                'org.freedesktop.DBus',
                'ListNames',
                GLib.Variant("()", ()),
                GLib.VariantType.new("(as)"),
                Gio.DBusCallFlags.NONE,
                -1,
                None,
                self._names_cb,
                None)

    def _names_cb(self, session_bus, result, user_data):
        names = set(session_bus.call_finish(result)[0])

        self.handlers.append(['(no preference)', ''])
        self.handler_combo.set_active(0)

        for name in sorted(names):
            if name.startswith(Tp.CLIENT_BUS_NAME_BASE):
                self.potential_handlers.add(name)
                Gio.DBusProxy.new(session_bus,
                        Gio.DBusProxyFlags.NONE,
                        None,
                        name,
                        '/' + name.replace('.', '/'),
                        Tp.IFACE_CLIENT_HANDLER,
                        None,
                        self._potential_handler_cb,
                        name)

        self.poll()

    def _potential_handler_cb(self, proxy, result, name):
        self.potential_handlers.remove(name)
        proxy = Gio.DBusProxy.new_finish(result)
        filters = proxy.get_cached_property("HandlerChannelFilter")
        if filters is not None:
            for asv in filters:
                if asv.get(Tp.PROP_CHANNEL_CHANNEL_TYPE) == Tp.IFACE_CHANNEL_TYPE_CALL:
                    tail = name[len(Tp.CLIENT_BUS_NAME_BASE):]
                    self.handlers.append([tail, name])
                    break

        self.poll()

    def _dial_clicked(self, button):
        tree_iter = self.account_combo.get_active_iter()
        account = self.accounts[tree_iter][1]
        tree_iter = self.handler_combo.get_active_iter()
        handler = self.handlers[tree_iter][1]

        acr = Tp.AccountChannelRequest.new(account,
                {
                    Tp.PROP_CHANNEL_CHANNEL_TYPE:
                        Tp.IFACE_CHANNEL_TYPE_CALL,
                    Tp.PROP_CHANNEL_TARGET_HANDLE_TYPE:
                        int(Tp.HandleType.CONTACT),
                    Tp.PROP_CHANNEL_TARGET_ID: self.target_entry.get_text(),
                    Tp.PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO: True,
                },
                USER_ACTION_TIME_NOT_USER_ACTION)
        acr.create_and_observe_channel_async(handler, None,
                self._create_call_cb, None)

    def _create_call_cb(self, acr, result, user_data):
        channel = acr.create_and_observe_channel_finish(result)
        print("accepting channel %s" % channel)
        channel.accept_async(self._accept_cb, None)

    def _accept_cb(self, channel, result, user_data):
        channel.accept_finish(result)
        print("accepted channel %s" % channel)

if __name__ == '__main__':
    Tp.debug_set_flags("all")

    sys.exit(Dialler().run(sys.argv))
