"""Generate test STL meshes for decomposition quality testing."""
import struct
import math

def write_stl(path, triangles):
    with open(path, "wb") as f:
        f.write(b'\0' * 80)
        f.write(struct.pack('<I', len(triangles)))
        for tri in triangles:
            v0, v1, v2 = tri
            e1 = [v1[i]-v0[i] for i in range(3)]
            e2 = [v2[i]-v0[i] for i in range(3)]
            n = [e1[1]*e2[2]-e1[2]*e2[1], e1[2]*e2[0]-e1[0]*e2[2], e1[0]*e2[1]-e1[1]*e2[0]]
            l = sum(x*x for x in n)**0.5
            n = [x/l for x in n] if l > 0 else [0,0,0]
            f.write(struct.pack('<3f', *n))
            for v in [v0, v1, v2]:
                f.write(struct.pack('<3f', *[float(x) for x in v]))
            f.write(struct.pack('<H', 0))

def sphere_mesh(center, radius, subdivisions=16):
    tris = []
    for i in range(subdivisions):
        theta0 = math.pi * i / subdivisions
        theta1 = math.pi * (i + 1) / subdivisions
        for j in range(subdivisions * 2):
            phi0 = 2 * math.pi * j / (subdivisions * 2)
            phi1 = 2 * math.pi * (j + 1) / (subdivisions * 2)
            def pt(t, p):
                return (
                    center[0] + radius * math.sin(t) * math.cos(p),
                    center[1] + radius * math.sin(t) * math.sin(p),
                    center[2] + radius * math.cos(t),
                )
            p00 = pt(theta0, phi0)
            p10 = pt(theta1, phi0)
            p01 = pt(theta0, phi1)
            p11 = pt(theta1, phi1)
            if i > 0:
                tris.append((p00, p10, p01))
            if i < subdivisions - 1:
                tris.append((p01, p10, p11))
    return tris

def cylinder_mesh(center, radius, height, segments=32):
    tris = []
    half_h = height / 2.0
    for i in range(segments):
        a0 = 2 * math.pi * i / segments
        a1 = 2 * math.pi * (i + 1) / segments
        x0, y0 = center[0] + radius * math.cos(a0), center[1] + radius * math.sin(a0)
        x1, y1 = center[0] + radius * math.cos(a1), center[1] + radius * math.sin(a1)
        top_z = center[2] + half_h
        bot_z = center[2] - half_h
        # Side
        tris.append(((x0, y0, bot_z), (x1, y1, bot_z), (x0, y0, top_z)))
        tris.append(((x1, y1, bot_z), (x1, y1, top_z), (x0, y0, top_z)))
        # Top cap
        tris.append(((center[0], center[1], top_z), (x0, y0, top_z), (x1, y1, top_z)))
        # Bottom cap
        tris.append(((center[0], center[1], bot_z), (x1, y1, bot_z), (x0, y0, bot_z)))
    return tris

def box_mesh(center, hx, hy, hz):
    cx, cy, cz = center
    v = [
        (cx-hx, cy-hy, cz-hz), (cx+hx, cy-hy, cz-hz),
        (cx+hx, cy+hy, cz-hz), (cx-hx, cy+hy, cz-hz),
        (cx-hx, cy-hy, cz+hz), (cx+hx, cy-hy, cz+hz),
        (cx+hx, cy+hy, cz+hz), (cx-hx, cy+hy, cz+hz),
    ]
    faces = [
        (0,2,1), (0,3,2),
        (4,5,6), (4,6,7),
        (0,1,5), (0,5,4),
        (2,3,7), (2,7,6),
        (0,4,7), (0,7,3),
        (1,2,6), (1,6,5),
    ]
    return [(v[a], v[b], v[c]) for a, b, c in faces]


# Test 1: Sphere (t=1)
print("generating sphere.stl...")
write_stl("examples/sphere.stl", sphere_mesh((0, 0, 0), 3.0, 24))

# Test 2: Cylinder (t=1)
print("generating cylinder.stl...")
write_stl("examples/cylinder.stl", cylinder_mesh((0, 0, 0), 2.0, 6.0, 48))

# Test 3: Two-box union (t=3: 2 boxes + 1 union)
# Separated so no overlapping faces
print("generating two_boxes.stl...")
tris = box_mesh((-3, 0, 0), 2, 1, 1) + box_mesh((3, 0, 0), 1, 2, 1)
write_stl("examples/two_boxes.stl", tris)

# Test 4: Box with sphere cut (t=3: box + sphere + difference)
print("generating box_minus_sphere.stl...")
# Approximate by densely sampling the SDF and marching-cubes-like output
# For simplicity, just use the box mesh (the cut won't show in a naive mesh union)
# Instead generate a box mesh - the decompiler should detect it as a box
tris = box_mesh((0, 0, 0), 3, 3, 3)
write_stl("examples/box3.stl", tris)

# Test 5: Translated sphere (t=2: sphere + translate)
print("generating translated_sphere.stl...")
write_stl("examples/translated_sphere.stl", sphere_mesh((5, 3, -2), 2.0, 20))

# Test 6: Box + cylinder (t=3: 2 objects + 1 union)
# Separated with gap
print("generating box_and_cylinder.stl...")
tris = box_mesh((-4, 0, 0), 2, 2, 2) + cylinder_mesh((4, 0, 0), 1.5, 4.0, 48)
write_stl("examples/box_and_cylinder.stl", tris)

print("done.")
