from main import DISPLAY, SOCKET_PATH
from main.remotecommands import *
from utils import xdr

import utils

import os
import sys
import socket
import threading
import atexit


class RemoteSocket(threading.Thread):

    """Class for the communication socket. """

    __PERIOD = 0.5


    def __init__(self):

        threading.Thread.__init__(self)

        self.__stopevent = threading.Event()

        # handlers for incoming messages
        self.__message_handlers = {}

        # create the socket
        utils.makedirs(SOCKET_PATH)
        self.__socket = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
        self.__socketpath = os.path.join(SOCKET_PATH, DISPLAY)
        try:
            self.__socket.bind(self.__socketpath)
        except socket.error:
            import traceback
            traceback.print_exc()
            print >> sys.stderr, "Couldn't bind to socket. Aborting."
            sys.exit(1)
        self.__socket.listen(1)
        self.__socket.setblocking(False)

        atexit.register(self.stop)


    def add_message_handler(self, message, handler):

        """ Adds a new message handler. """

        self.__message_handlers[message] = handler



    def stop(self):

        self.__stopevent.set()



    def run(self):

        """
        Thread for listening to the socket and calling message handlers.
        """

        class Handler(threading.Thread):


            def __init__(self, message_handlers, cnx, addr, stopevent):

                threading.Thread.__init__(self)

                self.__stopevent = stopevent
                self.__message_handlers = message_handlers
                self.__cnx  = cnx
                self.__addr = addr
                self.__cnx.setblocking(False)


            def run(self):

                while (not self.__stopevent.isSet()):

                    try:
                        data = xdr.recv(self.__cnx)
                    except socket.error:
                        # non-blocking (?)
                        pass

                    else:

                        if not data:
                            break

                        message = data[0]
                        args = data[1:]

                        try:
                            handler = self.__message_handlers[message]
                        except KeyError:
                            print >> sys.stderr, "Key %s doesn't exist in " \
                                                 "message_handlers!" % message

                        try:
                            response = utils.run_in_main_thread(handler, *args)
                            xdr.send(self.__cnx, *response)
                        except StandardError: # what are we expecting here ?
                            import traceback
                            traceback.print_exc()
                            xdr.send(self.__cnx, "")

                    self.__stopevent.wait(0.1)



        while (not self.__stopevent.isSet()):

            try:
                cnx, addr = self.__socket.accept()
            except socket.error:
                # non-blocking
                # check errno
                pass
            else:
                try:
                    # we share self.__stopevent so when self is stopped
                    # every handler is also closed
                    handler = Handler(self.__message_handlers, cnx, addr,
                                      self.__stopevent)
                    handler.start()
                except StandardError: # what are we expecting here ?
                    import traceback
                    traceback.print_exc()

            self.__stopevent.wait(RemoteSocket.__PERIOD)

        # we can't rely on __del__ for self
        # because we don't want to join self
        # so when self is stopped, we explicitly close the socket
        # so we can safely remove the socket file
        self.__socket.close()
        os.remove(self.__socketpath)
