import bpy
import bmesh
import json
import math
import mathutils
import statistics
import sys
from pathlib import Path


glb_path = Path(sys.argv[sys.argv.index("--") + 1])
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.ops.import_scene.gltf(filepath=str(glb_path))

report = {
    "file": str(glb_path),
    "objects": [],
}

for obj in bpy.context.scene.objects:
    if obj.type != "MESH":
        continue

    mesh = obj.data
    mesh.calc_loop_triangles()
    bm = bmesh.new()
    bm.from_mesh(mesh)
    bm.normal_update()

    boundary_edges = sum(1 for edge in bm.edges if edge.is_boundary)
    non_manifold_edges = sum(1 for edge in bm.edges if not edge.is_manifold)
    smooth_faces = sum(1 for face in mesh.polygons if face.use_smooth)

    triangle_areas = [tri.area for tri in mesh.loop_triangles]
    triangle_areas_sorted = sorted(triangle_areas)
    area_count = len(triangle_areas_sorted)

    area_weighted_normals = [mathutils.Vector((0.0, 0.0, 0.0)) for _ in mesh.vertices]
    for tri in mesh.loop_triangles:
        weighted = tri.normal * tri.area
        for vertex_index in tri.vertices:
            area_weighted_normals[vertex_index] += weighted
    for normal in area_weighted_normals:
        if normal.length_squared:
            normal.normalize()

    normal_deviation_degrees = []
    for corner_index, corner in enumerate(mesh.corner_normals):
        vertex_index = mesh.loops[corner_index].vertex_index
        dot = max(-1.0, min(1.0, corner.vector.dot(area_weighted_normals[vertex_index])))
        normal_deviation_degrees.append(math.degrees(math.acos(dot)))
    normal_deviation_degrees.sort()

    def percentile(values, fraction):
        if not values:
            return None
        return values[min(len(values) - 1, round((len(values) - 1) * fraction))]

    dimensions = obj.dimensions
    report["objects"].append(
        {
            "name": obj.name,
            "mesh": mesh.name,
            "vertices": len(mesh.vertices),
            "edges": len(mesh.edges),
            "polygons": len(mesh.polygons),
            "triangles": len(mesh.loop_triangles),
            "smooth_faces": smooth_faces,
            "flat_faces": len(mesh.polygons) - smooth_faces,
            "boundary_edges": boundary_edges,
            "non_manifold_edges": non_manifold_edges,
            "dimensions": [dimensions.x, dimensions.y, dimensions.z],
            "uv_layers": len(mesh.uv_layers),
            "color_attributes": len(mesh.color_attributes),
            "attributes": [attribute.name for attribute in mesh.attributes],
            "materials": [
                {
                    "name": slot.material.name if slot.material else None,
                    "use_nodes": slot.material.use_nodes if slot.material else None,
                }
                for slot in obj.material_slots
            ],
            "triangle_area": {
                "min": triangle_areas_sorted[0] if area_count else None,
                "p01": percentile(triangle_areas_sorted, 0.01),
                "p50": percentile(triangle_areas_sorted, 0.50),
                "p99": percentile(triangle_areas_sorted, 0.99),
                "max": triangle_areas_sorted[-1] if area_count else None,
                "mean": statistics.fmean(triangle_areas) if area_count else None,
                "below_1e_6": sum(1 for area in triangle_areas if area < 1e-6),
                "below_2e_6": sum(1 for area in triangle_areas if area < 2e-6),
            },
            "custom_to_vertex_normal_deviation_degrees": {
                "p50": percentile(normal_deviation_degrees, 0.50),
                "p90": percentile(normal_deviation_degrees, 0.90),
                "p99": percentile(normal_deviation_degrees, 0.99),
                "max": normal_deviation_degrees[-1] if normal_deviation_degrees else None,
            },
        }
    )
    bm.free()

print("TN_GLTF_AUDIT_BEGIN")
print(json.dumps(report, indent=2))
print("TN_GLTF_AUDIT_END")
