import os
import sys
import struct
import socket
import tempfile
from os.path import join as opj

from types import FileType
from socket import SocketType

try:
    import cPickle as pickle
except ImportError:
    import pickle

try:
    import cStringIO as StringIO
except ImportError:
    import StringIO

sys.path.append("/home/exarkun/projects/python/Twisted/sandbox/exarkun/papers/2004/pycon/migration/sendmsg")
sys.path.append("/home/exarkun/projects/python/Twisted/sandbox/exarkun/papers/2004/pycon/migration/migrate")

from twisted.internet import reactor
from twisted.internet import protocol
from twisted.internet import abstract
from twisted.internet import tcp
from twisted.internet import unix
from twisted.persisted import styles
from twisted.cred import portal
from twisted.cred import checkers
from twisted.protocols import basic
from twisted.internet import unix
from twisted.application import internet

from sendmsg import sendmsg
from sendmsg import SCM_RIGHTS

class Server(unix.Server):
    def sendFileDescriptors(self, fileno, data="Filler"):
        """
        @param fileno: An iterable of the file descriptors to pass.
        """
        payload = struct.pack("%di" % len(fileno), *fileno)
        r = sendmsg(self.fileno(), data, 0, (socket.SOL_SOCKET, SCM_RIGHTS, payload))
        return r

class Port(unix.Port):
    transport = Server

class UNIXServer(internet._AbstractServer):
    def getHost(self):
        return self._port.getHost()

    def _getPort(self):
        from twisted.internet import reactor
        return reactor.listenWith(Port, *self.args, **self.kwargs)

class _FileDescriptorPickler:

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

    def __id(self, fileno):
        id = len(self.fdmap)
        self.fdmap[id] = fileno
        return id

    def persistent_id(self, obj):
        from twisted.internet import reactor
        if obj is reactor:
            return 'reactor'
        elif isinstance(obj, FileType):
            return '%d:file:%s' % (self.__id(obj.fileno()), obj.mode)
        elif isinstance(obj, SocketType):
            return '%d:socket' % (self.__id(obj.fileno()),)
        elif isinstance(obj, styles.Ephemeral):
            print 'Serializing ephemeral', obj
            return obj.__dict__.copy()
        else:
            return None

def FileDescriptorPickler(s, fdmap):
    ph = _FileDescriptorPickler(fdmap)
    p = pickle.Pickler(s)
    p.persistent_id = ph.persistent_id
    return p

class FileDescriptorSendingProtocol(basic.LineReceiver):
    """
    Must be used with L{Port} as the transport.
    """

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

    def lineReceived(self, line):
        try:
            files = self.idmap[int(line)]
        except ValueError:
            log.msg("Peer sent us a bogus ID")
        except KeyError:
            log.msg("Peer sent us an unassigned ID")
        except:
            log.err()
        else:
            files = files.items()
            files.sort()
            self.transport.sendFileDescriptors([f[1] for f in files])
            return

        self.transport.loseConnection()

class FileDescriptorSendingFactory(protocol.ServerFactory):
    protocol = FileDescriptorSendingProtocol

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

    def buildProtocol(self, addr):
        p = self.protocol(self.idmap)
        return p

from pbnew import pb
class UserStateSender(pb.Avatar):
    id = 0

    def __init__(self, client):
        self.idmap = {}

        # PB Object for talking to our peer
        self.client = client

        self.dir = tempfile.mkdtemp()
        self.path = tempfile.mktemp(dir=self.dir)
        self.factory = FileDescriptorSendingFactory(self.idmap)
        self.server = UNIXServer(opj(self.dir, self.path), self.factory)
        self.server.startService()

        reactor.callLater(1, sendSomeState, self)

    def logout(self):
        self.server.stopService()
        # This is BUNK with a capital BUNK
        reactor.callLater(0, os.rmdir, self.dir)

    def sendUserState(self, state):
        """Transmit the given state to this service's peer.
        """
        d = {}
        s = StringIO.StringIO()
        p = FileDescriptorPickler(s, d)
        p.dump(state)
        state = s.getvalue()
        self.id += 1
        self.idmap[self.id] = d
        return self._sendUserStateWithID(self.id, state, opj(self.dir, self.path))

    def _sendUserStateWithID(self, id, state, path):
        return self.client.callRemote("takeResponsibility", id, state, path)

class Realm:
    __implements__ = (portal.IRealm,)

    def requestAvatar(self, avatarId, mind, *interfaces):
        print 'saaaaaaaaw'
        if pb.IPerspective not in interfaces:
            raise NotImplementedError
        a = UserStateSender(mind)
        return pb.IPerspective, a, a.logout

def sendSomeState(avatar):
    from twisted.web import server
    from twisted.web.woven import dirlist
    from twisted.internet import reactor
    port = reactor.listenTCP(19191, server.Site(dirlist.DirectoryLister(".")))
    avatar.sendUserState(port)

from pbnew import PBServerFactory, Root

def main():
    r = Root()
    f = PBServerFactory(r)

    from twisted.application import internet
    return internet.TCPServer(10301, f)

from twisted.application import service
application = service.Application("Copyover Server")
main().setServiceParent(application)
