
import pprint
import sys
import time

import gobject
import dbus.glib

from telepathy.client import (
    ConnectionManager, ManagerRegistry, Connection, Channel)
from telepathy.interfaces import (
    CONN_INTERFACE, CHANNEL_INTERFACE_GROUP, CHANNEL_TYPE_TEXT)
from telepathy.constants import (
    CONNECTION_HANDLE_TYPE_CONTACT, CONNECTION_HANDLE_TYPE_ROOM,
    CONNECTION_STATUS_CONNECTED, CONNECTION_STATUS_DISCONNECTED,
    CONNECTION_STATUS_CONNECTING, CONNECTION_HANDLE_TYPE_LIST)

CHANNEL_TYPE_TUBES = "org.freedesktop.Telepathy.Channel.Type.Tubes"

TUBE_TYPE_DBUS = 0

TUBE_STATE_LOCAL_PENDING = 0
TUBE_STATE_REMOTE_PENDING = 1
TUBE_STATE_OPEN = 2

tube_states = {
    TUBE_STATE_LOCAL_PENDING: 'local-pending',
    TUBE_STATE_REMOTE_PENDING: 'remote-pending',
    TUBE_STATE_OPEN: 'open'
}

loop = None
client = None

dev1_jid = 'daf@olpc.collabora.co.uk'
account_1 = {
        'account': dev1_jid,
        'password': 'badger',
        'server': 'olpc.collabora.co.uk',
}

dev2_jid = 'daf2@olpc.collabora.co.uk'
account_2 = {
        'account': dev2_jid,
        'password': 'badger',
        'server': 'olpc.collabora.co.uk',
}

room = 'test@conference.olpc.collabora.co.uk'

