import FreeCAD as App
import FreeCADGui as Gui
import Part, MeshPart
import json, os, traceback
import math

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 (OPTİMİZASYON KAPALI) ------------------
    vertices = []

    def add_vertex(v):
        # Her vertex duplicate ekleniyor
        x, y, z = (v.x, v.y, v.z) if hasattr(v, "x") else (v[0], v[1], v[2])
        idx = len(vertices)
        vertices.append([x, y, z])
        return idx

    # ------------------ FACE TRIANGLES & BREP INFO ------------------
    triangles = []
    vertex_colors = []
    vertex_alpha = []
    vertex_grup = []
    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:
            while len(vertex_colors) <= vi:
                vertex_colors.append(None)
                vertex_alpha.append(None)
                vertex_grup.append(None)
            vertex_colors[vi] = [r,g,b]
            vertex_alpha[vi] = a
            vertex_grup[vi] = [strti, count, grpi]
        strti += count
        grpi += 1

        # ------------------ 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 = {}  # Dictionary - dinamik
    edge_alpha = {}
    edge_grup = {}
    edge_brep_info = []

    strtlinei = 0
    grplinei = 0

    for ei, e in enumerate(shape.Edges):
        try:
            curve = e.Curve
            cinfo = {"index": ei}
            segment_vertices = []

            if isinstance(curve, Part.Line):
                # Düz çizgi - olduğu gibi
                v0 = add_vertex(e.Vertexes[0].Point)
                v1 = add_vertex(e.Vertexes[-1].Point)
                edges_out.append([v0, v1])
                segment_vertices = [[v0, v1]]

                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):
                # Circle - parçala
                center = curve.Center
                radius = curve.Radius

                # Edge üzerinde eşit aralıklı noktalar al
                tolerance = 10
                arc_length = e.Length  # Edge'in toplam uzunluğu
                num_segments = max(2, int(math.ceil(arc_length / tolerance)))

                pts = []
                for i in range(num_segments + 1):
                    param = e.ParameterRange[0] + (e.ParameterRange[1] - e.ParameterRange[0]) * i / num_segments
                    pt = e.valueAt(param)
                    pts.append(pt)

                # Segment'ler oluştur
                segment_vertices = []
                for i in range(len(pts) - 1):
                    v_i = add_vertex(pts[i])
                    v_next = add_vertex(pts[i + 1])
                    edges_out.append([v_i, v_next])
                    segment_vertices.append([v_i, v_next])

                cinfo.update({
                    "type": "Circle",
                    "center": [center.x, center.y, center.z],
                    "radius": radius,
                    "axis": [curve.Axis.x, curve.Axis.y, curve.Axis.z]
                })

            elif isinstance(curve, Part.BSplineCurve):
                # BSpline - parçala
                tolerance = 10
                arc_length = e.Length
                num_segments = max(2, int(math.ceil(arc_length / tolerance)))

                pts = []
                for i in range(num_segments + 1):
                    param = e.ParameterRange[0] + (e.ParameterRange[1] - e.ParameterRange[0]) * i / num_segments
                    pt = e.valueAt(param)
                    pts.append(pt)

                # Segment'ler oluştur
                segment_vertices = []
                for i in range(len(pts) - 1):
                    v_i = add_vertex(pts[i])
                    v_next = add_vertex(pts[i + 1])
                    edges_out.append([v_i, v_next])
                    segment_vertices.append([v_i, v_next])

                control_pts = curve.getPoles()
                cinfo.update({
                    "type": "BSplineCurve",
                    "control_points": [[p.x, p.y, p.z] for p in control_pts]
                })

            else:
                # Unknown - olduğu gibi
                v0 = add_vertex(e.Vertexes[0].Point)
                v1 = add_vertex(e.Vertexes[-1].Point)
                edges_out.append([v0, v1])
                segment_vertices = [[v0, v1]]
                cinfo.update({"type": "Unknown"})

        except Exception:
            v0 = add_vertex(e.Vertexes[0].Point)
            v1 = add_vertex(e.Vertexes[-1].Point)
            edges_out.append([v0, v1])
            segment_vertices = [[v0, v1]]
            cinfo = {"index": ei, "type": "Unknown"}

        # Segment'lere renk/alpha/grup ata (aynı grup ID)
        num_segments = len(segment_vertices)
        for seg in segment_vertices:
            for vi in seg:
                edge_colors[vi] = [0.2, 0.2, 0.2]
                edge_alpha[vi] = 1.0
                edge_grup[vi] = [strtlinei, num_segments * 2, grplinei]

        strtlinei += num_segments * 2
        grplinei += 1

        edge_brep_info.append(cinfo)

    # Face arrays'ı trim et
    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]

    # Edge dictionary'leri list'e çevir (indeks sırasına göre)
    max_edge_idx = max(edge_colors.keys()) if edge_colors else -1
    edge_colors_list = []
    edge_alpha_list = []
    edge_grup_list = []

    for i in range(max_edge_idx + 1):
        if i in edge_colors:
            edge_colors_list.append(edge_colors[i])
            edge_alpha_list.append(edge_alpha[i])
            edge_grup_list.append(edge_grup[i])
        else:
            edge_colors_list.append([0.2, 0.2, 0.2])
            edge_alpha_list.append(1.0)
            edge_grup_list.append([0, 0, 0])

    # ------------------ 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_list for c in col],
            "alpha": edge_alpha_list,
            "grup": [g for trip in edge_grup_list 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())
