bl_info = {
    "name": "Edge Tools Pie Menu",
    "author": "GG & ChatGPT",
    "version": (1, 0, 0),
    "blender": (3, 4, 0),  # minimum supported Blender version
    "location": "3D View > Edit Mode > Pie Menu",
    "description": "Quick access to edge tools: Sharp, Seam, Crease, Bevel Weight, Select Similar",
    "category": "Mesh",
}


import bpy
import bmesh
from bpy.types import Menu, Operator
from bpy.types import AddonPreferences

# --- Operators ---
class MESH_OT_mark_sharp(Operator):
    bl_idname = "mesh.quick_mark_sharp"
    bl_label = "Mark Sharp"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        obj = context.object
        me = obj.data
        bm = bmesh.from_edit_mesh(me)
        sel_edges = [e for e in bm.edges if e.select]

        if not sel_edges:
            self.report({'INFO'}, "No edges selected")
            return {'CANCELLED'}

        all_sharp = all(not e.smooth for e in sel_edges)
        new_state = True if not all_sharp else False

        for e in sel_edges:
            e.smooth = not new_state  # True=smooth, False=sharp

        bmesh.update_edit_mesh(me)
        return {'FINISHED'}


class MESH_OT_mark_seam(Operator):
    bl_idname = "mesh.quick_mark_seam"
    bl_label = "Mark Seam"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        obj = context.object
        me = obj.data
        bm = bmesh.from_edit_mesh(me)
        sel_edges = [e for e in bm.edges if e.select]

        if not sel_edges:
            self.report({'INFO'}, "No edges selected")
            return {'CANCELLED'}

        all_seam = all(e.seam for e in sel_edges)
        new_state = True if not all_seam else False

        for e in sel_edges:
            e.seam = new_state

        bmesh.update_edit_mesh(me)
        return {'FINISHED'}


class MESH_OT_edge_crease(Operator):
    bl_idname = "mesh.quick_edge_crease"
    bl_label = "Edge Crease"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        # Call Blender's built-in operator so the user can interactively set the value
        bpy.ops.transform.edge_crease(value=-1)
        bpy.ops.transform.edge_crease('INVOKE_DEFAULT')
        return {'FINISHED'}


class MESH_OT_edge_bevel_weight(Operator):
    bl_idname = "mesh.quick_edge_bevel_weight"
    bl_label = "Edge Bevel Weight"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        # Call Blender's built-in operator so the user can interactively set the value
        bpy.ops.transform.edge_bevelweight(value=-1)
        bpy.ops.transform.edge_bevelweight('INVOKE_DEFAULT')
        return {'FINISHED'}





class MESH_OT_select_similar_edges(Operator):
    bl_idname = "mesh.select_similar_edges"
    bl_label = "Select Similar Edges"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        obj = context.object
        if not obj or obj.type != 'MESH':
            self.report({'WARNING'}, "Active object must be a mesh")
            return {'CANCELLED'}

        bm = bmesh.from_edit_mesh(obj.data)

        # Get layers
        crease_layer = bm.edges.layers.float.get('crease_edge', None)
        bevel_layer = bm.edges.layers.float.get('bevel_weight')
        if not bevel_layer:
            for name, layer in bm.edges.layers.float.items():
                if "bevel" in name.lower():
                    bevel_layer = layer
                    break

        sel_edges = [e for e in bm.edges if e.select]
        if not sel_edges:
            self.report({'INFO'}, "No edges selected")
            return {'CANCELLED'}

        # Sharp and Seam
        sharp_selected = any(not e.smooth for e in sel_edges)
        seam_selected = any(e.seam for e in sel_edges)

        # Crease strength values of selected edges
        crease_values = set()
        if crease_layer:
            for e in sel_edges:
                if e[crease_layer] > 0.0:
                    crease_values.add(e[crease_layer])

        # Bevel weight strength values of selected edges
        bevel_values = set()
        if bevel_layer:
            for e in sel_edges:
                if e[bevel_layer] > 0.0:
                    bevel_values.add(e[bevel_layer])

        # Loop over all edges and select matching ones
        for e in bm.edges:
            match = False

            # Sharp
            if sharp_selected and not e.smooth:
                match = True

            # Seam
            if seam_selected and e.seam:
                match = True

            # Crease strength
            if crease_layer and crease_values:
                if e[crease_layer] in crease_values:
                    match = True

            # Bevel weight strength
            if bevel_layer and bevel_values:
                if e[bevel_layer] in bevel_values:
                    match = True

            e.select = e.select or match

        bmesh.update_edit_mesh(obj.data)
        self.report({'INFO'}, "Selected similar edges based on Sharp/Seam/Crease/Bevel (strength sensitive)")
        return {'FINISHED'}




