import sys, os
import threading
import base64
from SocketServer import TCPServer

from IsolatedDebugger import DebugServer, DebuggerConnection
from Tasks import ThreadedTaskHandler


try:
    from ExternalLib.xmlrpcserver import RequestHandler
except:
    # Add parent directory to the path search.
    sys.path[0:0] = [os.pardir]
    from ExternalLib.xmlrpcserver import RequestHandler


debug_server = None
connection = None
auth_str = ''
task_handler = ThreadedTaskHandler()


class DebugRequestHandler (RequestHandler):

    def _authenticate(self):
        h = self.headers
        if auth_str:
            s = h.get('authentication')
            if not s or s.split()[-1] != auth_str:
                raise 'Unauthorized', (
                    'Authentication header missing or incorrect')

    def call(self, method, params):
        # Override of xmlrpcserver.RequestHandler.call()
        self._authenticate()
        m = getattr(connection, method)
        result = apply(m, params)
        if result is None:
            result = 0
        return result

    def log_message(self, format, *args):
        pass


class TaskingMixIn:
    """Mix-in class to handle each request in a task thread."""

    def process_request(self, request, client_address):
        """Start a task to process the request."""
        task_handler.addTask(self.finish_request,
                             args=(request, client_address))

class TaskingTCPServer(TaskingMixIn, TCPServer):
    allow_reuse_address = 1


def start(username, password, host='127.0.0.1', port=26200,
          server_type='zope'):
    global auth_str, debug_server, connection

    if debug_server is not None:
        raise RuntimeError, 'The debug server is already running'

    # Create the debug server.
    if server_type == 'zope':
        from ZopeScriptDebugServer import ZopeScriptDebugServer
        ds = ZopeScriptDebugServer()
    elif server_type == 'basic':
        ds = DebugServer()
    else:
        raise ValueError, 'Unknown debug server type: %s' % server_type

    connection = DebuggerConnection(ds)
    connection.allowEnvChanges()  # Allow changing of sys.path, etc.

    # Create an authentication string.
    auth_str = base64.encodestring('%s:%s' % (username, password)).strip()

    debug_server = ds
    server = TaskingTCPServer((host, port), DebugRequestHandler)
    port = int(server.socket.getsockname()[1])

    # Provide a hard breakpoint hook.  Use it like this:
    # if hasattr(sys, 'breakpoint'): sys.breakpoint()
    sys.breakpoint = debug_server.set_trace
    sys.debugger_control = debug_server
    sys.boa_debugger = debug_server

    def serve_forever(server):
        while 1:
            server.handle_request()

    def startDaemon(target, args=()):
        t = threading.Thread(target=target, args=args)
        t.setDaemon(1)
        t.start()

    startDaemon(serve_forever, (server,))
    #startDaemon(debug_server.servicerThread)

    print >> sys.stderr, "Debug server listening on %s:%s" % tuple(
        server.socket.getsockname()[:2])

    try:
        import atexit
    except ImportError:
        pass
    else:
        atexit.register(server.socket.close)

def stop():
    global debug_server, connection
    debug_server = None
    connection = None
