import FreeCAD as App
import FreeCADGui as Gui
import Part, MeshPart
import json, os, traceback

def log(msg):
    App.Console.PrintMessage(str(msg) + "\n")

try:
    doc = App.ActiveDocument
    sel = Gui.Selection.getSelection()
    if not sel:
        log("❌ Bir obje seçilmedi.")
        raise SystemExit

    obj = sel[0]
    if not hasattr(obj, "Shape"):
        log("❌ Seçili obje Shape içermiyor.")
        raise SystemExit

    shape = obj.Shape
    log(f"✅ Objeyi aldı: {obj.Name}")
    log(f"Face sayısı: {len(shape.Faces)}, Edge sayısı: {len(shape.Edges)}, Vertex sayısı: {len(shape.Vertexes)}")

    # ------------------ FACE RENKLERİ ------------------
    diffuse = getattr(obj.ViewObject, "DiffuseColor", None)
    if diffuse and len(diffuse) == 1 and len(shape.Faces) > 1:
        diffuse = diffuse * len(shape.Faces)
    if not diffuse or len(diffuse) < len(shape.Faces):
        diffuse = [(0.8,0.8,0.8,1.0)] * len(shape.Faces)

    # ------------------ ADAPTIVE LINEAR DEFLECTION ------------------
    size = shape.BoundBox.DiagonalLength
    LinearDeflection = size / 500.0
    AngularDeflection = 0.5

    # ------------------ VERTEX POOL (DEDUPLICATE) ------------------
    vertices = []
    vertex_map = {}  # key -> vertex index
    tolerance = 1e-7  # Çok kesin tolerans

    def add_vertex(v):
        x, y, z = (v.x, v.y, v.z) if hasattr(v, "x") else (v[0], v[1], v[2])

        # Yaklaşık koordinatları olan vertex'i bul
        key = (round(x / tolerance), round(y / tolerance), round(z / tolerance))

        if key in vertex_map:
            return vertex_map[key]  # Mevcut vertex'i kullan

        # Yeni vertex ekle
        idx = len(vertices)
        vertices.append([x, y, z])
        vertex_map[key] = idx
        return idx

    # ------------------ FACE TRIANGLES & BREP INFO ------------------
    triangles = []
    vertex_colors = [None] * 100000  # Pre-allocate büyük array
    vertex_alpha = [None] * 100000
    vertex_grup = [None] * 100000
    face_brep_info = []

    strti = 0
    grpi = 0

    for fi, face in enumerate(shape.Faces):
        fmesh = MeshPart.meshFromShape(face, LinearDeflection=LinearDeflection, AngularDeflection=AngularDeflection)
        face_vertex_indices = []
        for tri in fmesh.Facets:
            # Facet point erişimi
            p = getattr(tri, "Points", None)
            if not p:
                continue
            v0 = add_vertex(p[0])
            v1 = add_vertex(p[1])
            v2 = add_vertex(p[2])
            triangles.append([v0, v1, v2])
            face_vertex_indices.extend([v0, v1, v2])

        # Vertex renk / alpha / grup
        count = len(face_vertex_indices)
        r,g,b,a = diffuse[fi]
        for vi in face_vertex_indices:
            # Pre-allocated array'a doğrudan ata
            if vertex_colors[vi] is None:
                vertex_colors[vi] = [r,g,b]
                vertex_alpha[vi] = a
                vertex_grup[vi] = [strti, count, grpi]
        strti += count
        grpi += 1

    # Array'ı trim et - gereksiz None'ları kaldır
    vertex_colors = [c for c in vertex_colors if c is not None]
    vertex_alpha = [a for a in vertex_alpha if a is not None]
    vertex_grup = [g for g in vertex_grup if g is not None]

    for fi, face in enumerate(shape.Faces):
        # ------------------ BREP INFO ------------------
        geom = face.Surface
        info = {"index": fi}

        if isinstance(geom, Part.Plane):
            info.update({
                "type": "Plane",
                "normal": [geom.Axis.x, geom.Axis.y, geom.Axis.z],
                "position": [geom.Position.x, geom.Position.y, geom.Position.z]
            })
        elif isinstance(geom, Part.Sphere):
            info.update({
                "type": "Sphere",
                "radius": geom.Radius,
                "center": [geom.Center.x, geom.Center.y, geom.Center.z]
            })
        elif isinstance(geom, Part.Cylinder):
            pos = face.CenterOfMass
            info.update({
                "type": "Cylinder",
                "radius": geom.Radius,
                "axis": [geom.Axis.x, geom.Axis.y, geom.Axis.z],
                "position": [pos.x, pos.y, pos.z]
            })
        elif isinstance(geom, Part.Cone):
            pos = face.CenterOfMass
            info.update({
                "type": "Cone",
                "radius1": geom.Radius1,
                "radius2": geom.Radius2,
                "axis": [geom.Axis.x, geom.Axis.y, geom.Axis.z],
                "position": [pos.x, pos.y, pos.z]
            })
        elif isinstance(geom, Part.Torus):
            pos = face.CenterOfMass
            info.update({
                "type": "Torus",
                "majorRadius": geom.MajorRadius,
                "minorRadius": geom.MinorRadius,
                "axis": [geom.Axis.x, geom.Axis.y, geom.Axis.z],
                "position": [pos.x, pos.y, pos.z]
            })
        elif isinstance(geom, Part.BSplineSurface):
            uPoles = geom.getPoles()[0]
            vPoles = geom.getPoles()[1]
            info.update({
                "type": "BSplineSurface",
                "uPoles": [[p.x, p.y, p.z] for p in uPoles],
                "vPoles": [[p.x, p.y, p.z] for p in vPoles]
            })
        else:
            info.update({"type": "Unknown"})
        face_brep_info.append(info)

    # ------------------ EDGES ------------------
    edges_out = []
    edge_colors = [None] * 100000  # Pre-allocate
    edge_alpha = [None] * 100000
    edge_grup = [None] * 100000
    edge_brep_info = []

    strtlinei = 0
    grplinei = 0

    for ei, e in enumerate(shape.Edges):
        v0 = add_vertex(e.Vertexes[0].Point)
        v1 = add_vertex(e.Vertexes[-1].Point)
        edges_out.append([v0, v1])

        for vi in [v0, v1]:
            if edge_colors[vi] is None:
                edge_colors[vi] = [0.2,0.2,0.2]
                edge_alpha[vi] = 1.0
                edge_grup[vi] = [strtlinei,2,grplinei]
        strtlinei += 2
        grplinei += 1

        # Edge BREP info
        try:
            curve = e.Curve
            cinfo = {"index": ei}
            if isinstance(curve, Part.Line):
                cinfo.update({
                    "type": "Line",
                    "start": [e.Vertexes[0].Point.x, e.Vertexes[0].Point.y, e.Vertexes[0].Point.z],
                    "end": [e.Vertexes[-1].Point.x, e.Vertexes[-1].Point.y, e.Vertexes[-1].Point.z]
                })
            elif isinstance(curve, Part.Circle):
                cinfo.update({
                    "type": "Circle",
                    "center": [curve.Center.x, curve.Center.y, curve.Center.z],
                    "radius": curve.Radius,
                    "axis": [curve.Axis.x, curve.Axis.y, curve.Axis.z]
                })
            elif isinstance(curve, Part.BSplineCurve):
                cinfo.update({
                    "type": "BSplineCurve",
                    "control_points": [[p.x, p.y, p.z] for p in curve.getPoles()]
                })
            else:
                cinfo.update({"type": "Unknown"})
        except Exception:
            cinfo = {"index": ei, "type": "Unknown"}

        edge_brep_info.append(cinfo)

    # Edge arrays'ı trim et
    edge_colors = [c for c in edge_colors if c is not None]
    edge_alpha = [a for a in edge_alpha if a is not None]
    edge_grup = [g for g in edge_grup if g is not None]

    # ------------------ POINTS ------------------
    points_geom = [[v.Point.x, v.Point.y, v.Point.z] for v in shape.Vertexes]

    # ------------------ JSON OUTPUT ------------------
    out = {
        "vertices": vertices,
        "faces": {
            "triangles": triangles,
            "color": [c for col in vertex_colors for c in col],
            "alpha": vertex_alpha,
            "grup": [g for trip in vertex_grup for g in trip],
            "brep": face_brep_info
        },
        "edges": {
            "lines": edges_out,
            "color": [c for col in edge_colors for c in col],
            "alpha": edge_alpha,
            "grup": [g for trip in edge_grup for g in trip],
            "brep": edge_brep_info
        },
        "points": points_geom,
        "object_name": obj.Name
    }

    path = os.path.join(os.path.expanduser("~"), f"{obj.Name}_threejs_brep.json")
    with open(path, "w", encoding="utf-8") as f:
        json.dump(out, f, separators=(',', ':'))

    log(f"\n✅ Export tamamlandı: {path}")

except Exception:
    log(traceback.format_exc())
