#!/usr/bin/env python
#
# Author: Morten Mjelva <morten.mjelva@gmail.com>
#
import dbus, dbus.glib
import gobject

import logging
logging.basicConfig(level=logging.DEBUG)

import telepathy
from telepathy.constants import * 
from telepathy.interfaces import * 

from accountmgr import AccountManager
from account import Account

DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'

def error(*args):
    logging.error("error %s" % args)

class PAccountManager(AccountManager):
    def __init__(self):
        logging.debug("Setting up account manager object")
        self.conn_cache = {}
        self.channel_cache = {}

        AccountManager.__init__(self)
        for acct_path in self[DBUS_PROPERTIES].Get(ACCOUNT_MANAGER, 'ValidAccounts'):
            acct = Account(acct_path)
            conn_status = acct[DBUS_PROPERTIES].Get(ACCOUNT, 'ConnectionStatus')
            
            if conn_status == 0: # online
                conn_path = acct[DBUS_PROPERTIES].Get(ACCOUNT, 'Connection')
                if conn_path not in self.conn_cache:
                    self.conn_cache[conn_path] = PConnection(conn_path,
                            self._connection_ready)

        #TODO: Connect to AccountValidityChanged signal

    def _channel_ready(self, chan):
        print chan

    def _create_channel(self, conn, ours, channel, properties):
        if channel not in self.channel_cache:
            self.channel_cache[channel] = PChannel(conn, channel, properties,
                    ready_handler=self._channel_ready)

    def _ensure_channels(self, conn):
        conn[CONNECTION_INTERFACE_REQUESTS].EnsureChannel({
                CHANNEL + '.ChannelType': CHANNEL_TYPE_CONTACT_LIST,
                CHANNEL + '.TargetHandleType': HANDLE_TYPE_LIST,
                CHANNEL + '.TargetID': 'subscribe',
                },
                reply_handler=lambda ours, channel, properties: self._create_channel(conn, ours, channel, properties),
                error_handler=error)

    # Called when the connection proxy is invalidated
    def _connection_closed(self, conn):
        logging.debug("Connection closed: %s" % conn)
        del self.connection_cache[conn.object_path]

    # Called when PConnection object has finished its tasks
    # Connects to 'disconnected' signal to listen for connection
    # invalidation. Triggers creation of PChannel objects for the
    # connection
    def _connection_ready(self, conn):
        logging.debug("Connection ready: %s" % conn)
        conn.connect('disconnected', self._connection_closed)
        self._ensure_channels(conn)

class PConnection(gobject.GObject, telepathy.client.Connection):
    """
    Extends telepathy.client.Connection, storing information about a proxy object

    Object extends tp.Connection object, letting us store information about the
    connection proxy locally.
    Arguments:
        path: Unique path to this connection
        ready_handler: Callback function run once we're done setting up object
    """
    __gsignals__ = {
            'disconnected': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
            }

    def __init__(self, path, ready_handler):
        service_name = path.replace('/', '.')[1:]

        # Ready callback
        self.ready_handler = ready_handler
        self.signals = []

        gobject.GObject.__init__(self)
        telepathy.client.Connection.__init__(self, service_name, path,
                ready_handler = self._connection_ready)

    def __repr__(self):
       return "PConnection(%s)" % self.object_path
    
    def _status_changed(self, status, reason):
        if status == CONNECTION_STATUS_DISCONNECTED:
            for signal in self.signals:
                signal.remove()
            self.emit('disconnected')

    def _connection_ready(self, conn):
        self.signals.append(
                self[CONNECTION].connect_to_signal('StatusChanged',
                    self._status_changed))
        self.ready_handler(self)

    def do_disconnected(self):
        pass
gobject.type_register(PConnection)

class PChannel(gobject.GObject, telepathy.client.Channel):
    """
    Extends telepathy.client.Channel

    This class allows us to store information about a telepathy.client.Channel
    object locally.
    Arguments:
    account_path: Path to the account used. Passed to ObserveChannels.
    connection: The connection used for the channel
    properties: Properties of the channel we're proxying, from ObserveChannels
    ready_handler: Callback function run once we're done setting up object
    """
    __gsignals__ = { 
            'closed': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
            }

    def __init__(self, connection, path, properties, account_path=None,
        ready_handler=None):
        
        self.account_path = account_path
        self.conn = connection
        self.properties = properties
        self.ready_handler = ready_handler

        self.pretty_statuses = {'available': "Available", 'away': 'Away',
                'busy': 'Busy', 'xa': 'Not Available'}

        self.handles = []
        self.signals = []

        gobject.GObject.__init__(self)
        telepathy.client.Channel.__init__(self, connection.service_name, path,
                ready_handler=self._channel_ready)

    def __repr__(self):
        return "PChannel(%s)" % self.object_path

    def _contact_attributes(self, attributes):
        self.handles = attributes

    def _presences_changed(self, presence):
        for key in presence:
            try:
                alias = self.handles[key][CONNECTION_INTERFACE_ALIASING + '/alias']
                status = presence[key][1]
                logging.debug("%s is now %s" % (alias, self.pretty_statuses[status]))
            except KeyError:
                logging.error("Contact handle not in list")

    def _members_changed(self, message, added, removed, local_pending,
            remote_pending, actor, reason):
        #TODO: Delete or add members to self.handles, whichever is appropriate
        pass

    def _channel_ready(self, channel):
        logging.debug("channel ready: %s" % channel)

        members = channel[DBUS_PROPERTIES].Get(CHANNEL_INTERFACE_GROUP, 'Members')
        self.conn[CONNECTION_INTERFACE_CONTACTS].GetContactAttributes(
                members,
                [CONNECTION_INTERFACE_ALIASING],
                False,
                reply_handler=self._contact_attributes,
                error_handler=error)

        self.signals.append(self.conn[CONNECTION_INTERFACE_SIMPLE_PRESENCE].connect_to_signal('PresencesChanged',
            self._presences_changed))
        self.signals.append(self[CHANNEL_INTERFACE_GROUP].connect_to_signal('MembersChanged',
            self._members_changed))
gobject.type_register(PChannel)

def main():
    # Get accounts, filter out the invalid ones, check which of the valid ones
    # are online, and create connection proxies for the online accounts
            
    def publish():
        PAccountManager()

    gobject.timeout_add(0, publish)
    loop = gobject.MainLoop()
    loop.run()
    return False

if __name__ == '__main__':
    main()
