"""Blender companion script: restore sharp + seam edge marks after importing a production GLB.

After importing a Schaeffler production GLB in Blender, run this script once via
the Scripting workspace (Text Editor → Run Script). It reads the sharp angle AND the
OCC B-rep sharp edge pairs baked into the GLB at export time, and re-applies
mark_sharp() + mark_seam() on every mesh object.

The GLB visual shading already encodes the sharp edges via vertex splits (normals).
This script restores the blue sharp-crease and red seam markers in Edit Mode for
further editing in Blender — including for UV unwrapping.

Usage:
    1. File → Import → glTF 2.0 (.glb)  — open your production GLB
    2. Open the Scripting workspace
    3. Open this file (Text Editor → Open → restore_sharp_marks.py)
    4. Click Run Script
"""

import bpy
import math
import bmesh
import mathutils

mesh_objects = [o for o in bpy.data.objects if o.type == "MESH"]
if not mesh_objects:
    print("No mesh objects found in scene.")
else:
    # --- Pass 1: dihedral-angle-based sharp/seam marks ---
    angle_deg = bpy.context.scene.get("schaeffler_sharp_angle_deg", 30.0)
    smooth_rad = math.radians(float(angle_deg))

    total_sharp = 0
    bpy.ops.object.select_all(action="DESELECT")
    for obj in mesh_objects:
        bpy.context.view_layer.objects.active = obj
        obj.select_set(True)
        bpy.ops.object.mode_set(mode="OBJECT")
        for poly in obj.data.polygons:
            poly.use_smooth = True
        bpy.ops.object.mode_set(mode="EDIT")
        bpy.ops.mesh.select_all(action="DESELECT")
        bpy.ops.mesh.edges_select_sharp(sharpness=smooth_rad)
        bpy.ops.mesh.mark_sharp()
        bpy.ops.mesh.mark_seam()
        bpy.ops.object.mode_set(mode="OBJECT")
        total_sharp += sum(1 for e in obj.data.edges if e.use_edge_sharp)
        obj.select_set(False)
    print(f"Pass 1 (dihedral {angle_deg}°): {total_sharp} sharp/seam edges across {len(mesh_objects)} objects.")

    # --- Pass 2: OCC B-rep sharp edges from GLB extras ---
    # The production GLB embeds schaeffler_sharp_edge_pairs (OCC B-rep topology,
    # dense curve samples at 0.3mm) in scenes[0].extras, which Blender maps to
    # scene custom properties on import. These cover geometrically sharp edges
    # that the dihedral-angle pass misses due to tessellation noise.
    #
    # Coordinate convention (mirrors export_gltf.py _apply_sharp_edges_from_occ):
    # OCC STEP space (Z-up, mm) → Blender (Z-up, m):
    #   Blender(X, Y, Z) = OCC(X*0.001, -Z*0.001, Y*0.001)
    occ_pairs = bpy.context.scene.get("schaeffler_sharp_edge_pairs") or []
    if occ_pairs:
        print(f"Pass 2 (OCC B-rep): applying {len(occ_pairs)} sharp edge segment pairs...")

        SCALE = 0.001   # OCC mm → Blender m
        TOL = 0.0005    # 0.5 mm tolerance in metres

        marked_total = 0
        for obj in mesh_objects:
            bm_obj = bmesh.new()
            bm_obj.from_mesh(obj.data)
            bm_obj.verts.ensure_lookup_table()
            bm_obj.edges.ensure_lookup_table()

            world_mat = obj.matrix_world
            kd = mathutils.kdtree.KDTree(len(bm_obj.verts))
            for v in bm_obj.verts:
                kd.insert(world_mat @ v.co, v.index)
            kd.balance()

            marked = 0
            for pair in occ_pairs:
                v0_bl = mathutils.Vector((pair[0][0] * SCALE, -pair[0][2] * SCALE, pair[0][1] * SCALE))
                v1_bl = mathutils.Vector((pair[1][0] * SCALE, -pair[1][2] * SCALE, pair[1][1] * SCALE))
                _co0, idx0, dist0 = kd.find(v0_bl)
                _co1, idx1, dist1 = kd.find(v1_bl)
                if dist0 > TOL or dist1 > TOL:
                    continue
                if idx0 == idx1:
                    continue
                bv0, bv1 = bm_obj.verts[idx0], bm_obj.verts[idx1]
                edge = bm_obj.edges.get((bv0, bv1)) or bm_obj.edges.get((bv1, bv0))
                if edge is not None:
                    edge.smooth = False
                    edge.seam = True
                    marked += 1

            bm_obj.to_mesh(obj.data)
            bm_obj.free()
            marked_total += marked

        print(f"Pass 2 (OCC B-rep): {marked_total} additional edges marked across {len(mesh_objects)} objects.")
    else:
        print("Pass 2 (OCC B-rep): no schaeffler_sharp_edge_pairs in GLB extras — skipped.")