class MESH_OT_clear_edge_data(bpy.types.Operator):
    bl_idname = "mesh.clear_edge_data"
    bl_label = "Clear Edge Data"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        obj = context.object
        if obj is None or obj.type != 'MESH':
            self.report({'WARNING'}, "Active object must be a mesh")
            return {'CANCELLED'}

        me = obj.data
        bm = bmesh.from_edit_mesh(me)
        bm.edges.ensure_lookup_table()

        sel_edges = [e for e in bm.edges if e.select]
        if not sel_edges:
            self.report({'INFO'}, "No edges selected")
            return {'CANCELLED'}

        # Try to get existing crease and bevel layers safely
        crease_layer = bm.edges.layers.float.get('crease_edge', None)
        bevel_layer = bm.edges.layers.float.get('bevel_weight_edge', None)

        # Fallbacks for other possible naming variations
        if not crease_layer:
            for name, layer in bm.edges.layers.float.items():
                if "crease" in name.lower():
                    crease_layer = layer
                    break

        if not bevel_layer:
            for name, layer in bm.edges.layers.float.items():
                if "bevel" in name.lower():
                    bevel_layer = layer
                    break

        # Clear properties safely
        for e in sel_edges:
            e.smooth = True
            e.seam = False
            if crease_layer:
                e[crease_layer] = 0.0
            if bevel_layer:
                e[bevel_layer] = 0.0

        bmesh.update_edit_mesh(me)
        self.report({'INFO'}, "Cleared all edge data for selected edges")
        return {'FINISHED'}




# --- Pie Menu ---
class VIEW3D_MT_edit_mode_edge_pie(Menu):
    bl_label = "Edge Tools"

    def draw(self, context):
        layout = self.layout
        pie = layout.menu_pie()
        row = layout.row()
        
        obj = context.object
        bm = bmesh.from_edit_mesh(obj.data)
        sel_edges = [e for e in bm.edges if e.select]

        # Dynamic icons for toggles
        if sel_edges:
            sharp_icon = 'CHECKMARK' if all(not e.smooth for e in sel_edges) else 'SHARPCURVE'
            seam_icon = 'CHECKMARK' if all(e.seam for e in sel_edges) else 'EDGESEL'
        else:
            sharp_icon = 'SHARPCURVE'
            seam_icon = 'EDGESEL'

        # --- Pie slots ---
        
        pie.operator("mesh.quick_mark_sharp", text="Mark Sharp", icon=sharp_icon)        # slot 0
        
        pie.operator("mesh.quick_mark_seam", text="Mark Seam", icon=seam_icon)          # slot 1
        box = pie.split()
        column = box.column()

        row = column.row(align=True)
        row.scale_y = 1.2

        row.operator("mesh.clear_edge_data", text="Clear Edge Data", icon='X')
        row.operator("mesh.select_similar_edges", text="Select Similar", icon='X')

        pie.split()
        pie.operator("mesh.quick_edge_crease", text="Edge Crease", icon='IPO_CONSTANT') # slot 2
        pie.operator("mesh.quick_edge_bevel_weight", text="Bevel Weight", icon='MOD_BEVEL') # slot 4
    
        # --- Bottom vertical box for "Clear Edge Data" ---

        


                
        

# --- Register ---
classes = (
    MESH_OT_mark_sharp,
    MESH_OT_mark_seam,
    MESH_OT_edge_crease,
    MESH_OT_edge_bevel_weight,
    VIEW3D_MT_edit_mode_edge_pie,
    MESH_OT_clear_edge_data,
    MESH_OT_select_similar_edges,
)

# --- Live-updating Addon Preferences ---
class EdgeToolsPiePreferences(AddonPreferences):
    bl_idname = __name__

    def update_shortcut(self, context):
        unregister_keymap()
        register_keymap()

    pie_shortcut: bpy.props.StringProperty(
        name="Pie Menu Shortcut",
        description="Shortcut for Edge Tools Pie Menu",
        default="Q",
        update=update_shortcut
    )

    pie_ctrl: bpy.props.BoolProperty(
        name="Ctrl",
        description="Use Ctrl modifier",
        default=True,
        update=update_shortcut
    )

    pie_shift: bpy.props.BoolProperty(
        name="Shift",
        description="Use Shift modifier",
        default=False,
        update=update_shortcut
    )

    pie_alt: bpy.props.BoolProperty(
        name="Alt",
        description="Use Alt modifier",
        default=False,
        update=update_shortcut
    )

    def draw(self, context):
        layout = self.layout
        row = layout.row()
        row.prop(self, "pie_shortcut")
        row.prop(self, "pie_ctrl")
        row.prop(self, "pie_shift")
        row.prop(self, "pie_alt")

# --- Keymap management ---
addon_keymaps = []

def register_keymap():
    prefs = bpy.context.preferences.addons[__name__].preferences
    wm = bpy.context.window_manager
    kc = wm.keyconfigs.addon
    if kc:
        km = kc.keymaps.new(name='Mesh', space_type='EMPTY')
        kmi = km.keymap_items.new(
            idname='wm.call_menu_pie',
            type=prefs.pie_shortcut.upper(),
            value='PRESS',
            ctrl=prefs.pie_ctrl,
            shift=prefs.pie_shift,
            alt=prefs.pie_alt
        )
        kmi.properties.name = "VIEW3D_MT_edit_mode_edge_pie"
        addon_keymaps.append((km, kmi))

def unregister_keymap():
    for km, kmi in addon_keymaps:
        km.keymap_items.remove(kmi)
    addon_keymaps.clear()

# --- Register/unregister ---
def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.utils.register_class(EdgeToolsPiePreferences)
    register_keymap()

def unregister():
    unregister_keymap()
    bpy.utils.unregister_class(EdgeToolsPiePreferences)
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
