
import json
from os.path import abspath
from pathlib import Path
import bpy
from bpy.types import Operator
from bpy.props import IntProperty
from mathutils import Matrix

from ..core.shape import get_picker_data
from ..core.addon_utils import is_shape, get_picker_collection
from ..core.bl_utils import link_mat_to_object, flip_name


class RP_OT_create_shape(Operator):
    bl_label = 'Create UI shape'
    bl_idname = 'rigpicker.create_shape'
    #bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return (context.object and context.object.mode == 'POSE')
    
    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)
    
    def draw(self, context):
        for col in bpy.data.collections:
            if col.rig_picker.enabled:
                self.layout.prop(col.rig_picker, 'link_shape', text=col.name)

    def execute(self,context):
        scn = context.scene
        vl = context.view_layer

        offset = 0
        for bone in context.selected_pose_bones:
            name = bone.name

            mesh = bpy.data.meshes.new(name)
            ob = bpy.data.objects.new(name, mesh)

            ob.rig_picker.name = name

            verts=[(0,0,0)]
            edges = []
            faces =[]

            mesh.from_pydata(verts, edges, faces)

            picker_selected_cols = [col for col in bpy.data.collections if col.rig_picker.link_shape]

            for col in picker_selected_cols or [scn.collection]:
                col.objects.link(ob)

            ob.location.z = 0.05
            ob.location.x = offset

            for obj in scn.objects:
                obj.select_set(False)

            vl.objects.active = ob
            ob.select_set(True)

            offset += 0.05

        return {'FINISHED'}


class RP_OT_name_from_bone(Operator):
    bl_label = 'Name Shape from selected bones'
    bl_idname = 'rigpicker.name_from_bone'
    bl_description = 'Rename all shapes from related bones name'
    #bl_options = {'REGISTER', 'UNDO'}

    def execute(self,context):

        col = get_picker_collection(context.object)

        for ob in col.all_objects:
            if ob.rig_picker.shape_type == 'BONE':
                ob.name = ob.rig_picker.name

        return {'FINISHED'}


class RP_OT_mirror_shape(Operator):
    bl_label = 'Mirror UI shape'
    bl_idname = 'rigpicker.mirror_shape'
    #bl_options = g

    @classmethod
    def poll(cls, context):
        return (context.object and context.object.type in ('MESH', 'CURVE', 'TEXT'))

    def execute(self,context):
        collection = get_picker_collection(context.object)
        objects = context.selected_objects

        # Remove mirror object:
        for ob in list(collection.all_objects):
            if ob in objects:
                continue

            for mod in ob.modifiers:
                if (mod.type == 'NODES' and mod.node_group and mod.node_group.name == 'Symmetrize' and
                        mod.get('Socket_2') in objects):
                
                    bpy.data.objects.remove(ob)

        if collection.rig_picker.symmetry:
            x_axis = collection.rig_picker.symmetry.matrix_world.to_translation()[0]
        else:
            x_axis = 0

        for ob in objects:
            flipped_name = flip_name(ob.name)
            if flipped_name == ob.name:
                suffix = '.L'
                flipped_suffix = '.R'
                # Determine side of the object
                if ob.matrix_world.to_translation()[0] < x_axis:
                    suffix, flipped_suffix = flipped_suffix, suffix
                
                flipped_name = f'{ob.name}{flipped_suffix}'
                ob.name = f'{ob.name}{suffix}'

            flipped_object = ob.copy()
            flipped_object.name = flipped_name
            flipped_object.parent = None
            flipped_object.matrix_world = Matrix()
            for mod in list(flipped_object.modifiers):
                flipped_object.modifiers.remove(mod)

            # Add symmetrize modifier TODO add it into a resource
            mod = flipped_object.modifiers.new(name='Symmetrize', type='NODES')
            mod.node_group = bpy.data.node_groups['Symmetrize']
            mod['Socket_2'] = ob
            mod['Socket_3'] = collection.rig_picker.symmetry

            for col in ob.users_collection:
                col.objects.link(flipped_object)

            if ob.rig_picker.shape_type == 'BONE':
                flipped_object.rig_picker.name = flip_name(ob.rig_picker.name)

            elif ob.rig_picker.shape_type == 'FUNCTION':
                args = {}
                for key,value in eval(ob.rig_picker.arguments).items():
                    if type(value) == list:
                        mirrored_value = []
                        for item in value:
                            mirrored_value.append(flip_name(item))

                    elif type(value) == str:
                        mirrored_value = flip_name(value)
                    args[key] = mirrored_value

                flipped_object.rig_picker.arguments = str(args)

        return {'FINISHED'}


class RP_OT_select_shape_type(Operator):
    bl_label = 'Select Shape by Type'
    bl_idname = 'rigpicker.select_shape_type'
    #bl_options = {'REGISTER', 'UNDO'}

    shape_type = bpy.props.EnumProperty(items =[(i.upper(), i, '') for i in ('Bone', 'Display', 'Function')])

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

        col = layout.column()
        col.prop(self,'shape_type', expand=True)

    def execute(self,context):
        scene = context.scene
        #print(self.shape_type)
        canvas = context.scene.rig_picker.canvas
        if canvas:
            for ob in [o for o in bpy.data.objects if o.layers==canvas.layers]:
                if ob.type in ['MESH', 'CURVE', 'FONT'] and ob.rig_picker.shape_type == self.shape_type:
                    ob.select = True

        return {'FINISHED'}

    def invoke(self, context, event):
        wm = context.window_manager
        return wm.invoke_props_dialog(self, width=150)


class RP_OT_save_picker(Operator):
    bl_label = 'Store UI Data'
    bl_idname = 'rigpicker.save_picker'

    index : IntProperty(default=-1)

    def execute(self, context):
        scn = context.scene
        ob = context.object

        if self.index == -1:
            collection = get_picker_collection(ob)
        else:
            source = context.active_object.data.rig_picker.sources[self.index].source
            source = Path(bpy.path.abspath(source, library=ob.data.library)).resolve()
            collection = next((c for c in set(scn.collection.children_recursive) if c.rig_picker.enabled 
                    and Path(bpy.path.abspath(c.rig_picker.destination)).resolve() == source), None)
            
            if not collection:
                self.report({"ERROR"}, 'No Picker found')
                return {'CANCELLED'}

        canvas = collection.rig_picker.canvas
        #rig = collection.rig_picker.rig
        shapes = [o for o in collection.all_objects if o != canvas and o.type in ('MESH', 'CURVE', 'FONT')]

        if not canvas:
            self.report({'ERROR'}, 'Choose a Canvas')
            return {'CANCELLED'}


        picker_data = get_picker_data(collection)

        picker_path = Path(bpy.path.abspath(collection.rig_picker.destination))

        print(f'Save Picker to {picker_path}')
        picker_path.write_text(json.dumps(picker_data))

        bpy.ops.rigpicker.reload_picker()

        return {'FINISHED'}

classes = (
    RP_OT_create_shape,
    RP_OT_name_from_bone,
    RP_OT_mirror_shape,
    RP_OT_select_shape_type,
    RP_OT_save_picker
)

register, unregister = bpy.utils.register_classes_factory(classes)