import time
import threading
import socket
import handlers
import log
from httpserver import HTTPServer
from outchannel import BufferOverload

class Engine(object):
    def __init__(self, config, game):
        self.config = config
        self.game = game
        self.clients = {}
        self._quit = False

    def initialize(self):
        # XXX cross-references!
        handlers.CommandPipe.engine = handlers.ClientEventPage.engine = self
        self.server = server = HTTPServer(self.config, handlers.application)
        server.engine = self
        server.initialize()
        self.game.initialize(self)
        self.gamethread = gt = threading.Thread(target=self.gameloop)
        gt.start()

    def run(self, use_poll=True, timeout=None, count=None):
        if not self.server:
            raise SystemError('please initialize first')
        try:
            self.server.serve(use_poll=use_poll, timeout=timeout, count=count)
        except (KeyboardInterrupt, SystemExit):
            pass
        except:
            raise

    def close(self):
        self._quit = True
        while self.gamethread.isAlive():
            time.sleep(0.1)
        self.server.close()

    def add_client(self, id, client):
        log.log(log.DEBUG, 'adding client %s' % (id,))
        self.clients[int(id)] = client
        self.game.add_user(id)
            
    def del_client(self, id):
        log.log(log.DEBUG, 'deleting client %s' % (id,))
        self.game.del_user(id)
        client = self.clients.pop(int(id))
        client.closed = True

    def gameloop(self):
        frametime = 1.0 / self.config.framerate
        prevtime = time.time()
        while not self._quit:
            starttime = time.time()
            self.update_game(starttime - prevtime)
            prevtime = starttime
            timespent = time.time() - starttime
            if timespent < frametime:
                time.sleep(frametime - timespent)

    def update_game(self, timespent):
        framestates = dict([(id, client.inchannel.pop_state()) for
                             id, client in self.clients.iteritems()])
        self.game.calculate_state(framestates, timespent)
        self.game.handle_changes(framestates, timespent)
        clientscopy = self.clients.copy()
        initialized = []
        for id, client in clientscopy.iteritems():
            if not client.initialized:
                initialized.append(id)
                client.initialized = True
                for command in self.game.get_create_commands():
                    client.push_command(command)
        todelete = []
        for id, command in self.game.get_frame_commands():
            if id == -1:
                clients = clientscopy.iteritems()
            elif id == -2:
                clients = (t for t in clientscopy.iteritems() if
                           t[0] not in initialized)
            else:
                clients = [(id, clientscopy[id])]
            for id, client in clients:
                try:
                    client.push_command(command.clone())
                except BufferOverload:
                    print 'overload for client', client.id
                    # client are sent to that client itself... not a very
                    # charming way to fix it, but not sure of a better one
                    if not client.closed:
                        self.del_client(client.id)
        if self.server:
            self.server.poll_next()

    def __del__(self):
        del self.game.engine
        del self.game


