#!/usr/bin/env python

# 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 generates nsStyleStructList.h, which contains macro invocations
# that can be used for three things:
#
# 1. To generate code for each inherited style struct.
# 2. To generate code for each reset style struct.
# 3. To generate the dependency of each style struct.

from __future__ import print_function

import math

NORMAL_DEP = ["Variables"]
COLOR_DEP = ["Color"]
LENGTH_DEP = ["Font", "Visibility"]

# List of style structs and their corresponding Check callback functions,
# if any.
STYLE_STRUCTS = [("INHERITED",) + x for x in [
    # Inherited style structs.
    ("Font",           "CheckFontCallback",     NORMAL_DEP + ["Visibility"]),
    ("Color",          "CheckColorCallback",    NORMAL_DEP),
    ("List",           "nullptr",               NORMAL_DEP + LENGTH_DEP),
    ("Text",           "CheckTextCallback",     NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Visibility",     "nullptr",               NORMAL_DEP),
    ("UserInterface",  "nullptr",               NORMAL_DEP),
    ("TableBorder",    "nullptr",               NORMAL_DEP + LENGTH_DEP),
    ("SVG",            "nullptr",               NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Variables",      "CheckVariablesCallback",[]),
]] + [("RESET",) + x for x in [
    # Reset style structs.
    ("Background",     "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Position",       "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("TextReset",      "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Display",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("Content",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("UIReset",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("Table",          "nullptr",   NORMAL_DEP),
    ("Margin",         "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("Padding",        "nullptr",   NORMAL_DEP + LENGTH_DEP),
    ("Border",         "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Outline",        "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("XUL",            "nullptr",   NORMAL_DEP),
    ("SVGReset",       "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Column",         "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
    ("Effects",        "nullptr",   NORMAL_DEP + LENGTH_DEP + COLOR_DEP),
]]


# ---- Generate nsStyleStructList.h ----

count = len(STYLE_STRUCTS)

# Check for problems with style struct dependencies
resolved_items = []
# This whole loop tries to sort the style structs in topological order
# according to the dependencies. A topological order exists iff there
# are no cyclic dependencies between the style structs. It resolves one
# struct each iteration, and append the resolved one to |resolved_items|.
for i in range(count):
    # This inner loop picks one style struct which does not have
    # unsolved dependencies. If nothing can be picked, then we
    # must have some cyclic dependencies.
    for j in range(count):
        _, name, _, dependencies = STYLE_STRUCTS[j]
        if name in resolved_items:
            continue
        # Check whether all dependencies of this item have been placed
        for dep in dependencies:
            if dep not in resolved_items:
                break
        else:
            resolved_items.append(name)
            break
    else:
        import sys
        print("ERROR: Cannot resolve style struct dependencies", file=sys.stderr)
        print("Resolved items:", " ".join(resolved_items), file=sys.stderr)
        unsolved_items = [name for _, name, _, _ in STYLE_STRUCTS
                          if name not in resolved_items]
        print("There exist cyclic dependencies between " +
                  "the following structs:", " ".join(unsolved_items), file=sys.stderr)
        exit(1)

def printEntry(header, i):
    if STYLE_STRUCTS[i][1] == "Variables":
        print("#ifndef STYLE_STRUCT_LIST_IGNORE_VARIABLES", file=header)
    print("STYLE_STRUCT_%s(%s, %s)" % STYLE_STRUCTS[i][:3], file=header)
    for dep in STYLE_STRUCTS[i][3]:
        print("STYLE_STRUCT_DEP(%s)" % (dep,), file=header)
    print("STYLE_STRUCT_END()", file=header)
    if STYLE_STRUCTS[i][1] == "Variables":
        print("#endif", file=header)

HEADER = """/* THIS FILE IS AUTOGENERATED BY generate-stylestructlist.py - DO NOT EDIT */

// IWYU pragma: private, include "nsStyleStructFwd.h"

/*
 * list of structs that contain the data provided by nsStyleContext, the
 * internal API for computed style data for an element
 */

/*
 * This file is intended to be used by different parts of the code, with
 * the STYLE_STRUCT macro (or the STYLE_STRUCT_INHERITED and
 * STYLE_STRUCT_RESET pair of macros) defined in different ways.
 */

#ifndef STYLE_STRUCT_INHERITED
#define STYLE_STRUCT_INHERITED(name, checkdata_cb) \\
  STYLE_STRUCT(name, checkdata_cb)
#define UNDEF_STYLE_STRUCT_INHERITED
#endif

#ifndef STYLE_STRUCT_RESET
#define STYLE_STRUCT_RESET(name, checkdata_cb) \\
  STYLE_STRUCT(name, checkdata_cb)
#define UNDEF_STYLE_STRUCT_RESET
#endif

#ifndef STYLE_STRUCT_DEP
#define STYLE_STRUCT_DEP(dep)
#define UNDEF_STYLE_STRUCT_DEP
#endif

#ifndef STYLE_STRUCT_END
#define STYLE_STRUCT_END()
#define UNDEF_STYLE_STRUCT_END
#endif

// The inherited structs are listed before the Reset structs.
// nsStyleStructID assumes this is the case, and callers other than
// nsStyleStructFwd.h that want the structs in id-order just define
// STYLE_STRUCT rather than including the file twice.

"""
FOOTER = """
#ifdef UNDEF_STYLE_STRUCT_INHERITED
#undef STYLE_STRUCT_INHERITED
#undef UNDEF_STYLE_STRUCT_INHERITED
#endif

#ifdef UNDEF_STYLE_STRUCT_RESET
#undef STYLE_STRUCT_RESET
#undef UNDEF_STYLE_STRUCT_RESET
#endif

#ifdef UNDEF_STYLE_STRUCT_DEP
#undef STYLE_STRUCT_DEP
#undef UNDEF_STYLE_STRUCT_DEP
#endif

#ifdef UNDEF_STYLE_STRUCT_END
#undef STYLE_STRUCT_END
#undef UNDEF_STYLE_STRUCT_END
#endif
"""

def main(header):
    print(HEADER, file=header)
    for i in range(count):
        printEntry(header, i)
    print(FOOTER, file=header)
