#!/usr/bin/env python3
"""
Template-Skript für 3D-Modell-Generierung.
Kopiere und passe dieses Skript für jedes neue Projekt an.

Pfade (ZimaOS-headless, az-a, HOME=/DATA):
  VENV: source /DATA/AppData/hermes/venv/bin/activate
  OUTPUT: /DATA/AppData/hermes/Projekte/3D-Druck/  (schreibbar)
  MATPLOTLIB: Agg-Backend headless; MPLCONFIGDIR=/tmp/mplconf setzen
"""

import numpy as np
import trimesh
from trimesh.creation import box, cylinder, extrude_polygon
from trimesh.primitives import Sphere
import manifold3d as m3d
import os
import matplotlib
matplotlib.use('Agg')  # Headless-Backend
import matplotlib.pyplot as plt

OUTPUT_DIR = os.environ.get("OUTPUT_DIR", "/DATA/AppData/hermes/Projekte/3D-Druck")
os.makedirs(OUTPUT_DIR, exist_ok=True)


def trimesh_to_manifold(mesh):
    """Konvertiert trimesh.Mesh zu manifold3d.Manifold.

    Zwingend: float32 für Vertices, uint32 für Faces.
    Direkte Übergabe von trimesh-Arrays führt zu TypeError.
    """
    verts = np.asarray(mesh.vertices, dtype=np.float32)
    faces = np.asarray(mesh.faces, dtype=np.uint32)
    return m3d.Manifold(m3d.Mesh(vert_properties=verts, tri_verts=faces))


def manifold_to_trimesh(manifold):
    """Konvertiert manifold3d.Manifold zurück zu trimesh.Trimesh."""
    m = manifold.to_mesh()
    return trimesh.Trimesh(
        vertices=m.vert_properties[:, :3],
        faces=m.tri_verts
    )


def union(m1, m2):
    """Boolsche Vereinigung zweier trimesh-Meshes via manifold3d."""
    a = trimesh_to_manifold(m1)
    b = trimesh_to_manifold(m2)
    return manifold_to_trimesh(a + b)


def difference(m1, m2):
    """Boolsche Differenz (m1 minus m2) via manifold3d."""
    a = trimesh_to_manifold(m1)
    b = trimesh_to_manifold(m2)
    return manifold_to_trimesh(a - b)


def intersection(m1, m2):
    """Boolsche Schnittmenge zweier trimesh-Meshes via manifold3d."""
    a = trimesh_to_manifold(m1)
    b = trimesh_to_manifold(m2)
    return manifold_to_trimesh(a ^ b)


def save_stl(mesh, filename):
    """Speichert Mesh als STL mit manifold3d-Reparatur falls nötig."""
    if not mesh.is_watertight:
        print("WARN: Nicht watertight — repariere mit manifold3d...")
        mesh = manifold_to_trimesh(trimesh_to_manifold(mesh))
    path = os.path.join(OUTPUT_DIR, filename)
    mesh.export(path)
    print(f"✅ Gespeichert: {path}")
    print(f"   Volumen: {mesh.volume / 1000:.2f} cm³")
    print(f"   Oberfläche: {mesh.area / 100:.2f} cm²")
    print(f"   Bounding Box: {mesh.bounding_box.extents}")
    return path


def validate(mesh):
    """Druckbarkeits-Validierung."""
    ok = True
    if not mesh.is_watertight:
        print("❌ Nicht watertight")
        ok = False
    if mesh.volume <= 0:
        print("❌ Negatives Volumen (inside-out?)")
        ok = False
    if ok:
        print("✅ Druckbarkeit OK")
    return ok


def hole(position, diameter, depth):
    """Erzeugt ein zylindrisches Loch (für Schrauben/Gewinde)."""
    h = cylinder(radius=diameter / 2, height=depth, sections=32)
    h.apply_translation(position)
    return h


def render_preview(mesh, filepath=None, elevation=30, azimuth=45):
    """Erzeugt eine PNG-Vorschau des Meshes."""
    scene = mesh.scene()
    png = scene.save_image(resolution=[1024, 768], visible=False)
    if filepath is None:
        filepath = os.path.join(OUTPUT_DIR, "preview.png")
    with open(filepath, 'wb') as f:
        f.write(png)
    print(f"🖼️  Preview: {filepath}")
    return filepath


# ==========================================
# BEISPIEL: Einfache Box mit Loch
# ==========================================
if __name__ == "__main__":
    # Basis-Box: 50x30x10 mm
    base = box(extents=[50, 30, 10])

    # Loch für M5-Schraube in der Mitte (5.2mm = Gleitpassung)
    h = hole([0, 0, 0], diameter=5.2, depth=12)

    # Differenz
    model = difference(base, h)

    # Validieren, preview und speichern
    if validate(model):
        save_stl(model, "beispiel_box_m5.stl")
        try:
            render_preview(model)
        except Exception as e:
            print(f"⚠️ Preview-Fehler (kein Display?): {e}")
