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

class HI_PHY_OT_CreateJointConstraint(bpy.types.Operator):
    bl_idname = "hi_phy.create_joint_constraint"
    bl_label = "Hi Phy Create Joint Constraint"
    bl_options = {'REGISTER'}

    binding_simulation_object_1: bpy.props.StringProperty(name="Affine Body 1")
    binding_simulation_object_2: bpy.props.StringProperty(name="Affine Body 2")
    joint_location: bpy.props.FloatVectorProperty(name="Joint Location")
    binding_option: bpy.props.EnumProperty(
        name="Binding Option",
        items=[
            ('OP_JointTwoBodies', "Create a Joint Between Two Affine Bodies", "Create a Joint Between Two Affine Bodies"),
            ('OP_JointOneBody', "Create a Joint On A Single Affine Body", "Create a Joint On A Single Affine Body"),
        ],
        default='OP_JointOneBody')
    constraint_mode: bpy.props.EnumProperty(
        name="Constraint Mode",
        items=[
            ('OP_JointConstraintFull', "Constraint in space", "The constrained point will not be able to move"),
            ('OP_JointConstraintLine', "Constraint on line", "Allow the the constrained point to slide along the constraint direction"),
            ('OP_JointConstraintPlane', "Constraint in plane", "Allow the the constrained point to be kept in plain perpendicular to the constraint normal"),
        ],
        default='OP_JointConstraintFull')
    constraint_direction: bpy.props.FloatVectorProperty(name="Direction", default=(0., 1., 0.))
    constraint_direction_space1: bpy.props.EnumProperty(
        name="Constraint Direction Space",
        items=[
            ('OP_JointConstraintDirectionSpaceConstraint', "Constraint Space", "The constrained direction is in the constraint space"),
            ('OP_JointConstraintDirectionSpaceObject1', "Object 1 Space", "The constrained direction is in the object 1 space"),
        ],
        default='OP_JointConstraintDirectionSpaceObject1')
    constraint_direction_space: bpy.props.EnumProperty(
        name="Constraint Direction Space",
        items=[
            ('OP_JointConstraintDirectionSpaceConstraint', "Constraint Space", "The constrained direction is in the constraint space"),
            ('OP_JointConstraintDirectionSpaceObject1', "Object 1 Space", "The constrained direction is in the object 1 space"),
            ('OP_JointConstraintDirectionSpaceObject2', "Object 2 Space", "The constrained direction is in the object 2 space"),
        ],
        default='OP_JointConstraintDirectionSpaceObject1')

    def invoke(self, context, event):
        self.binding_simulation_object_1 = ""
        self.binding_simulation_object_2 = ""
        for obj in bpy.context.selected_objects:
            if utils.isSimulationObject(obj) and self.binding_simulation_object_1 == "":
                self.binding_simulation_object_1 = obj.name
            elif utils.isSimulationObject(obj):
                self.binding_simulation_object_2 = obj.name
        if (self.binding_simulation_object_2):
            self.binding_option = 'OP_JointTwoBodies'
        else:
            self.binding_option = 'OP_JointOneBody'
        self.joint_location = bpy.context.scene.cursor.location
        return context.window_manager.invoke_props_dialog(self, width=600)

    def draw(self, context):
        layout = self.layout
        layout.prop(self, "binding_option")
        layout.prop(self, "binding_simulation_object_1")
        if (self.binding_option == 'OP_JointTwoBodies') :
            layout.prop(self, "binding_simulation_object_2")
            # layout.prop(self, "constraint_direction_space")
        layout.prop(self, "joint_location")
        # Two body joint does not support line or plane constraint yet.
        if (self.binding_option == 'OP_JointOneBody') :
            layout.prop(self, "constraint_mode")
            if (self.constraint_mode == 'OP_JointConstraintLine') :
                layout.prop(self, "constraint_direction_space1", text = "Constraint Direction Space")
                layout.prop(self, "constraint_direction")
            if (self.constraint_mode == 'OP_JointConstraintPlane') :
                layout.prop(self, "constraint_direction_space1", text = "Constraint Normal Space")
                layout.prop(self, "constraint_direction", text = "Normal")

    def execute(self, context):
        depsgraph = bpy.context.evaluated_depsgraph_get()
        # evaluate it so we can get the correct topology after modifiers
        sim_obj_1 = bpy.data.objects[self.binding_simulation_object_1].evaluated_get(depsgraph)
        name = self.binding_simulation_object_1
        sim_obj_2 = None
        if (self.binding_option == 'OP_JointTwoBodies') :
            if self.binding_simulation_object_2 != "" and self.binding_simulation_object_2 != self.binding_simulation_object_1:
                sim_obj_2 = bpy.data.objects[self.binding_simulation_object_2].evaluated_get(depsgraph)
                name += "_" + self.binding_simulation_object_2;
            else:
                self.report({"ERROR"}, "Please choose a vaid 2nd affine body to be bond")
                return {'CANCELLED'}
            if (self.constraint_direction_space == 'OP_JointConstraintDirectionSpaceConstraint') :
                constraint_direction_space = SimulationAPI.ConstraintDirectionSpace.ConstraintDirectionSpaceConstraint
            elif (self.constraint_direction_space == 'OP_JointConstraintDirectionSpaceObject1') :
                constraint_direction_space = SimulationAPI.ConstraintDirectionSpace.ConstraintDirectionSpaceObject1
            elif (self.constraint_direction_space == 'OP_JointConstraintDirectionSpaceObject2') :
                constraint_direction_space = SimulationAPI.ConstraintDirectionSpace.ConstraintDirectionSpaceObject2
            else:
                self.report({"ERROR"}, "Unknown constraint direction space " + self.constraint_direction_space)
                return {'CANCELLED'}
        else:
            if (self.constraint_direction_space1 == 'OP_JointConstraintDirectionSpaceConstraint') :
                constraint_direction_space = SimulationAPI.ConstraintDirectionSpace.ConstraintDirectionSpaceConstraint
            elif (self.constraint_direction_space1 == 'OP_JointConstraintDirectionSpaceObject1') :
                constraint_direction_space = SimulationAPI.ConstraintDirectionSpace.ConstraintDirectionSpaceObject1

        if (self.constraint_mode == 'OP_JointConstraintFull') :
            constraint_order = 3
        elif (self.constraint_mode == 'OP_JointConstraintLine') :
            constraint_order = 2
        elif (self.constraint_mode == 'OP_JointConstraintPlane') :
            constraint_order = 1
        else:
            self.report({"ERROR"}, "Unknown constraint mode " + self.constraint_mode)
            return {'CANCELLED'}

        # 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')
        create_joint_constraint.createJointConstraint(sim_obj_1, sim_obj_2, self.joint_location, constraint_order, self.constraint_direction, constraint_direction_space, name + "_joint_constraint", self);
        bpy.ops.object.mode_set(mode=bk_mode)
        return {'FINISHED'}

__CLASSES__ = [
    HI_PHY_OT_CreateJointConstraint
]

# 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_joint_constraint")

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


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