"""
Various plugins for nose, that let us do our magic.
"""
import socket
import threading
import os

from django.core.handlers.wsgi import WSGIHandler
from django.core.servers import basehttp
from django.test.testcases import call_command, TestCase

import nose
from nose.plugins import Plugin

from cases import HttpTestCase

__all__ = ("LiveHttpServerRunnerPlugin",)

class StoppableWSGIServer(basehttp.WSGIServer):
    """WSGIServer with short timeout, so that server thread can stop this server."""

    def server_bind(self):
        """Sets timeout to 1 second."""
        basehttp.WSGIServer.server_bind(self)
        self.socket.settimeout(1)

    def get_request(self):
        """Checks for timeout when getting request."""
        try:
            sock, address = self.socket.accept()
            sock.settimeout(None)
            return (sock, address)
        except socket.timeout:
            raise

class TestServerThread(threading.Thread):
    """Thread for running a http server while tests are running."""

    def __init__(self, address, port):
        self.address = address
        self.port = port
        self._stopevent = threading.Event()
        self.started = threading.Event()
        self.error = None
        super(TestServerThread, self).__init__()

    def run(self):
        """Sets up test server and database and loops over handling http requests."""
        try:
            handler = basehttp.AdminMediaHandler(WSGIHandler())
            server_address = (self.address, self.port)
            httpd = StoppableWSGIServer(server_address, basehttp.WSGIRequestHandler)
            httpd.set_app(handler)
            self.started.set()
        except basehttp.WSGIServerException, e:
            self.error = e
            self.started.set()
            return

        # Must do database stuff in this new thread if database in memory.
#        from django.conf import settings
#        if settings.DATABASE_ENGINE == 'sqlite3' \
#            and (not settings.TEST_DATABASE_NAME or settings.TEST_DATABASE_NAME == ':memory:'):
#            from django.db import connection
#            db_name = connection.creation.create_test_db(0)
#            # Import the fixture data into the test database.
#            if hasattr(self, 'fixtures'):
#                # We have to use this slightly awkward syntax due to the fact
#                # that we're using *args and **kwargs together.
#                call_command('loaddata', *self.fixtures, **{'verbosity': 0})

        # Loop until we get a stop event.
        while not self._stopevent.isSet():
            httpd.handle_request()

    def join(self, timeout=None):
        """Stop the thread and wait for it to finish."""
        self._stopevent.set()
        threading.Thread.join(self, timeout)

class LiveHttpServerRunnerPlugin(Plugin):
    """
    Patch Django on fly and start live HTTP server, if TestCase is inherited
    from HttpTestCase or start_live_server attribute is set to True.
    
    Taken from Michael Rogers implementation from http://trac.getwindmill.com/browser/trunk/windmill/authoring/djangotest.py
    """
    enabled = True
    
    def __init__(self):
        super(self.__class__, self).__init__()
        self.server_started = False
        self.server_thread = None
    
    def options(self, parser, env=os.environ):
        pass

    def configure(self, options, conf):
        if not self.can_configure:
            return

    def startTest(self, test):
        if isinstance(test.test, nose.case.MethodTestCase):
            cls = test.test.test.im_class
        else:
            cls = test.test.__class__

        if not self.server_started and (issubclass(cls, HttpTestCase) or (hasattr(cls, "start_live_server") and cls.start_live_server)):
            self.start_server()
            self.server_started = True
        
        if self.server_started:
            # Must do database stuff in this new thread if database in memory.
            from django.conf import settings
            if settings.DATABASE_ENGINE == 'sqlite3' \
                and (not settings.TEST_DATABASE_NAME or settings.TEST_DATABASE_NAME == ':memory:'):
                from django.db import connection
                db_name = connection.creation.create_test_db(0)
                # Import the fixture data into the test database.
                if hasattr(self, 'fixtures'):
                    # We have to use this slightly awkward syntax due to the fact
                    # that we're using *args and **kwargs together.
                    call_command('loaddata', *self.fixtures, **{'verbosity': 0})
    
#    def finalize(self, test):
#        if self.server_started:
#            self.stop_test_server()
#            self.server_started = False
    
    def start_server(self, address='0.0.0.0', port=8000):
        self.server_thread = TestServerThread(address, port)
        self.server_thread.start()
        self.server_thread.started.wait()
        if self.server_thread.error:
            raise self.server_thread.error

    def stop_test_server(self):
        if self.server_thread:
            self.server_thread.join()


        