import bpy
from bpy.props import BoolProperty
from ... utils.objects import move_modifier_up, apply_modifier, only_select, set_active
from ... material import assign_material


class HOPS_OT_ComplexSplitBooleanOperator(bpy.types.Operator):
    bl_idname = "hops.complex_split_boolean"
    bl_label = "Complex Split Boolean"
    bl_options = {"REGISTER", "UNDO"}
    bl_description = "Split the primary mesh using the secondary mesh"

    split_mesh: BoolProperty(name="Split Mesh",
                             description="Separate the mesh after CSplit",
                             default=True)

    text = "Meshes Separated"
    op_tag = "(C)Slice"
    op_detail = "Meshes split"

    use_bmesh: BoolProperty(name="Use Bmesh Boolean",
                            description="Use new bmesh boolean",
                            default=False)

    @classmethod
    def poll(cls, context):
        if len(cls.get_cutter_objects()) == 0: return False
        if getattr(context.active_object, "type", "") != "MESH": return False
        if getattr(context.active_object, "mode", "") != "OBJECT": return False
        return getattr(context.active_object, "type", "") == "MESH"
        return True

    def draw(self, context):
        layout = self.layout

        layout.prop(self, "split_mesh")
        layout.prop(self, "use_bmesh")

    def execute(self, context):
        original_mesh_objects = [object for object in context.view_layer.objects if object.type == 'MESH' and object != context.active_object]
        target = context.active_object
        cutters = self.get_cutter_objects()
        for cutter in cutters:
            cutter.display_type = "WIRE"
            assign_material(context, cutter, csplit=True)
            self.split(target, cutter, self.use_bmesh)
        if self.split_mesh:
            self.separate_mesh_by_looseparts(target)
        split_objects = [obj for obj in [obj for obj in context.view_layer.objects if obj.type == 'MESH'] if obj not in original_mesh_objects]
        target = self.get_target_object(split_objects, target.data.vertices[:])
        split_objects.remove(target)
        for object in split_objects:
            assign_material(context, object, replace=True)
        only_select(cutters)
        return {'FINISHED'}

    def split(self, object, cutter, use_bmesh, margin=0.0001):
        if margin != 0:
            solidify = cutter.modifiers.new(name="temp", type="SOLIDIFY")
            solidify.thickness = margin
            solidify.offset = 1
        if not object.hops.is_pending_boolean:
            self.cut_object(object, cutter, "DIFFERENCE", use_bmesh)

        else:
            bpy.ops.hops.complex_sharpen()
        if margin != 0:
            cutter.modifiers.remove(solidify)

    @staticmethod
    def get_cutter_objects():
        selection = bpy.context.selected_objects
        active = bpy.context.active_object
        return [object for object in selection if object != active and object.type == "MESH"]

    @staticmethod
    def separate_mesh_by_looseparts(object):
        set_active(object)
        bpy.ops.object.mode_set(mode='EDIT')
        bpy.ops.mesh.separate(type='LOOSE')
        bpy.ops.object.mode_set(mode='OBJECT')

    @staticmethod
    def get_target_object(split_objects, target_verts):
        vert_differences = []
        for obj in split_objects:
            verts = [vert.co for vert in obj.data.vertices[:]]
            vert_difference = [abs(verts[vert].x - target_verts[vert].x) + abs(verts[vert].y - target_verts[vert].y) + abs(verts[vert].z - target_verts[vert].z) for vert in range(len(verts))]
            vert_differences.append(sum(vert_difference))
        return split_objects[vert_differences.index(min(vert_differences))]

    @staticmethod
    def cut_object(object, cutter, operation, use_bmesh):
        modifier = object.modifiers.new(name="temp", type="BOOLEAN")
        modifier.operation = operation

        modifier.object = cutter
        move_modifier_up(modifier)
        apply_modifier(modifier)
