import sys
import optparse
import time

from threading import Thread

import unittest
from unittest import TestCase, TestSuite, makeSuite
from nose.plugins import Plugin

from webob.dec import wsgify
from wsgiref.simple_server import make_server, WSGIRequestHandler, ServerHandler

from NaayaTestCase import NaayaTestCase

HTTP_PORT = 15380
SELENIUM_GRID_PORT = 5555
BROWSERS = ['*firefox',
    '*firefoxproxy',
    '*pifirefox',
    '*firefox3',
    '*firefox2',
    '*firefoxchrome',
    '*chrome',
    '*googlechrome',
    '*konqueror',
    '*safari',
    '*safariproxy',
    '*iexplore',
    '*iexploreproxy',
    '*piiexplore',
    '*opera']

@wsgify.middleware
def no_hop_by_hop(req, app):
    response = req.get_response(app)
    del response.headers['Connection']
    return response

def create_user(db, user_id, password, roles):
    import transaction
    app = db.open().root()['Application']
    app.acl_users._doAddUser(user_id, password, roles, [])
    transaction.commit()

class QuietServerHandler(ServerHandler):
    """
    Used to suppress tracebacks of broken pipes.
    """
    def log_exception(self, exc_info):
        import errno
        if exc_info[1].args[0] == errno.EPIPE:
            return # don't log broken pipes
        ServerHandler.log_exception(self, exc_info) # log other

class NaayaRequestHandler(WSGIRequestHandler):
    """
    Used to suppress WSGI logging interface and the tracebacks generated by
    broken pipes when browsers are closing the connection.
    """
    def log_message(self, format, *args):
        """
        Do nothing. This will ensure that all accesses will not be logged,
        cluttering the output.
        """
        pass

    def handle(self):
        """
        Handle a single HTTP request.

        Code is taken verbatim from the parent class, changing only the
        handler variable to our own handler
        """
        self.raw_requestline = self.rfile.readline()
        if not self.parse_request(): # An error code has been sent, just exit
            return

        # this is what we changed
        handler = QuietServerHandler(
                self.rfile, self.wfile, self.get_stderr(), self.get_environ())
        # end of changes
        handler.request_handler = self # backpointer for logging
        handler.run(self.server.get_app())

class NaayaHttpThread(Thread):
    def __init__(self, tzope):
        super(NaayaHttpThread, self).__init__()
        self.tzope = tzope
        self._stop = False

    def run(self):
        app = self.tzope.wsgi_app
        self.httpd = make_server('127.0.0.1', HTTP_PORT, no_hop_by_hop(app),
                handler_class=NaayaRequestHandler)
        self.httpd.socket.settimeout(1)

        while not self._stop:
            self.httpd.handle_request()
            # timeout is seen as a broken pipe, which was handled above by
            # QuietServerHandler

    def stop(self):
        self._stop = True

class SeleniumTestCase(NaayaTestCase):
    def login_user(self, user, password):
        self.selenium.open("/portal/login_html", True)
        self.selenium.wait_for_page_to_load("30000")
        self.selenium.type("__ac_name", user)
        self.selenium.type("__ac_password", password)
        self.selenium.click("submit")
        self.selenium.wait_for_page_to_load("30000")

    def logout_user(self):
        self.selenium.open("/portal/login_html", True)
        self.selenium.wait_for_page_to_load("30000")
        self.selenium.click("link=Logout")
        self.selenium.wait_for_page_to_load("30000")

class NaayaSeleniumTestPlugin(Plugin):
    """
    Nose plugin that prepares the environment for a SeleniumTestCase to run
    """

    def __init__(self, tzope):
        super(NaayaSeleniumTestPlugin, self).__init__()
        self.tzope = tzope
        self.naaya_started = False

    def options(self, parser, env):
        Plugin.options(self, parser, env)
        parser.add_option("--ny-instance-port", dest="HTTP_PORT",
                help="Change the port where the browser will look for a "
                        "Naaya instance. Default %s" % HTTP_PORT, type=int,
                        default=HTTP_PORT)
        parser.add_option("--selenium-grid-port", dest="SELENIUM_GRID_PORT",
                help="Change the selenium grid port. MUST be the same used by "
                        "selenium grid (default %s)" % SELENIUM_GRID_PORT,
                        type=int, default=SELENIUM_GRID_PORT)
        parser.add_option("--ny-selenium-browsers", dest="browsers",
                help="Select the browsers used in testing. Use one value from "
                "%s" % BROWSERS, default="*firefox")
        parser.add_option("--ny-selenium", dest="skip_tests",
                help=("Enable ny-selenium plugin for running selenium "
                      "test cases in nynose"),
                      action="store_false", default=True)

    def configure(self, options, config):
        global HTTP_PORT, SELENIUM_GRID_PORT
        Plugin.configure(self, options, config)

        HTTP_PORT = options.HTTP_PORT
        SELENIUM_GRID_PORT = options.SELENIUM_GRID_PORT
        self.browsers = options.browsers
        self.skip_tests = options.skip_tests

        self.enabled = True

    def skip_test_callable(self, test):
        """This replaces the run() method in TestCase `test`"""
        return

    def prepareTestCase(self, testCase):
        # if regular TestCase, abort selenium init
        if not isinstance(testCase.test, SeleniumTestCase):
            return
        else:
            # We have a SeleniumTestCase
            if self.skip_tests:
                return self.skip_test_callable
            else:
                if not self.naaya_started:
                    from selenium import selenium
                    self.selenium = selenium("localhost", SELENIUM_GRID_PORT,
                            self.browsers, "http://localhost:%s/" % HTTP_PORT)
                    self.selenium.start()
                    self.naaya_started = True
                    self.http_thread = NaayaHttpThread(self.tzope)
                    self.http_thread.start()
                testCase.test.selenium = self.selenium

    def afterTest(self, test):
        if (not isinstance(test.test, SeleniumTestCase) or
            self.skip_tests):
            return
        else:
            self.selenium.delete_all_visible_cookies()

    def finalize(self, result):
        if self.naaya_started:
            self.naaya_started = False
            self.http_thread.stop()
            self.http_thread.join()
            self.selenium.stop()

