"""Generate per-tile ASCII walkmeshes (`twu01_<code>.wok`) from tiles.py.

    python make_wok.py [outdir]       # default: ./wok

Pure Python, no Blender. Emits the Aurora ASCII walkmesh node (verts + faces with
per-face surface material + an AABB bounding-volume tree). This is the usable
intermediate the toolset/cleanmodels/nwnmdlcomp compile to a binary `.wok`
(AGENTS.md §6); NeverBlender would otherwise produce this. Geometry comes from
tiles.wok_quads so it matches the Blender WOK_/BLOCK_ proxies exactly.

Surface material ids (surfacemat.2da, AGENTS.md §3.3): walkable faces use WALK,
non-walkable proxy faces use BLOCK. Verify these two ids against your
surfacemat.2da before shipping.
"""

import os
import sys

import tiles

WALK = 4    # surfacemat.2da row 4 = Stone, walkable (Neverblender wok_Stone)
BLOCK = 7   # surfacemat.2da row 7 = Nonwalk, non-walkable (wok_Nonwalk)


def geometry(spec):
    """Return (verts, faces): dedup verts, faces = (a, b, c, surfacemat)."""
    verts, vmap, faces = [], {}, []

    def vid(v):
        key = tuple(round(c, 4) for c in v)
        if key not in vmap:
            vmap[key] = len(verts)
            verts.append(key)
        return vmap[key]

    for quad, walkable in tiles.wok_quads(spec):
        s = WALK if walkable else BLOCK
        a, b, c, d = (vid(p) for p in quad)
        faces.append((a, b, c, s))          # triangulate quad -> 2 tris
        faces.append((a, c, d, s))
    return verts, faces


def _box(verts, face):
    pts = [verts[face[0]], verts[face[1]], verts[face[2]]]
    return ([min(p[i] for p in pts) for i in range(3)],
            [max(p[i] for p in pts) for i in range(3)])


def aabb_tree(verts, faces):
    """Binary BVH, pre-order (node, left subtree, right subtree). Leaf carries its
    face index; internal carries -1. Reader reconstructs by the same recursion."""
    nodes = []

    def rec(items):  # items = [(face_index, (min, max)), ...]
        mn = [min(b[0][i] for _, b in items) for i in range(3)]
        mx = [max(b[1][i] for _, b in items) for i in range(3)]
        if len(items) == 1:
            nodes.append((mn, mx, items[0][0]))
            return
        ax = max(range(3), key=lambda i: mx[i] - mn[i])          # split longest axis
        items.sort(key=lambda ib: ib[1][0][ax] + ib[1][1][ax])   # by box centre
        mid = len(items) // 2
        nodes.append((mn, mx, -1))                               # internal placeholder
        rec(items[:mid])
        rec(items[mid:])

    rec([(i, _box(verts, f)) for i, f in enumerate(faces)])
    assert sum(1 for _, _, fi in nodes if fi >= 0) == len(faces)  # every face is a leaf
    assert len(nodes) == 2 * len(faces) - 1                       # full binary tree
    return nodes


def aabb_node(spec):
    """The `node aabb <ref>_wok ... endnode` block (reused by make_mdl embed)."""
    verts, faces = geometry(spec)
    nodes = aabb_tree(verts, faces)
    name = tiles.resref(spec)
    L = [f"node aabb {name}_wg",
         f"  parent {name}",
         "  position 0.0 0.0 0.0",
         "  orientation 0.0 0.0 0.0 0.0",
         "  bitmap NULL",
         "  ambient 0.0 0.0 0.0",
         "  diffuse 0.0 0.0 0.0",
         "  specular 0.0 0.0 0.0",
         "  render 0",
         "  shadow 0",
         f"  verts {len(verts)}"]
    L += [f"    {v[0]:.5f} {v[1]:.5f} {v[2]:.5f}" for v in verts]
    L.append(f"  faces {len(faces)}")
    # face line: v1 v2 v3 smoothgroup tv1 tv2 tv3 surfacemat
    L += [f"    {a} {b} {c} 1 0 0 0 {s}" for a, b, c, s in faces]
    L.append("  aabb")
    L += [f"    {mn[0]:.5f} {mn[1]:.5f} {mn[2]:.5f} {mx[0]:.5f} {mx[1]:.5f} {mx[2]:.5f} {fi}"
          for mn, mx, fi in nodes]
    L.append("endnode")
    return name, L


def emit(spec):
    name, L = aabb_node(spec)
    head = f"# {name} walkmesh (ASCII) - compile via nwnmdlcomp/cleanmodels"
    return name, head + "\n" + "\n".join(L) + "\n"


def main():
    outdir = sys.argv[1] if len(sys.argv) > 1 else "wok"
    os.makedirs(outdir, exist_ok=True)
    for spec in tiles.TILES:
        name, text = emit(spec)
        with open(os.path.join(outdir, name + ".wok"), "w") as fh:
            fh.write(text)
    print(f"wrote {len(tiles.TILES)} .wok files to {outdir}/")


if __name__ == "__main__":
    main()
