"""
$URL: svn+ssh://svn.mems-exchange.org/repos/trunk/dulcinea/lib/ui/page_loader.py $
$Id: page_loader.py 31181 2008-09-25 20:26:26Z rmasse $
"""
from dulcinea.common import set_test_environment
from durus.utils import BytesIO, as_bytes
from pprint import pformat
from qp.pub.common import get_request, get_session, get_response, get_publisher
from qp.pub.publish import RespondNow
from sancho.utest import UTest
from subprocess import Popen, PIPE

class PageLoader (object):

    def set_test_environment(self, **kwargs):
        return set_test_environment(**kwargs)

    def __init__(self, user=None, site_name=None):
        self.set_test_environment(site_name=site_name)
        self.site_name = site_name
        session = get_session()
        for key, value in get_publisher().get_sessions().items():
            if value is session:
                self.session_key = key
                break
        else:
            assert 0, "no session!"
        if user is None:
            self.user = self.get_admin_user()
        else:
            self.user = user
        self.cookies = get_request().get_environ('HTTP_COOKIE')
        session.set_authenticated(self.user)
        self.session = session

    def process(self,
                PATH_INFO='/',
                input_string = '',
                SCRIPT_NAME='',
                SERVER_NAME='test@example.org',
                HTTPS='on',
                **kwargs):
        env = dict(PATH_INFO=PATH_INFO,
                   SCRIPT_NAME=SCRIPT_NAME,
                   SERVER_NAME=SERVER_NAME,
                   HTTPS=HTTPS,
                   **kwargs)
        get_publisher().process(BytesIO(as_bytes(input_string)), env)
        request = get_request()
        return request

    def get_admin_user(self):
        for user in get_publisher().get_users().itervalues():
            if user.is_admin():
                return user
        return None

    def get_paths(self, user):
        def get_paths_inner(directory, base_path):
            get_request().environ['PATH_INFO'] = base_path + '/'
            get_request().environ['SERVER_NAME'] = "example.org"
            get_request().scheme = 'https'
            self.session.set_authenticated(user)
            get_request().session = self.session
            exports = []
            try:
                for export in directory.get_exports():
                    exports.append(export[0])
            except RespondNow:
                pass
            exports = sorted(exports)
            for name in exports:
                objname = directory._q_translate(name)
                if objname is None:
                    obj = directory._q_lookup(name)
                else:
                    obj = getattr(directory, objname)
                if hasattr(obj, '_q_traverse'):
                    for path in get_paths_inner(obj, base_path + '/' + name):
                        yield path
                else:
                    yield base_path +'/' + name
        set_test_environment(site_name=self.site_name)
        self.session.set_authenticated(user)
        get_request().session = self.session
        return [path for path in get_paths_inner(
            get_publisher().get_root_directory(), '')]

    def get_processed_request(self, path, follow_redirects=(301,302), **kwargs):
        """
        Returns a request after processing, after one redirect if necessary.
        """
        if 'HTTP_COOKIE' not in kwargs:
            kwargs['HTTP_COOKIE'] = self.cookies
        self.session.set_authenticated(self.user)
        get_publisher().get_sessions()[self.session_key] = self.session
        request = self.process(path, **kwargs)
        n = 4
        while n > 0 and get_response().get_status_code() in follow_redirects:
            n -= 1
            location = get_response().get_header('location')
            path = '/' + location.split('/', 3)[-1] # strip http://server
            if '?' in path:
                path, query = path.split('?')
                kwargs['QUERY_STRING'] = query
            self.session.set_authenticated(self.user)
            get_publisher().get_sessions()[self.session_key] = self.session
            request = self.process(path, **kwargs)
        return request

    def is_reportable(self, line):
        if 'Warning: ' in line:
            warning = line.split('Warning: ', 1)[1].strip()
            if warning in (
                '<table> lacks "summary" attribute',
                '<a> attribute "href" lacks value',
                '<form> attribute "action" lacks value',
                '<input> attribute "name" lacks value',
                'trimming empty <option>',
                'trimming empty <span>',
                '<textarea> proprietary attribute "wrap"',
                '<a> cannot copy name attribute to id', # starts with digit
                ):
                return False
            if warning.startswith("trimming empty <"):
                return False
            if (warning.endswith('" already defined') and
                warning.startswith('<a> anchor "')):
                return False
        return True

    def expect(self, path, status=200, **kwargs):
        request = self.get_processed_request(path, **kwargs)
        try:
            assert get_response().get_status_code() == status, (
                "%r (expected %r) %s" % (get_response().get_status_code(),
                                         status,
                                         path))
        except:
            #import sys
            #get_response().write(sys.stdout)
            print("\n%s %s" % (get_response().get_status_code(), path))
            raise
        return (request, get_response())

    def validate_html(self, request, response, assert_valid=True):
        if response.get_mime_type() == 'text/html':
            s = BytesIO()
            for chunk in response.generate_body_chunks():
                s.write(chunk)
            body = s.get_value().decode('utf-8')
            subprocess = Popen("/usr/bin/env tidy -q -e 2>&1", stdin=PIPE, stdout=PIPE)
            ins = subprocess.stdout
            outs = subprocess.stdin
            outs.write(body)
            outs.close()
            body_lines = body.splitlines()
            errors = []
            for line in ins.readlines():
                if self.is_reportable(line):
                    splitline = line.split()
                    lineno = int(splitline[1])
                    colno = int(splitline[3])
                    if body_lines:
                        bodyline = body_lines[lineno-1]
                    else:
                        bodyline = ''
                    errors.append(
                        '%s    ->%s: %r\n\n' % (
                        line,
                        lineno,
                        bodyline[max(colno-20, 0):colno+100]))
            if errors:
                print("\nLoading %r:\n%s\n\n" % (
                    request.get_path(), ''.join(errors)))
                if assert_valid:
                    assert not errors, 'html errors found'
        return request

    def crawl(self, user, omit_status=(200,)):
        """
        Collect information to use in a more general test.
        """
        expect = {}
        for path in self.get_paths(user):
            request = self.get_processed_request(path)
            if request.response.get_status_code() not in omit_status:
                expect[path] = request.response.get_status_code()
        assert 0, 'expect=\n%s' % pformat(expect)


class PageTest(UTest):

    def __init__(self, site_name=None):
        loader = PageLoader(site_name=site_name)
        self.__class__._expect = loader.expect
        self.__class__._get_admin_user = loader.get_admin_user
        self.__class__._get_paths = loader.get_paths
        self.__class__._validate_html = loader.validate_html
        UTest.__init__(self)



