#!/usr/bin/python

import time

import dbus.glib

from twisted.internet import glib2reactor
glib2reactor.install()

from twisted.words.protocols import irc
from twisted.internet import reactor, protocol

import telepathy.interfaces
import telepathy.server

nickname = 'daf_tp'

class Client(irc.IRCClient):
    nickname = nickname

    def connectionMade(self):
        self.tp_conn = self.factory.tp_conn

        irc.IRCClient.connectionMade(self)

    def connectionLost(self, reason):
        self.tp_conn._disconnected()

    def signedOn(self):
        self.tp_conn._connected(self)

    def joined(self, channel):
        self.tp_conn._joined(channel)

    def privmsg(self, user, channel, message):
        user = user.split('!', 1)[0]
        self.tp_conn._message_received(user, channel, message)

    def irc_RPL_NAMREPLY(self, prefix, params):
        channel = params[2]
        nicks = [nick.lstrip('+@') for nick in params[3].split()]
        self.tp_conn._members_add(channel, nicks)

class ClientFactory(protocol.ClientFactory):
    protocol = Client

    def __init__(self, tp_conn):
        self.tp_conn = tp_conn

class IRCChannelChannel(
        telepathy.server.ChannelTypeText,
        telepathy.server.ChannelInterfaceGroup,
        telepathy.server.PropertiesInterface):
    def __init__(self, connection, handle_type, handle):
        telepathy.server.ChannelTypeText.__init__(self, connection, handle)
        telepathy.server.ChannelInterfaceGroup.__init__(self)
        telepathy.server.PropertiesInterface.__init__(self)

        self._next_id = 1

    def ListProperties(self):
        return []

    def Send(self, type, msg):
        self._conn._channel_send(self, self._handle, msg)
        self.Sent(long(time.time()),
            telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, msg)

    def _message_received(self, user, msg):
        handle = self._conn._handle_lookup(
            telepathy.CONNECTION_HANDLE_TYPE_CONTACT, user)
        self.Received(self._next_id, long(time.time()), handle.get_id(),
            telepathy.CHANNEL_TEXT_MESSAGE_TYPE_NORMAL, 0, msg)
        self._next_id += 1

    def _members_add(self, nicks):
        handles = [self._conn._handle_lookup(
            telepathy.CONNECTION_HANDLE_TYPE_CONTACT, nick).get_id()
            for nick in nicks]
        self.MembersChanged('', handles, [], [], [], 0,
            telepathy.CHANNEL_GROUP_CHANGE_REASON_NONE)

class Connection(
        telepathy.server.Connection,
        telepathy.server.ConnectionInterfacePresence):
    def __init__(self, manager, parameters):
        telepathy.server.Connection.__init__(self, 'irc', 'irc')
        telepathy.server.ConnectionInterfacePresence.__init__(self)

        self._client = None
        self._channels = {}
        self._pending_channels = {}
        self._handle_cache = {}
        self._self_handle = self._request_handle(
            telepathy.CONNECTION_HANDLE_TYPE_CONTACT, nickname)
        self._connect()

    def _request_handle(self, type, name):
        id = self.get_handle_id()
        handle = telepathy.server.Handle(id, type, name)
        self._handles[type, id] = handle
        self._handle_cache[name] = handle
        return handle

    def _handle_lookup(self, type, name):
        if name in self._handle_cache:
            return self._handle_cache[name]
        else:
            return self._request_handle(type, name)

    def _connect(self):
        factory = ClientFactory(self)
        reactor.connectTCP('irc.freenode.net', 6667, factory)
        self.StatusChanged(
            telepathy.CONNECTION_STATUS_CONNECTING,
            telepathy.CONNECTION_STATUS_REASON_REQUESTED)

    def _connected(self, client):
        self._client = client
        self.StatusChanged(
            telepathy.CONNECTION_STATUS_CONNECTED,
            telepathy.CONNECTION_STATUS_REASON_REQUESTED)

    def _disconnected(self):
        self.Disconnect()
        self.StatusChanged(
            telepathy.CONNECTION_STATUS_DISCONNECTED,
            telepathy.CONNECTION_STATUS_REASON_NETWORK_ERROR)

    @dbus.service.method(telepathy.CONN_INTERFACE, in_signature='suub',
        out_signature='o', async_callbacks=('return_cb', 'error_cb'))
    def RequestChannel(self, type, handle_type, handle, suppress_handler,
            return_cb, error_cb):
        if type != telepathy.interfaces.CHANNEL_TYPE_TEXT:
            raise telepathy.errors.NotImplemented()

        if handle_type != telepathy.constants.CONNECTION_HANDLE_TYPE_ROOM:
            raise telepathy.errors.NotImplemented()

        name = self.InspectHandles(handle_type, [handle])[0].encode('ascii')
        self._pending_channels[name] = (return_cb, handle_type, handle)
        self._client.join(name)

    def _joined(self, name):
        cb, handle_type, handle = self._pending_channels.get(name)

        if cb is None:
            return

        del self._pending_channels[name]
        channel = IRCChannelChannel(self, handle_type, handle)
        self._channels[name] = channel
        cb(channel._object_path)

    def _channel_send(self, channel, handle, msg):
        name = self.InspectHandles(telepathy.CONNECTION_HANDLE_TYPE_ROOM,
            [handle])[0].encode('ascii')
        self._client.msg(name, msg.encode('utf8'))

    def _message_received(self, user, channel, msg):
        channel = self._channels.get(channel)

        if channel is None:
            return

        channel._message_received(user, msg)

    def _members_add(self, channel, nicks):
        channel = self._channels.get(channel)

        if channel is None:
            return

        channel._members_add(nicks)

class ConnectionManager(telepathy.server.ConnectionManager):
    def __init__(self):
        telepathy.server.ConnectionManager.__init__(self, 'irc')
        self._protos['irc'] = Connection

if __name__ == '__main__':
    manager = ConnectionManager()

    try:
        reactor.run()
    except KeyboardInterrupt:
        manager.quit()

