# Copyright (C) 2018 Igalia S.L.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1.  Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
# 2.  Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer in the
#     documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import logging
import optparse
import os
import sys

from webkitpy.common.host import Host
from webkitpy.common.system.logutils import configure_logging
from webkitpy.common.webkit_finder import WebKitFinder
from webkitpy.w3c.common import WPTPaths
from webkitpy.w3c.test_downloader import TestDownloader
from webkitpy.webdriver_tests.webdriver_driver import create_driver

_log = logging.getLogger(__name__)


def main(script_name, argv):
    options, args = parse_args(argv)

    configure_logging(logging.DEBUG if options.verbose else logging.INFO)

    # Determine Port for the specified platform.
    host = Host()
    try:
        port = host.port_factory.get(options.platform, options)
    except NotImplementedError as e:
        _log.error(str(e))
        sys.exit(-1)

    runner = WPTRunner(port, port.host, script_name, options)
    # If necessary, inject the jhbuild wrapper or re-run in flatpak sandbox.
    if port.name() in ['gtk', 'wpe']:
        filesystem = host.filesystem

        top_level_directory = filesystem.normpath(filesystem.join(filesystem.dirname(__file__), '..', '..', '..', '..'))
        sys.path.insert(0, filesystem.join(top_level_directory, 'Tools', 'flatpak'))
        import flatpakutils
        if not flatpakutils.is_sandboxed() and flatpakutils.check_flatpak(verbose=False):
            flatpak_runner = flatpakutils.WebkitFlatpak.load_from_args(sys.argv)
            if flatpak_runner.clean_args() and flatpak_runner.has_environment():
                if not runner.prepare_wpt_checkout():
                    sys.exit(1)

                hostfilename = os.path.join(flatpak_runner.build_path, "wpt_etc_hosts")
                with open(hostfilename, "w") as stdout:
                    flatpak_runner.run_in_sandbox(os.path.join(options.wpt_checkout, "wpt"), "make-hosts-file", stdout=stdout)

                sys.exit(flatpak_runner.run_in_sandbox(*sys.argv,
                    extra_flatpak_args=['--bind-mount=/etc/hosts=%s' % hostfilename]))

        sys.path.insert(0, filesystem.join(top_level_directory, 'Tools', 'jhbuild'))
        import jhbuildutils

        if not flatpakutils.is_sandboxed() and not jhbuildutils.enter_jhbuild_environment_if_available(port.name()):
            _log.warning('jhbuild environment not present. Run update-webkitgtk-libs before build-webkit to ensure proper testing.')

    # Create the Port-specific driver.
    display_driver = port.create_driver(worker_number=0, no_timeout=True)._make_driver(pixel_tests=False)
    if not display_driver.check_driver(port):
        raise RuntimeError("Failed to check driver %s" % display_driver.__class__.__name__)
    os.environ.update(display_driver._setup_environ_for_test())

    if not options.child_processes:
        options.child_processes = os.environ.get("WEBKIT_TEST_CHILD_PROCESSES",
                                                 str(port.default_child_processes()))

    if not runner.run(args):
        sys.exit(1)


def parse_args(args):
    option_parser = optparse.OptionParser(usage='usage: %prog [options] [test...]')
    option_parser.add_option('--verbose', action='store_true',
                             help='Show debug message')
    option_parser.add_option('--platform', action='store',
                             help='Platform to use (e.g., "gtk")')
    option_parser.add_option('--gtk', action='store_const', dest='platform', const='gtk',
                             help='Alias for --platform=gtk')
    option_parser.add_option('--wpe', action='store_const', dest='platform', const='wpe',
                             help='Alias for --platform=wpe')
    option_parser.add_option('--child-processes',
                             help='Number of tests to run in parallel'),
    option_parser.add_option('--wpt-checkout', default=None,
                             help='web-platform-tests repository location')
    option_parser.add_option('--wpt-metadata', default=None,
                             help='web-platform-tests metadata location')
    option_parser.add_option('--wpt-manifest', default=None,
                             help='web-platform-tests manifest location')
    option_parser.add_option('--wpt-include-manifest', default=None,
                             help='web-platform-tests include manifest location')
    option_parser.add_option('--display-server', choices=['headless', 'xvfb', 'xorg', 'weston', 'wayland'], default='xvfb',
                             help='"headless": Use headless mode. "xvfb": Use a virtualized X11 server. '
                                  '"xorg": Use the current X11 session. "weston": Use a virtualized Weston server. '
                                  '"wayland": Use the current wayland session.')

    return option_parser.parse_args(args)


def create_webdriver(port):
    return create_driver(port)


def spawn_wpt(script_name, wpt_checkout, wpt_args):
    # Import the WPT python module.
    sys.path.insert(0, wpt_checkout)
    try:
        from tools.wpt import wpt
    except ImportError:
        _log.error("Failed to import wpt Python modules from the web-platform-tests directory")
        sys.exit(-1)

    wpt.main(script_name, wpt_args)


class WPTRunner(object):
    def __init__(self, port, host, script_name, options, downloader_class=TestDownloader,
        create_webdriver_func=create_webdriver, spawn_wpt_func=spawn_wpt):
        self._port = port
        self._host = host
        self._finder = WebKitFinder(self._host.filesystem)

        self._script_name = script_name
        self._options = options

        self._downloader_class = downloader_class
        self._create_webdriver_func = create_webdriver_func
        self._spawn_wpt_func = spawn_wpt_func

    def prepare_wpt_checkout(self):
        if not self._options.wpt_checkout:
            test_downloader = self._downloader_class(WPTPaths.checkout_directory(self._finder),
                self._host, self._downloader_class.default_options())
            test_downloader.clone_tests()
            self._options.wpt_checkout = WPTPaths.wpt_checkout_path(self._finder)

        if not self._options.wpt_checkout or not self._host.filesystem.exists(self._options.wpt_checkout):
            _log.error("Valid web-platform-tests directory required")
            return False
        return True

    def run(self, args):
        if not self.prepare_wpt_checkout():
            return False

        # Construct the WebDriver instance and run WPT tests via the 'webkit' product.
        webdriver = self._create_webdriver_func(self._port)

        wpt_args = ["run", "--webkit-port={}".format(self._port.name()),
            "--processes={}".format(self._options.child_processes)]
        if self._options.wpt_metadata:
            wpt_args += ["--metadata={}".format(self._options.wpt_metadata)]
        if self._options.wpt_manifest:
            wpt_args += ["--manifest={}".format(self._options.wpt_manifest)]
        if self._options.wpt_include_manifest:
            wpt_args += ["--include-manifest={}".format(self._options.wpt_include_manifest)]
        wpt_args += ["--webdriver-binary={}".format(webdriver.binary_path()),
            "--binary={}".format(webdriver.browser_path())]
        wpt_args += ["--binary-arg={}".format(arg) for arg in webdriver.browser_args()]
        wpt_args += ["webkit"] + args

        self._spawn_wpt_func(self._script_name, self._options.wpt_checkout, wpt_args)
        return True
