import adsk.core, adsk.fusion, math, traceback

def run(context):
    app = adsk.core.Application.get()
    ui = app.userInterface

    try:
        design = app.activeProduct
        rootComp = design.rootComponent
        sketches = rootComp.sketches
        xyPlane = rootComp.xYConstructionPlane

        # === GET PARAMETERS FROM FUSION ===
        params = design.userParameters
        sphere_diameter = params.itemByName('sphere_diameter').value * 10
        material_thickness = params.itemByName('material_thickness').value * 10
        spacing = params.itemByName('spacing').value * 10
        kerf = params.itemByName('kerf').value * 10
        min_radius = params.itemByName('min_radius').value * 10
        #kerf = 0
        slot_width = material_thickness - kerf

        # === CALCULATIONS ===
        radius = sphere_diameter / 2
        num_slices_half = math.floor(radius / material_thickness)

        discs = []
        for i in range(num_slices_half + 1):
            h = i * material_thickness
            if h > radius:
                break
            r = math.sqrt(radius**2 - h**2)
            if r < min_radius:
                continue
            if i == 0:
                discs.append((r, 1))
            else:
                discs.append((r, 2))

        # === DELETE OLD SKETCHES ===
        names_to_delete = ["Sphere Slices", "Spine"]
        for i in range(sketches.count - 1, -1, -1):
            if sketches.item(i).name in names_to_delete:
                sketches.item(i).deleteMe()

        # =========================================
        # SKETCH 1: DISCS AS CLOSED PROFILES
        # =========================================
        sketch = sketches.add(xyPlane)
        sketch.name = "Sphere Slices"
        lines = sketch.sketchCurves.sketchLines
        arcs = sketch.sketchCurves.sketchArcs

        cursor_x = 0
        half_slot = slot_width / 2

        for r, count in discs:
            for c in range(count):
                center_x = cursor_x + r
                cx = center_x / 10  # mm to cm
                cy = 0
                r_cm = r / 10
                hs = half_slot / 10  # half slot in cm

                # The slot opens on the right edge of the circle
                # We need to find the angle where the slot edges meet the circle
                # Slot top/bottom y = +/- half_slot from center
                # On the circle: y = r * sin(angle)
                # So sin(angle) = half_slot / r
                # angle from positive x-axis

                if half_slot >= r:
                    # Slot is wider than disc - just draw a rectangle
                    p1 = adsk.core.Point3D.create(cx - r_cm, cy + hs, 0)
                    p2 = adsk.core.Point3D.create(cx + r_cm, cy + hs, 0)
                    p3 = adsk.core.Point3D.create(cx + r_cm, cy - hs, 0)
                    p4 = adsk.core.Point3D.create(cx - r_cm, cy - hs, 0)
                    lines.addByTwoPoints(p1, p2)
                    lines.addByTwoPoints(p2, p3)
                    lines.addByTwoPoints(p3, p4)
                    lines.addByTwoPoints(p4, p1)
                else:
                    # Angle where slot meets circle (from positive x-axis)
                    slot_angle = math.asin(half_slot / r)

                    # Points where slot edges intersect the circle (right side)
                    slot_top_x = cx + r_cm * math.cos(slot_angle)
                    slot_top_y = cy + hs
                    slot_bot_x = cx + r_cm * math.cos(slot_angle)
                    slot_bot_y = cy - hs

                    # Slot inner end (at center of circle)
                    slot_inner_top_x = cx
                    slot_inner_top_y = cy + hs
                    slot_inner_bot_x = cx
                    slot_inner_bot_y = cy - hs

                    # === Draw the profile ===

                    # 1. Arc: from slot_bottom intersection, going LEFT (the long way
                    #    around), to slot_top intersection
                    #    This is the main body of the circle minus the slot opening
                    centerPt = adsk.core.Point3D.create(cx, cy, 0)
                    startPt = adsk.core.Point3D.create(slot_bot_x, slot_bot_y, 0)
                    endPt = adsk.core.Point3D.create(slot_top_x, slot_top_y, 0)

                    # sweepAngle: going from bottom slot angle around the left side
                    # to top slot angle = full circle minus the slot opening
                    slot_opening_angle = 2 * slot_angle
                    sweep_angle = -(2 * math.pi - slot_opening_angle)  # negative = clockwise

                    arcs.addByCenterStartSweep(centerPt, startPt, sweep_angle)

                    # 2. Line: from slot_top circle intersection to slot inner top
                    p_st = adsk.core.Point3D.create(slot_top_x, slot_top_y, 0)
                    p_it = adsk.core.Point3D.create(slot_inner_top_x, slot_inner_top_y, 0)
                    lines.addByTwoPoints(p_st, p_it)

                    # 3. Line: slot inner top to slot inner bottom (the back wall)
                    p_ib = adsk.core.Point3D.create(slot_inner_bot_x, slot_inner_bot_y, 0)
                    lines.addByTwoPoints(p_it, p_ib)

                    # 4. Line: slot inner bottom to slot_bottom circle intersection
                    p_sb = adsk.core.Point3D.create(slot_bot_x, slot_bot_y, 0)
                    lines.addByTwoPoints(p_ib, p_sb)

                cursor_x += (r * 2) + spacing

        ui.messageBox(f"Discs done! Created {sum(c for _, c in discs)} discs.\nBuilding spine...")

        # =========================================
        # SKETCH 2: SPINE WITH NOTCHES AS CLEAN PROFILE
        # =========================================
        spine_sketch = sketches.add(xyPlane)
        spine_sketch.name = "Spine"
        spine_lines = spine_sketch.sketchCurves.sketchLines

        mt_cm = material_thickness / 10
        hs_cm = half_slot / 10

        # Collect all notch positions and sort bottom to top
        all_positions = []
        for i in range(num_slices_half + 1):
            h = i * material_thickness
            if h > radius:
                break
            r_disc = math.sqrt(radius**2 - h**2)
            if i == 0:
                all_positions.append((0, r_disc))
            else:
                all_positions.append((h, r_disc))
                all_positions.append((-h, r_disc))

        all_positions.sort(key=lambda x: x[0])

        # Spine dimensions
        spine_width = radius / 10  # cm - matches slot depth
        spine_left = 0.0
        spine_right = spine_width

        # Position spine below disc row
        spine_center_y = -(radius / 10 + spacing / 10 + sphere_diameter / 10 / 2 + 1)
        spine_bottom = spine_center_y - (sphere_diameter / 10 / 2)
        spine_top = spine_center_y + (sphere_diameter / 10 / 2)

        # Build the spine outline as a continuous path
        # Start bottom-right, go up the right side, across top,
        # down the left side with notches, across bottom

        path = []

        # Bottom-right corner
        path.append((spine_right, spine_bottom))

        # Up the right side (straight)
        path.append((spine_right, spine_top))

        # Across the top
        path.append((spine_left, spine_top))

        # Down the left side with notches (top to bottom)
        for h, r_disc in reversed(all_positions):
            notch_cy = spine_center_y + (h / 10)
            notch_top = notch_cy + mt_cm / 2
            notch_bot = notch_cy - mt_cm / 2
            notch_depth = r_disc / 10

            # Come down to notch top
            path.append((spine_left, notch_top))
            # Go right into notch
            path.append((spine_left + notch_depth, notch_top))
            # Down inside notch
            path.append((spine_left + notch_depth, notch_bot))
            # Back left out of notch
            path.append((spine_left, notch_bot))

        # Bottom-left corner
        path.append((spine_left, spine_bottom))

        # Close back to start
        path.append((spine_right, spine_bottom))

        # Draw all line segments
        for i in range(len(path) - 1):
            p1 = adsk.core.Point3D.create(path[i][0], path[i][1], 0)
            p2 = adsk.core.Point3D.create(path[i + 1][0], path[i + 1][1], 0)
            # Skip zero-length lines
            if abs(path[i][0] - path[i + 1][0]) > 0.0001 or abs(path[i][1] - path[i + 1][1]) > 0.0001:
                spine_lines.addByTwoPoints(p1, p2)

        # === SUMMARY ===
        total_discs = sum(count for _, count in discs)
        ui.messageBox(
            f"Done!\n"
            f"Created {total_discs} discs with integrated slots\n"
            f"Created spine with {len(all_positions)} notches\n"
            f"Sphere: {sphere_diameter:.1f}mm\n"
            f"Material: {material_thickness:.1f}mm"
        )

    except:
        ui.messageBox(f"Error:\n{traceback.format_exc()}")