#!/usr/bin/env python3

import json
import copy

from pathlib import Path


class ConfigUnit:
    def __init__(self, name: str, options: dict[str, str]):
        self.name = name
        self.cflags = options["cflags"]
        self.extra_cflags = options["extra_cflags"]
        self.mw_version = options["mw_version"]

    def get_all_cflags(self):
        return " ".join(self.cflags) + " " + " ".join(self.extra_cflags)


class ConfigVersion:
    def __init__(self, name: str, options: dict[str, str], units: list[ConfigUnit], root_path: Path):
        self.name = name
        self.objdiff_path = Path(options["objdiff"]).resolve()

        objdiff_json = json.loads(self.objdiff_path.read_text())
        self.objdiff_json: dict = copy.copy(objdiff_json)

        # remove deprecated options
        self.objdiff_json.pop("target_dir")
        self.objdiff_json.pop("base_dir")
        self.objdiff_json.pop("build_base")

        # fix watch patterns, remove yaml, json and all txt files
        # except symbols, delinks and relocs
        self.objdiff_json["watch_patterns"].remove("*.yml")
        self.objdiff_json["watch_patterns"].remove("*.yaml")
        self.objdiff_json["watch_patterns"].remove("*.json")
        self.objdiff_json["watch_patterns"].remove("*.txt")
        self.objdiff_json["watch_patterns"].append("*symbols.txt")
        self.objdiff_json["watch_patterns"].append("*delinks.txt")
        self.objdiff_json["watch_patterns"].append("*relocs.txt")
        self.objdiff_json["watch_patterns"].append("libs/cpp/include/*")

        for i, unit_dict in enumerate(objdiff_json["units"]):
            if "name" in unit_dict:
                unit_dict["name"] = f"{self.name}/{unit_dict['name']}"

            def get_cleaned_path(base_path: Path):
                parts = list(base_path.parts)
                for part in base_path.parts:
                    if "build" in part:
                        break
                    parts.remove(part)
                return Path("/".join(parts))

            if "target_path" in unit_dict:
                target_path = root_path / get_cleaned_path(Path(unit_dict["target_path"]))
                self.objdiff_json["units"][i]["target_path"] = str(target_path.resolve())

            if "base_path" in unit_dict:
                base_path = root_path / get_cleaned_path(Path(unit_dict["base_path"]))
                self.objdiff_json["units"][i]["base_path"] = str(base_path)

            if "scratch" in unit_dict:
                ctx_path = get_cleaned_path(Path(unit_dict["scratch"]["ctx_path"]))
                self.objdiff_json["units"][i]["scratch"]["ctx_path"] = str(ctx_path)

                entry = self.get_entry_from_name(units, self.objdiff_json["units"][i]["name"])
                if entry is not None:
                    self.objdiff_json["units"][i]["scratch"]["c_flags"] = f"{entry.get_all_cflags()} -DVERSION={self.name.upper()}"
                    self.objdiff_json["units"][i]["scratch"]["compiler"] = entry.mw_version

    def get_entry_from_name(self, units: list[ConfigUnit], base_name: str):
        split = base_name.split("/")
        split.pop(0)

        name = Path(base_name).stem
        for entry in units:
            if name in entry.name:
                return entry

        return None

class ObjdiffConfig:
    def __init__(self, root_path: Path):
        self.root_path = root_path
        self.versions: list[ConfigVersion] = []

    def get_json(self):
        assert len(self.versions) > 0, "no versions?"
        out_json = copy.copy(self.versions[0].objdiff_json)

        # merge units
        for i, entry in enumerate(self.versions):
            if i > 0:
                out_json["units"].extend(entry.objdiff_json["units"])

        return out_json

    @staticmethod
    def new(path: str):
        json_path = Path(path).resolve()
        cfg_json = json.loads(json_path.read_text())
        cfg = ObjdiffConfig(Path(cfg_json["root_path"]))

        units: list[ConfigUnit] = []
        for name, options in cfg_json["units"].items():
            units.append(ConfigUnit(name, options))

        for name, options in cfg_json["versions"].items():
            cfg.versions.append(ConfigVersion(name, options, units, cfg.root_path))

        return cfg


def main():
    cfg = ObjdiffConfig.new("build/objdiff_cfg.json")

    out_json = cfg.get_json()
    out_path = Path("objdiff.json").resolve()
    with out_path.open("w") as f:
        json.dump(out_json, f, indent=2)


if __name__ == "__main__":
    main()