class Client:
    def __init__(self, conn):
        conn[CONN_INTERFACE].connect_to_signal('StatusChanged',
            self.status_changed_cb)
        conn[CONN_INTERFACE].Connect()
        self.conn = conn

    def connected_cb(self):
        self.self_handle = self.conn[CONN_INTERFACE].GetSelfHandle()
        print "self handle:", self.self_handle

        self.room_handle = self.conn.RequestHandles(
            CONNECTION_HANDLE_TYPE_ROOM, [room])[0]
        print "room handle:", self.room_handle

        print "joining room"
        self.channel_text = self.conn.request_channel(
            CHANNEL_TYPE_TEXT, CONNECTION_HANDLE_TYPE_ROOM,
            self.room_handle, True)
        print 'channel =', self.channel_text

        if sys.argv[1] == '1':
            self.dev1()
        else:
            self.dev2()

    def status_changed_cb_unsafe(self, state, reason):
        if state == CONNECTION_STATUS_CONNECTING:
            print 'connecting'
        elif state == CONNECTION_STATUS_CONNECTED:
            print 'connected'
            self.connected_cb()
        elif state == CONNECTION_STATUS_DISCONNECTED:
            print 'disconnected'
            loop.quit()
        else:
            raise RuntimeError

    def status_changed_cb(self, state, reason):
        self.status_changed_cb_unsafe(state, reason)

    def tubes_test(self):
        print "request tubes channel"
        chan_path = self.conn[CONN_INTERFACE].RequestChannel(
            CHANNEL_TYPE_TUBES, CONNECTION_HANDLE_TYPE_ROOM,
            self.room_handle, True)
        self.channel_tubes = Channel(self.conn._dbus_object._named_service,
            chan_path)

        self.channel_tubes[CHANNEL_TYPE_TUBES].connect_to_signal(
            "NewTube", self.new_tube_cb)
        self.channel_tubes[CHANNEL_TYPE_TUBES].connect_to_signal(
            "TubeStateChanged", self.tube_state_changed_cb)
        self.channel_tubes[CHANNEL_TYPE_TUBES].connect_to_signal(
            "TubeClosed", self.tube_closed_cb)

    def print_tube(self, id, initiator, type, service, params, state):
        initiator_name = self.conn.InspectHandles(
            CONNECTION_HANDLE_TYPE_CONTACT, [initiator])[0]
        print '  id:', id
        print '    initiator:', initiator_name
        print '    type:     ', type
        print '    service:  ', service
        print '    params:   ', dict(params)
        print '    state:    ', tube_states[state]

    def list_tubes(self):
        print "list tubes says:"
        tubes = self.channel_tubes[CHANNEL_TYPE_TUBES].ListTubes()

        if tubes:
            for tube in tubes:
                self.print_tube(*tube)
        else:
            print ' (no tubes)'

    def new_tube_cb(self, id, initiator, type, service, params, state):
        print "new tube:"
        self.print_tube(id, initiator, type, service, params, state)

        if state == TUBE_STATE_LOCAL_PENDING and initiator != self.self_handle:
            print "accepting tube"
            self.channel_tubes[CHANNEL_TYPE_TUBES].AcceptTube(id)
            self.list_tubes()

        addr = (self.channel_tubes[CHANNEL_TYPE_TUBES]
            .GetDBusServerAddress(id))
        assert addr
        print 'address:', addr
        print 'names:', pprint.pformat(list(
            self.channel_tubes[CHANNEL_TYPE_TUBES] .GetDBusNames(id)))

        import _dbus_bindings
        import dbus.lowlevel
        conn = _dbus_bindings.Connection(addr)
        conn.add_message_filter(lambda conn, msg:
            self.message_cb(conn, msg, id))
        msg = dbus.lowlevel.MethodCallMessage(
            'org.freedesktop.DBus', '/org/freedesktop/DBus',
            'org.freedesktop.DBus', 'Hello')
        conn.send_message(msg)
        msg = dbus.lowlevel.MethodCallMessage(
            ':1.2', '/foo/bar', 'org.foo', 'lala')
        conn.send_message(msg)
        conn.flush()
        # hack to keep the connection open
        self.dbus_conn = conn

    def message_cb(self, conn, msg, tube_id):
        print 'got %s on tube %d' % (msg.__class__.__name__, tube_id)
        print '  sender: %r' % msg.get_sender()
        print '  destination: %r' % msg.get_destination()
        print '  path: %r' % msg.get_path()
        print '  interface %r' % msg.get_interface()
        print '  member %r' % msg.get_member()
        print '  args: %r' % msg.get_args_list()

        print 'names:', pprint.pformat(list(
            self.channel_tubes[CHANNEL_TYPE_TUBES].GetDBusNames(tube_id)))

    def tube_state_changed_cb(self, id, state):
        print "tube state changed: tube: %d, state: %s" % (
            id, tube_states[state])

    def tube_closed_cb (self, id):
        print "tube closed", id

    # connect and wait
    def dev1(self):
        self.tubes_test()
        self.list_tubes()
        print "wait"

    # start chat with dev2 
    def dev2(self):
        import time
        # hack
        time.sleep(2)
        self.tubes_test()

        print "available tube types:", map(int,
            self.channel_tubes[CHANNEL_TYPE_TUBES].GetAvailableTubeTypes())

        self.list_tubes()

        params = {"login": "cassidy", "just_a_int" : 10}
        print "offer tube"
        id = self.channel_tubes[CHANNEL_TYPE_TUBES].OfferTube(0, "test", params)

def name_owner_changed_cb(name, old_owner, new_owner, account):
    global client

    if name != 'org.freedesktop.Telepathy.ConnectionManager.gabble':
        return

    if new_owner:
        print 'gabble now belongs to %s' % new_owner
        assert not client
        mgr = ConnectionManager(name, "/" + name.replace(".", "/"))
        conn = mgr.request_connection('jabber', account)
        client = Client(conn)
    else:
        print 'gabble went away'
        client = None

def main(account):
    bus = dbus.Bus()
    bus_obj = bus.get_object('org.freedesktop.DBus', '/org/freedesktop/DBus')
    bus_iface = dbus.Interface(bus_obj, 'org.freedesktop.DBus')
    bus_iface.connect_to_signal('NameOwnerChanged',
        lambda name, old, new: name_owner_changed_cb(name, old, new, account))
    loop = gobject.MainLoop()
    loop.run()

    if client:
        try:
            client.conn.Disconnect()
        except:
            pass

    return 0

if __name__ == '__main__':
    import logging
    logging.basicConfig()

    account_name = sys.argv[1]
    account = {'1': account_1, '2': account_2}[account_name]
    raise sys.exit(main(account))

