#!/usr/bin/env python

import os, sys, json
from common_paths import *
import spec_validator
import argparse


def expand_test_expansion_pattern(spec_test_expansion, test_expansion_schema):
    expansion = {}
    for artifact in spec_test_expansion:
        artifact_value = spec_test_expansion[artifact]
        if artifact_value == '*':
            expansion[artifact] = test_expansion_schema[artifact]
        elif isinstance(artifact_value, list):
            expansion[artifact] = artifact_value
        else:
            expansion[artifact] = [artifact_value]

    return expansion


def permute_expansion(expansion, selection = {}, artifact_index = 0):
    artifact_order = ['delivery_method', 'redirection', 'origin',
                      'source_protocol', 'target_protocol', 'subresource',
                      'referrer_url', 'name']

    if artifact_index >= len(artifact_order):
        yield selection
        return

    artifact_key = artifact_order[artifact_index]

    for artifact_value in expansion[artifact_key]:
        selection[artifact_key] = artifact_value
        for next_selection in permute_expansion(expansion,
                                                selection,
                                                artifact_index + 1):
            yield next_selection


def generate_selection(selection, spec, subresource_path,
                       test_html_template_basename):
    selection['spec_name'] = spec['name']
    selection['spec_title'] = spec['title']
    selection['spec_description'] = spec['description']
    selection['spec_specification_url'] = spec['specification_url']
    selection['subresource_path'] = subresource_path
    # Oddball: it can be None, so in JS it's null.
    selection['referrer_policy_json'] = json.dumps(spec['referrer_policy'])

    test_filename = test_file_path_pattern % selection
    test_directory = os.path.dirname(test_filename)
    full_path = os.path.join(spec_directory, test_directory)

    test_html_template = get_template(test_html_template_basename)
    test_js_template = get_template("test.js.template")
    disclaimer_template = get_template('disclaimer.template')
    test_description_template = get_template("test_description.template")

    html_template_filename = os.path.join(template_directory,
                                          test_html_template_basename)
    generated_disclaimer = disclaimer_template \
        % {'generating_script_filename': os.path.relpath(__file__,
                                                         test_root_directory),
           'html_template_filename': os.path.relpath(html_template_filename,
                                                     test_root_directory)}

    # Adjust the template for the test invoking JS. Indent it to look nice.
    selection['generated_disclaimer'] = generated_disclaimer.rstrip()
    test_description_template = \
        test_description_template.rstrip().replace("\n", "\n" + " " * 33)
    selection['test_description'] = test_description_template % selection

    # Adjust the template for the test invoking JS. Indent it to look nice.
    indent = "\n" + " " * 6;
    test_js_template = indent + test_js_template.replace("\n", indent);
    selection['test_js'] = test_js_template % selection

    # Directory for the test files.
    try:
        os.makedirs(full_path)
    except:
        pass

    selection['meta_delivery_method'] = ''

    if spec['referrer_policy'] != None:
        if selection['delivery_method'] == 'meta-referrer':
            selection['meta_delivery_method'] = \
                '<meta name="referrer" content="%(referrer_policy)s">' % spec
        elif selection['delivery_method'] == 'meta-csp':
            selection['meta_delivery_method'] = \
                '<meta http-equiv="Content-Security-Policy" ' + \
                'content="referrer %(referrer_policy)s">' % spec
        elif selection['delivery_method'] == 'http-csp':
            selection['meta_delivery_method'] = \
                "<!-- No meta: CSP delivered via HTTP headers. -->"
            test_headers_filename = test_filename + ".headers"
            with open(test_headers_filename, "w") as f:
                f.write('Content-Security-Policy: ' + \
                        'referrer %(referrer_policy)s\n' % spec)
                # TODO(kristijanburnik): Limit to WPT origins.
                f.write('Access-Control-Allow-Origin: *\n')
        elif selection['delivery_method'] == 'attr-referrer':
            # attr-referrer is supported by the JS test wrapper.
            pass
        elif selection['delivery_method'] == 'rel-noreferrer':
            # rel=noreferrer is supported by the JS test wrapper.
            pass
        else:
            raise ValueError('Not implemented delivery_method: ' \
                              + selection['delivery_method'])

    # Obey the lint and pretty format.
    if len(selection['meta_delivery_method']) > 0:
        selection['meta_delivery_method'] = "\n    " + \
                                            selection['meta_delivery_method']

    with open(test_filename, 'w') as f:
        f.write(test_html_template % selection)


def generate_test_source_files(spec_json, target):
    test_expansion_schema = spec_json['test_expansion_schema']
    specification = spec_json['specification']

    spec_json_js_template = get_template('spec_json.js.template')
    with open(generated_spec_json_filename, 'w') as f:
        f.write(spec_json_js_template
                % {'spec_json': json.dumps(spec_json)})

    # Choose a debug/release template depending on the target.
    html_template = "test.%s.html.template" % target

    # Create list of excluded tests.
    exclusion_dict = {}
    for excluded_pattern in spec_json['excluded_tests']:
        excluded_expansion = \
            expand_test_expansion_pattern(excluded_pattern,
                                          test_expansion_schema)
        for excluded_selection in permute_expansion(excluded_expansion):
            excluded_selection_path = selection_pattern % excluded_selection
            exclusion_dict[excluded_selection_path] = True

    for spec in specification:
        for spec_test_expansion in spec['test_expansion']:
            expansion = expand_test_expansion_pattern(spec_test_expansion,
                                                      test_expansion_schema)
            for selection in permute_expansion(expansion):
                selection_path = selection_pattern % selection
                if not selection_path in exclusion_dict:
                    subresource_path = \
                        spec_json["subresource_path"][selection["subresource"]]
                    generate_selection(selection,
                                       spec,
                                       subresource_path,
                                       html_template)
                else:
                    print 'Excluding selection:', selection_path


def main(target):
    spec_json = load_spec_json();
    spec_validator.assert_valid_spec_json(spec_json)
    generate_test_source_files(spec_json, target)


if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Test suite generator utility')
    parser.add_argument('-t', '--target', type = str,
        choices = ("release", "debug"), default = "release",
        help = 'Sets the appropriate template for generating tests')
    # TODO(kristijanburnik): Add option for the spec_json file.
    args = parser.parse_args()
    main(args.target)
