# 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/.

"""
This script implements the `mach devtools-css-db` command. It runs an xpcshell
script that uses InspectorUtils to query the CSS properties used by the browser.
This information is used to generate the properties-db.js file.
"""

import json
import logging
import os
import runpy
import sys
import string
import subprocess
from mozbuild import shellutil
from mozbuild.base import (
    MozbuildObject,
    BinaryNotFoundException,
)
from mach.decorators import (
    Command,
)


def resolve_path(start, relativePath):
    """Helper to resolve a path from a start, and a relative path"""
    return os.path.normpath(os.path.join(start, relativePath))


def stringify(obj):
    """Helper to stringify to JSON"""
    return json.dumps(obj, sort_keys=True, indent=2, separators=(",", ": "))


@Command(
    "devtools-css-db",
    category="post-build",
    description="Rebuild the devtool's static css properties database.",
)
def generate_css_db(command_context):
    """Generate the static css properties database for devtools and write it to file."""

    print("Re-generating the css properties database...")
    db = get_properties_db_from_xpcshell(command_context)
    if not db:
        return 1

    output_template(
        command_context,
        {
            "cssProperties": stringify(db["cssProperties"]),
            "pseudoElements": stringify(db["pseudoElements"]),
        },
    )


def get_properties_db_from_xpcshell(command_context):
    """Generate the static css properties db for devtools from an xpcshell script."""
    build = MozbuildObject.from_environment()

    # Get the paths
    script_path = resolve_path(
        command_context.topsrcdir,
        "devtools/shared/css/generated/generate-properties-db.js",
    )
    gre_path = resolve_path(command_context.topobjdir, "dist/bin")
    browser_path = resolve_path(command_context.topobjdir, "dist/bin/browser")
    try:
        xpcshell_path = build.get_binary_path(what="xpcshell")
    except BinaryNotFoundException as e:
        command_context.log(
            logging.ERROR, "devtools-css-db", {"error": str(e)}, "ERROR: {error}"
        )
        command_context.log(
            logging.INFO, "devtools-css-db", {"help": e.help()}, "{help}"
        )
        return None

    print(browser_path)

    sub_env = dict(os.environ)
    if sys.platform.startswith("linux"):
        sub_env["LD_LIBRARY_PATH"] = gre_path

    # Run the xcpshell script, and set the appdir flag to the browser path so that
    # we have the proper dependencies for requiring the loader.
    contents = subprocess.check_output(
        [xpcshell_path, "-g", gre_path, "-a", browser_path, script_path],
        env=sub_env,
    )
    # Extract just the output between the delimiters as the xpcshell output can
    # have extra output that we don't want.
    contents = contents.decode().split("DEVTOOLS_CSS_DB_DELIMITER")[1]

    return json.loads(contents)


def output_template(command_context, substitutions):
    """Output a the properties-db.js from a template."""
    js_template_path = resolve_path(
        command_context.topsrcdir,
        "devtools/shared/css/generated/properties-db.js.in",
    )
    destination_path = resolve_path(
        command_context.topsrcdir, "devtools/shared/css/generated/properties-db.js"
    )

    with open(js_template_path, "rb") as handle:
        js_template = handle.read().decode()

    preamble = "/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */\n\n"
    contents = string.Template(js_template).substitute(substitutions)

    with open(destination_path, "wb") as destination:
        destination.write(preamble.encode() + contents.encode())

    print("The database was successfully generated at " + destination_path)
