import sys
import optparse
import time
import errno
import socket

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,
                                   WSGIServer, 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()

IGNORED_ERRNO = (errno.EPIPE, errno.ECONNRESET, errno.EAGAIN)
def should_ignore(exc_info):
    exc_type, exc_value = exc_info[:2]
    if exc_type is socket.error and exc_value.args[0] in IGNORED_ERRNO:
        return True
    else:
        return False

class QuietServer(WSGIServer):
    def handle_error(self, request, client_address):
        if not should_ignore(sys.exc_info()):
            WSGIServer.handle_error(self, request, client_address)

class QuietServerHandler(ServerHandler):
    """
    Used to suppress tracebacks of broken pipes.
    """
    def log_exception(self, exc_info):
        if not should_ignore(exc_info):
            ServerHandler.log_exception(self, exc_info)

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),
                server_class=QuietServer, 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()

