"""Generates Script/SmartCityData.as directly from the real simulation output
(sim/output/light_schedule.json). This is the same "Python is the only place
with simulation logic" rule as the Three.js viewer — the .as file holds
nothing but the exact logged events, baked in at generation time. No JSON
parsing happens at runtime in Unreal; re-run this after re-running run_all.py
and the AngelScript director picks up the new numbers on its next hot-reload.
"""

import json
from pathlib import Path

OUT_DIR = Path(__file__).parent / "output"
# Public showcase version: point this at the target project's Script folder.
UE_SCRIPT_DIR = Path("Script")
CHUNK_SIZE = 200


def esc(s):
    return str(s).replace('"', '\\"')


def emit_chunked(f, events, struct_name, field_order, populate_name):
    total = len(events)
    n_chunks = max(1, (total + CHUNK_SIZE - 1) // CHUNK_SIZE)

    for c in range(n_chunks):
        chunk = events[c * CHUNK_SIZE:(c + 1) * CHUNK_SIZE]
        f.write(f"    void {populate_name}_{c}(array<{struct_name}>& Out)\n    {{\n")
        for e in chunk:
            f.write(f"        {struct_name} Ev;\n")
            for field, key in field_order:
                val = e[key]
                if isinstance(val, str):
                    f.write(f'        Ev.{field} = "{esc(val)}";\n')
                else:
                    f.write(f"        Ev.{field} = {float(val):.6f};\n")
            f.write("        Out.InsertLast(Ev);\n")
        f.write("    }\n\n")

    f.write(f"    void {populate_name}(array<{struct_name}>& Out)\n    {{\n")
    for c in range(n_chunks):
        f.write(f"        {populate_name}_{c}(Out);\n")
    f.write("    }\n\n")


def main():
    data = json.load(open(OUT_DIR / "light_schedule.json"))
    UE_SCRIPT_DIR.mkdir(parents=True, exist_ok=True)
    out_path = UE_SCRIPT_DIR / "SmartCityData.as"

    with open(out_path, "w", newline="\n") as f:
        f.write(
            "// AUTO-GENERATED by sim/export_angelscript.py — do not hand-edit.\n"
            "// Source of truth: sim/output/light_schedule.json (real logged simulation output).\n"
            f"// min_green={data['min_green']} max_green={data['max_green']} "
            f"duration_s={data['duration_s']}\n\n"
        )

        f.write("struct FSCIntersection\n{\n")
        f.write("    FString Id;\n    float X = 0.0;\n    float Z = 0.0;\n};\n\n")

        f.write("struct FSCLightEvent\n{\n")
        f.write("    float T = 0.0;\n    FString Intersection;\n"
                "    FString NsState;\n    FString EwState;\n};\n\n")

        f.write("struct FSCReleaseEvent\n{\n")
        f.write("    float T = 0.0;\n    FString Intersection;\n"
                "    FString Axis;\n    FString VehicleType;\n};\n\n")

        f.write("namespace SmartCityData\n{\n")
        f.write(f"    float DurationS = {float(data['duration_s']):.6f};\n\n")

        f.write("    void PopulateIntersections(array<FSCIntersection>& Out)\n    {\n")
        for it in data["intersections"]:
            f.write("        FSCIntersection I;\n")
            f.write(f'        I.Id = "{esc(it["id"])}";\n')
            f.write(f"        I.X = {float(it['x']):.6f};\n")
            f.write(f"        I.Z = {float(it['z']):.6f};\n")
            f.write("        Out.InsertLast(I);\n")
        f.write("    }\n\n")

        emit_chunked(
            f, data["light_log"], "FSCLightEvent",
            [("T", "t"), ("Intersection", "intersection"),
             ("NsState", "ns_state"), ("EwState", "ew_state")],
            "PopulateLightLog",
        )
        emit_chunked(
            f, data["release_log"], "FSCReleaseEvent",
            [("T", "t"), ("Intersection", "intersection"),
             ("Axis", "axis"), ("VehicleType", "vehicle_type")],
            "PopulateReleaseLog",
        )

        f.write("}\n")

    print(f"Wrote {out_path}")
    print(f"  intersections: {len(data['intersections'])}")
    print(f"  light_log events: {len(data['light_log'])}")
    print(f"  release_log events: {len(data['release_log'])}")


if __name__ == "__main__":
    main()
