#!/usr/bin/env python3
from pathlib import Path
import sys

import yaml


def fix_name(name):
    if name == '[% c("var/compiler") %]':
        return "compiler"
    # node is a reserved word in dot
    return name.replace("-", "_").replace("node", "nodejs")


colors = {
    "browser": "indianred1",
    "firefox": '"#E66000"',
    "toolchain": "lightskyblue1",
    "tor": '"#7D4698"',
    "android": '"#3DDC84"',
    "signing": "yellow",
    "namecoin": '"#495f7e"',
}
# Ignore some projects because they make the graph unreadable.
ignore = set(["binutils", "container_image", "compiler", "go"])

# We skip firefox-l10n because SafeLoader will not parse it.
dependencies = {
    "compiler": set(
        [
            "gcc",
            "mingw_w64_clang",
            "macosx_toolchain",
            "android_toolchain",
        ]
    ),
    "firefox_l10n": set(),
}
proj_components = {
    "compiler": "toolchain",
    "firefox_l10n": "firefox",
}
for p in (Path(__file__).parent.parent / "projects").glob("*"):
    config_file = p / "config"
    project = fix_name(p.name)
    if not config_file.is_file():
        print(f"{project} doesn't have a config file", file=sys.stderr)
        continue
    with config_file.open() as f:
        try:
            config = yaml.load(f, yaml.SafeLoader)
        except yaml.composer.ComposerError as e:
            # Currently only firefox-l10n has this error, and it hasn't
            # dependencies. So, ignoring it works better in this case.
            print(f"Ignoring {project} because of {e}", file=sys.stderr)
            continue
    if "deps_graph" in config:
        proj_components[project] = config["deps_graph"]["component"]
    else:
        proj_components[project] = ""
    deps = set()

    def add_projects(obj):
        if "input_files" not in obj:
            return
        for i in obj["input_files"]:
            if "project" in i:
                proj = fix_name(i["project"])
                if proj == project:
                    # Different targets/steps might add loops
                    continue
                elif proj in ignore:
                    continue
                else:
                    deps.add(proj)

    add_projects(config)
    if "steps" in config:
        for step in config["steps"].values():
            add_projects(step)
    dependencies[project] = deps

components = {c: [] for c in set(proj_components.values())}
for project, component in proj_components.items():
    components[component].append(project)
outside = []
print("digraph {")
for project in components.pop("", []):
    print(f"  {project};")
    for dep in dependencies[project]:
        outside.append((project, dep))
for i, (component, projects) in enumerate(components.items()):
    color = colors.get(component, "lightgrey")
    print(f"  subgraph cluster_{i} {{")
    print("    style=filled;")
    print(f"    color={color};")
    print("    node [style=filled,color=white];")
    print(f'    label="{component}";')
    for project in projects:
        print(f"    {project};")
        for dep in sorted(dependencies[project]):
            if proj_components[dep] == proj_components[project]:
                print(f"    {project} -> {dep};")
            else:
                outside.append((project, dep))
    print("  }")
for project, dep in outside:
    print(f"  {project} -> {dep};")
print("}")
