from io import BytesIO

from twisted.python import failure
from twisted.internet import reactor, protocol
from twisted.test import testutils


class FakeTransport(protocol.FileWrapper):
    disconnecting = False
    disconnect_done = False

    def __init__(self):
        self.data = BytesIO()
        protocol.FileWrapper.__init__(self, self.data)


class FasterIOPump(testutils.IOPump):
    def pump(self):
        """Move data back and forth.

        Returns whether any data was moved.
        """
        self.clientIO.seek(0)
        self.serverIO.seek(0)
        cData = self.clientIO.read()
        sData = self.serverIO.read()
        self.clientIO.seek(0)
        self.serverIO.seek(0)
        self.clientIO.truncate()
        self.serverIO.truncate()
        self.server.dataReceived(cData)
        self.client.dataReceived(sData)
        if cData or sData:
            return 1
        else:
            return 0


class IOPump(FasterIOPump):
    active = []

    def __init__(self,
                 client, server,
                 clientTransport, serverTransport):
        self.clientTransport = clientTransport
        self.serverTransport = serverTransport
        testutils.IOPump.__init__(self,
                                  client=client,
                                  server=server,
                                  clientIO=clientTransport.data,
                                  serverIO=serverTransport.data)
        self.active.append(self)


def returnConnected(server, client):
    """Take two Protocol instances and connect them.
    """
    clientTransport = FakeTransport()
    client.makeConnection(clientTransport)
    serverTransport = FakeTransport()
    server.makeConnection(serverTransport)
    pump = IOPump(client, server,
                  clientTransport,
                  serverTransport)
    # Challenge-response authentication:
    pump.flush()
    # Uh...
    pump.flush()
    return pump


def _getDeferredResult(d):
    resultSet = []
    d.addBoth(resultSet.append)
    while not resultSet:
        for pump in IOPump.active:
            pump.pump()
        reactor.iterate()
    return resultSet[0]


def pumpingDeferredResult(d):
    result = _getDeferredResult(d)
    return (
        result.raiseException()
        if isinstance(result, failure.Failure)
        else result
    )
