# Copyright (c) 2000 Phil Thompson <phil@river-bank.demon.co.uk>


import sys
import os
import socket
from qt import SIGNAL, PYSIGNAL, QServerSocket, QSocket

from DebugProtocol import *


class DebugServer(QServerSocket):
    """DebugServer()

    A class that implements the server side of a debugger/client connection.

    """
    def __init__(self):
        QServerSocket.__init__(self,0)

        self.qsock = None
        self.progLoaded = 0
        self.queue = []

        self.startRemote()

    def startRemote(self):
        """
        Private method to start a remote interpreter.
        """
        os.spawnv(os.P_NOWAIT,sys.executable,
                                [sys.executable,
                                 os.path.join(sys.path[0],'DebugClient.py'),
                                 `self.port()`])

    def newConnection(self,sockfd):
        """
        Re-implemented to handle a new connection.
        """
        sock = QSocket()
        sock.setSocket(sockfd)

        # If we already have a connection, refuse this one.  It will be closed
        # automatically.
        if self.qsock is not None:
            return

        self.connect(sock,SIGNAL('readyRead()'),self.handleLine)
        self.connect(sock,SIGNAL('connectionClosed()'),self.startClient)

        self.qsock = sock

        # Send commands that were waiting for the connection.
        for cmd in self.queue:
            self.qsock.writeBlock(cmd)

        self.queue = []

    def remoteLoad(self,finfo,argv):
        """
        Public method to load a new program to debug.
        """
        # Restart the client if there is already a program loaded.
        if self.progLoaded:
            self.startClient(0)
 
        self.sendCommand('%s%s %s\n' % (RequestLoad, str(finfo.absFilePath()),str(argv)))
        self.progLoaded = 1

    def remoteStatement(self,stmt):
        """
        Public method to execute a Python statement.  The statement should not
        have a trailing newline.
        """
        self.sendCommand('%s\n%s\n' % (stmt,RequestOK))

    def remoteStep(self):
        """
        Public method to single step the debugged program.
        """
        self.sendCommand(RequestStep + '\n')

    def remoteContinue(self):
        """
        Public method to continue the debugged program.
        """
        self.sendCommand(RequestContinue + '\n')

    def remoteBreakpoint(self,fn,line,set):
        """
        Public method to set or clear a breakpoint.
        """
        self.sendCommand('%s%s,%d,%d\n' % (RequestBreak,fn,line,set))

    def handleLine(self):
        """
        Private method to handle a data from the client.
        """
        if not self.qsock.canReadLine():
            return

        line = str(self.qsock.readLine())

        eoc = line.find('<') + 1

        if line[0] == '>' and eoc > 0:
            # Emit the signal corresponding to the response.
            resp = line[:eoc]

            if resp == ResponseOK:
                self.emit(PYSIGNAL('clientStatement'),(0,))
                return

            if resp == ResponseContinue:
                self.emit(PYSIGNAL('clientStatement'),(1,))
                return

            if resp == ResponseException:
                self.emit(PYSIGNAL('clientException'),())
                return

            if resp == ResponseLine:
                fn, line = line[eoc:-1].split(',')
                self.emit(PYSIGNAL('clientLine'),(fn,int(line)))
                return

            if resp == ResponseExit:
                self.emit(PYSIGNAL('clientExit'),(line[eoc:-1],))
                return

        self.emit(PYSIGNAL('clientOutput'),(line,))

    def startClient(self,unplanned=1):
        """
        Private method to handle the loss of the client.
        """
        if self.qsock is not None:
            self.qsock = None
            self.emit(PYSIGNAL('clientGone'),(unplanned,))

        self.startRemote()

    def sendCommand(self,cmd):
        """
        Private method to send a single line command to the client.
        """
        if self.qsock is not None:
            self.qsock.writeBlock(cmd)
        else:
            self.queue.append(cmd)
