import bpy
import bmesh
import os
from ..cAPI import SimulationAPI
from .. import utils
from ..utils import create_mesh_constraint
from ..utils import getSelectedPoints
import numpy as np

class HI_PHY_OT_CreateMeshConstraint(bpy.types.Operator):
    bl_idname = "hi_phy.create_mesh_constraint"
    bl_label = "Hi Phy Create Mesh Constraint"
    bl_options = {'REGISTER', 'UNDO_GROUPED'}

    binding_simulation_object: bpy.props.StringProperty(name="Binding Simulation Object")
    binding_mesh: bpy.props.StringProperty(name="Binding Mesh")

    binding_option: bpy.props.EnumProperty(
        name="Binding Option",
        items=[
            ('OP_UseSelectedPoints', "Use Selected Points", "Use Selected Points"),
            ('OP_UsePointsWithinRadius', "Use Points Within Radius", "Use Points Within Radius"),
        ],
        default='OP_UseSelectedPoints'
    )

    auto_selection_radius: bpy.props.FloatProperty(
        default=0.01,
        name="auto selection radius",
        precision=5
    )

    def invoke(self, context, event):
        for obj in bpy.context.selected_objects:
            if utils.isSimulationObject(obj):
                self.binding_simulation_object = obj.name
            else:
                self.binding_mesh = obj.name
        if (bpy.context.object.mode == 'OBJECT'):
            self.binding_option = 'OP_UsePointsWithinRadius'
        else:
            self.binding_option = 'OP_UseSelectedPoints'
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "binding_simulation_object")
        layout.prop(self, "binding_mesh")
        layout.prop(self, "binding_option")
        if (self.binding_option == 'OP_UsePointsWithinRadius') :
            layout.prop(self, "auto_selection_radius")

    def execute(self, context):
        depsgraph = bpy.context.evaluated_depsgraph_get()
        # evaluate it so we can get the correct topology after modifiers
        sim_obj = bpy.data.objects[self.binding_simulation_object].evaluated_get(depsgraph)
        mesh_obj = bpy.data.objects[self.binding_mesh].evaluated_get(depsgraph)
        if (self.binding_option == 'OP_UseSelectedPoints'):
            selected_indices = getSelectedPoints(sim_obj, self)
            if not selected_indices:
                return {'CANCELLED'}
            selection_radius = 1e10; # some large value that sqr won't exceed float limit, but also large enough to cover the whole scene
        else:
            # if no point selected, we use every point
            selected_indices = [i for i in range(sim_obj.data.attributes.domain_size('POINT'))]
            selection_radius = self.auto_selection_radius
        # we can't get position attributes from mesh unless we are in object mode
        bk_mode = bpy.context.object.mode
        bpy.ops.object.mode_set(mode='OBJECT')
        if not create_mesh_constraint.create(sim_obj, mesh_obj, selected_indices, selection_radius, sim_obj.name + "_to_" + mesh_obj.name + "_mesh_constraint", self):
            bpy.ops.object.mode_set(mode=bk_mode)
            return {'CANCELLED'}
        bpy.ops.object.mode_set(mode=bk_mode)
        return {'FINISHED'}

__CLASSES__ = [
    HI_PHY_OT_CreateMeshConstraint
]

# Define the menu draw function
def menu_draw(self, context):
    # TODO: only add to the menu if the edited object is a valid simulation object
    layout = self.layout
    layout.operator_context = 'INVOKE_DEFAULT'
    layout.operator("hi_phy.create_mesh_constraint")

def register():
    for class_ in __CLASSES__:
        bpy.utils.register_class(class_)


def unregister():
    for class_ in reversed(__CLASSES__):
        bpy.utils.unregister_class(class_)
