# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import os
import subprocess
import tempfile

from mozprocess import ProcessHandler

from .base import Browser, require_arg, get_free_port, browser_command, ExecutorBrowser
from .servo import render_arg
from ..executors import executor_kwargs as base_executor_kwargs
from ..executors.executorservodriver import (ServoWebDriverTestharnessExecutor,
                                             ServoWebDriverRefTestExecutor)

here = os.path.join(os.path.split(__file__)[0])

__wptrunner__ = {"product": "servodriver",
                 "check_args": "check_args",
                 "browser": "ServoWebDriverBrowser",
                 "executor": {"testharness": "ServoWebDriverTestharnessExecutor",
                              "reftest": "ServoWebDriverRefTestExecutor"},
                 "browser_kwargs": "browser_kwargs",
                 "executor_kwargs": "executor_kwargs",
                 "env_options": "env_options"}

hosts_text = """127.0.0.1 web-platform.test
127.0.0.1 www.web-platform.test
127.0.0.1 www1.web-platform.test
127.0.0.1 www2.web-platform.test
127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
127.0.0.1 xn--lve-6lad.web-platform.test
"""


def check_args(**kwargs):
    require_arg(kwargs, "binary")


def browser_kwargs(**kwargs):
    return {"binary": kwargs["binary"],
            "debug_info": kwargs["debug_info"],
            "user_stylesheets": kwargs.get("user_stylesheets"),
            "render_backend": kwargs.get("servo_backend")}


def executor_kwargs(test_type, server_config, cache_manager, run_info_data, **kwargs):
    rv = base_executor_kwargs(test_type, server_config,
                              cache_manager, **kwargs)
    return rv


def env_options():
    return {"host": "127.0.0.1",
            "external_host": "web-platform.test",
            "bind_hostname": "true",
            "testharnessreport": "testharnessreport-servodriver.js",
            "supports_debugger": True}


def make_hosts_file():
    hosts_fd, hosts_path = tempfile.mkstemp()
    with os.fdopen(hosts_fd, "w") as f:
        f.write(hosts_text)
    return hosts_path


class ServoWebDriverBrowser(Browser):
    used_ports = set()

    def __init__(self, logger, binary, debug_info=None, webdriver_host="127.0.0.1",
                 user_stylesheets=None, render_backend="cpu"):
        Browser.__init__(self, logger)
        self.binary = binary
        self.webdriver_host = webdriver_host
        self.webdriver_port = None
        self.proc = None
        self.debug_info = debug_info
        self.hosts_path = make_hosts_file()
        self.command = None
        self.user_stylesheets = user_stylesheets if user_stylesheets else []
        self.render_backend = render_backend

    def start(self):
        self.webdriver_port = get_free_port(4444, exclude=self.used_ports)
        self.used_ports.add(self.webdriver_port)

        env = os.environ.copy()
        env["HOST_FILE"] = self.hosts_path
        env["RUST_BACKTRACE"] = "1"

        debug_args, command = browser_command(self.binary,
                                              [render_arg(self.render_backend), "--hard-fail",
                                               "--webdriver", str(self.webdriver_port),
                                               "about:blank"],
                                              self.debug_info)

        for stylesheet in self.user_stylesheets:
            command += ["--user-stylesheet", stylesheet]

        self.command = command

        self.command = debug_args + self.command

        if not self.debug_info or not self.debug_info.interactive:
            self.proc = ProcessHandler(self.command,
                                       processOutputLine=[self.on_output],
                                       env=env,
                                       storeOutput=False)
            self.proc.run()
        else:
            self.proc = subprocess.Popen(self.command, env=env)

        self.logger.debug("Servo Started")

    def stop(self):
        self.logger.debug("Stopping browser")
        if self.proc is not None:
            try:
                self.proc.kill()
            except OSError:
                # This can happen on Windows if the process is already dead
                pass

    def pid(self):
        if self.proc is None:
            return None

        try:
            return self.proc.pid
        except AttributeError:
            return None

    def on_output(self, line):
        """Write a line of output from the process to the log"""
        self.logger.process_output(self.pid(),
                                   line.decode("utf8", "replace"),
                                   command=" ".join(self.command))

    def is_alive(self):
        if self.runner:
            return self.runner.is_running()
        return False

    def cleanup(self):
        self.stop()

    def executor_browser(self):
        assert self.webdriver_port is not None
        return ExecutorBrowser, {"webdriver_host": self.webdriver_host,
                                 "webdriver_port": self.webdriver_port}
