import bpy
from bpy.props import EnumProperty, IntProperty, BoolProperty, StringProperty
from bpy.types import Operator, Menu
from ..constants import PICKERS, SHADERS
from ..core.addon_utils import get_picker_collection
from ..core.bl_utils import get_view_3d_override, split_path, eval_attr
from ..core.geometry_utils import bounding_rect
from ..core.picker import get_picker_path, pack_picker, unpack_picker, load_picker_data
#from .func_bgl import draw_callback_px
#from .func_bgl import select_bone
#from core.picker import *
#from .utils import is_over_region
import gpu
from mathutils import Vector, Euler, Matrix
from gpu_extras.batch import batch_for_shader
from pathlib import Path
import json
import os





def is_picker_space(space_data=None):
    if not space_data:
        space_data = bpy.context.space_data
    if space_data and (space_data.type == 'NODE_EDITOR' and space_data.tree_type == 'RigPickerTree'):
        return True
    
    return False


class RP_OT_box_select(Operator):
    """Box Select bones in the picker view"""
    bl_idname = "node.rp_box_select"
    bl_label = "Picker Box Select"

    mode: EnumProperty(items=[(i, i.title(), '') for i in ('SET', 'EXTEND', 'SUBSTRACT')])

    @classmethod
    def poll(cls, context):
        if not is_picker_space(context.space_data):
            return
            
        ob = context.object
        return ob and ob in PICKERS

    '''
    def mode_from_event(self, event):
        if event.alt:
            return 'SUBSTRACT'
        elif event.ctrl or event.shift:
            return 'EXTEND'
        else:
            return 'SET'
    '''

    def draw_callback(self):
        #print('draw callback border')
        if not self.draw_border:
            return

        gpu.state.blend_set('ALPHA')

        #print('DRAW BORDER')
        
        self.color_shader.bind()
        self.color_shader.uniform_float("color", self.bg_color)
        self.bg_batch.draw(self.color_shader)

        self.dash_shader.bind()
        matrix = gpu.matrix.get_projection_matrix()

        self.dash_shader.uniform_float("color", self.border_color)
        self.dash_shader.uniform_float("viewMatrix", matrix)
        self.dash_shader.uniform_float("dashSize", 5)
        self.dash_shader.uniform_float("gapSize", 4)
        
        self.contour_batch.draw(self.dash_shader)

        gpu.state.blend_set('NONE')

    def invoke(self, context, event):
        #print(f'invoke: type={event.type}, value={event.value}, ctrl={event.ctrl}, shift={event.shift}, alt={event.alt}')

        if context.object.mode != 'POSE':
            bpy.ops.object.posemode_toggle()

        self.timer = None
        #self.mode = self.mode_from_event(event)
        #self.invoke_event = event.copy()
        self.region = context.region
        self.draw_border = False

        self.picker = PICKERS[context.object]

        self.start_mouse = event.mouse_region_x, event.mouse_region_y
        #self.shader = line_strip_shader
        self.border_color = [1, 1, 1, 1]
        self.bg_color = [1, 1, 1, 0.05]

        #args = (self, context)
        self.color_shader = gpu.shader.from_builtin('UNIFORM_COLOR')
        self.dash_shader = SHADERS['dashed_line']
        self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
        
        context.window_manager.modal_handler_add(self)

        self.picker.press_event(self.mode)
        self.region.tag_redraw()

        return {'RUNNING_MODAL'}
    
    def modal(self, context, event):

        self.mouse = event.mouse_region_x, event.mouse_region_y

        self.border = bounding_rect((self.start_mouse, self.mouse))

        self.bg_batch = batch_for_shader(self.color_shader, 'TRI_FAN', {"pos": self.border})
        self.contour_batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": self.border})

        self.draw_border = True

        self.region.tag_redraw()

        if event.value == 'RELEASE':
            return self.release_event(context)
                
        return {'RUNNING_MODAL'}

    def release_event(self, context):

        if get_picker_collection():
            self.picker.assign_bone_event()

        elif (self.start_mouse[0] != self.mouse[0] or self.start_mouse[1] != self.mouse[1]):
            self.picker.border_select(self.border, self.mode)
        else:
            self.picker.move_event(self.mouse)
            self.picker.release_event(self.mode)
        
        bpy.ops.ed.undo_push(message="Box Select")
        
        return self.exit(context)

    def exit(self, context):
        bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
        context.region.tag_redraw()
        return {'FINISHED'}


class RP_OT_picker_transform(Operator):
    """Transform Bones in the picker view"""
    bl_idname = "node.picker_transform"
    bl_label = "Transform Bone in Picker View"
    
    mode : EnumProperty(items=[(m, m.title(), '') for m in ('ROTATE', 'SCALE')])

    @classmethod
    def poll(cls, context):
        return context.selected_pose_bones and is_picker_space(context.space_data)

    '''
    def draw_callback(self):
        gpu.state.blend_set('ALPHA')
        gpu.state.line_width_set(2)

        self.dash_shader.bind()
        matrix = gpu.matrix.get_projection_matrix()

        self.dash_shader.uniform_float("color", [0, 0, 0, 1])
        self.dash_shader.uniform_float("viewMatrix", matrix)
        self.dash_shader.uniform_float("dashSize", 5)
        self.dash_shader.uniform_float("gapSize", 4)

        self.batch = batch_for_shader(self.dash_shader, 'LINE_LOOP', {"pos": [self.view_center, self.mouse]})
        self.batch.draw(self.dash_shader)

        gpu.state.line_width_set(1)
        gpu.state.blend_set('NONE')
        '''

    def invoke(self, context, event):
        self.override = get_view_3d_override()

        self.view_center = Vector((int(context.region.width / 2), int(context.region.height / 2)))
        self.mouse_start = Vector((event.mouse_region_x, event.mouse_region_y))
        self.mouse = Vector((0, 0))

        transform_type = context.scene.tool_settings.transform_pivot_point
        self.center = context.active_pose_bone.matrix.to_translation()
        if transform_type == 'MEDIAN_POINT':
            origins = [b.matrix.to_translation() for b in context.selected_pose_bones]
            self.center = sum(origins, Vector()) / len(context.selected_pose_bones)
        # self.bone_data = {}
        # for bone in context.selected_pose_bones:
        #     self.bone_data[bone] = bone.matrix.copy()
        self.bone_data = {b: b.matrix.copy() for b in context.selected_pose_bones}

        space = self.override['area'].spaces.active

        view_matrix = space.region_3d.view_matrix
        if space.region_3d.view_perspective == 'CAMERA':
            view_matrix = context.scene.camera.matrix_world

        context.window.cursor_modal_set('MOVE_X')
        self.view_vector = Vector((0, 0, 1))
        if self.mode == 'ROTATE':
            self.view_vector.rotate(view_matrix)
            

        elif self.mode == 'SCALE':
            self.view_vector = Vector((0, 0, 0))
        
        self.transform_type = "VIEW"
        self.transform_types = ["VIEW", 'GLOBAL', 'LOCAL']
        if context.scene.transform_orientation_slots[0].type == 'LOCAL':
            self.transform_types = ["VIEW", 'LOCAL', 'GLOBAL']

        self.transform_orientation = context.scene.transform_orientation_slots[0].type

        #self.dash_shader = SHADERS['dashed_line']
        

        #self._handle = bpy.types.SpaceNodeEditor.draw_handler_add(self.draw_callback, (), 'WINDOW', 'POST_PIXEL')
        context.window_manager.modal_handler_add(self)

        return {'RUNNING_MODAL'}

    def change_transform_type(self):
        indices = {0: 1, 1:2, 2:1}
        index = self.transform_types.index(self.transform_type)
        new_index = indices[index]
        self.transform_type = self.transform_types[new_index]

    def release_event(self, context):
        scn = bpy.context.scene

        # Insert keyframe        
        #bpy.ops.ed.undo_push(message="Transform")
        if scn.tool_settings.use_keyframe_insert_auto:
            try:
                bpy.ops.animtoolbox.insert_keyframe()
            except Exception:
                self.report({"WARNING"}, 'You need animtoolbox addon for inserting keyframe')

        self.exit(context)

    def exit(self, context):
        #bpy.types.SpaceNodeEditor.draw_handler_remove(self._handle, 'WINDOW')
        context.window.cursor_modal_restore()
        #context.region.tag_redraw()
        
    def modal(self, context, event):
        scn = context.scene
        self.mouse = Vector((event.mouse_region_x, event.mouse_region_y))

        if self.mode == 'ROTATE':
            delta = (event.mouse_region_x - self.mouse_start[0]) / 100
        elif self.mode == 'SCALE':
            delta = 1 + (event.mouse_region_x - self.mouse_start[0]) / 100 
            #delta = (self.mouse - self.view_center).length / (self.mouse_start - self.view_center).length


        transform_type = self.transform_type
        #self.batch = batch_for_shader(self.dash_shader, 'LINE_STRIP', {"pos": [self.mouse_start, self.mouse]})
        #context.area.tag_redraw()
        
        #print(event.type, event.value)
        if self.mode == 'ROTATE' and event.type == "R" and event.value == 'PRESS':
            with context.temp_override(**self.override):
                self.exit(context)
                bpy.ops.transform.trackball('INVOKE_DEFAULT')
            return {'FINISHED'}

        if event.type == "LEFTMOUSE" and event.value == 'RELEASE':
            self.release_event(context)
            return {'FINISHED'}

        if event.type in {"RIGHTMOUSE", "ESC"}:
            for bone, matrix in self.bone_data.items():
                bone.matrix = matrix
            self.exit(context)
            return {'CANCELLED'}

        elif event.type == "X" and event.value == 'PRESS':
            self.change_transform_type()
            self.view_vector = Vector((1, 0, 0))
            #transform_type = self.transform_orientation

        elif event.type == "Y" and event.value == 'PRESS':
            self.change_transform_type()
            self.view_vector = Vector((0, 1, 0))
            #transform_type = self.transform_orientation

        elif event.type == "Z" and event.value == 'PRESS':
            self.change_transform_type()
            self.view_vector = Vector((0, 0, 1))
            #transform_type = self.transform_orientation

        elif event.type == "MOUSEMOVE":
            
            if self.mode == 'ROTATE':
                transform_matrix = Matrix.Rotation(delta, 4, self.view_vector)
            elif self.mode == 'SCALE':
                if self.view_vector.length:
                    transform_matrix = Matrix.Scale(delta, 4, self.view_vector)
                else:
                    transform_matrix = Matrix.Scale(delta, 4)

            for bone, matrix in self.bone_data.items():
                center = self.center
                if scn.tool_settings.transform_pivot_point == "INDIVIDUAL_ORIGINS":
                    center = matrix.to_translation()

                if self.mode == 'ROTATE':
                    if transform_type == 'LOCAL':
                        view_vector = self.view_vector.copy()
                        view_vector.rotate(matrix)
                        transform_matrix = Matrix.Rotation(delta, 4, view_vector)

                elif self.mode == 'SCALE':                    
                    if transform_type == 'LOCAL':
                        view_vector = self.view_vector.copy()
                        view_vector.rotate(matrix)
                        transform_matrix = Matrix.Scale(delta, 4, view_vector)

                mat = matrix.copy()
                mat.translation -= center

                mat = transform_matrix @ mat

                mat.translation += center

                bone.matrix = mat

        return {'RUNNING_MODAL'}


class RP_OT_toogle_property(Operator):
    """Invert a bone custom property"""

    bl_idname = "rigpicker.toogle_property"
    bl_label = 'Toogle Property'

    data_path : StringProperty()

    @classmethod
    def poll(cls, context):
        return is_picker_space(context.space_data)

    def execute(self,context):
        ob = context.object
        
        try:
            value = ob.path_resolve(self.data_path)
        except Exception:
            return {'CANCELLED'}

        data = ob
        prop_name = self.data_path
        #if '.' in self.data_path:
        #    data, prop = prop_name.rsplit('.', 1)
        
        value = type(value)(not value)
        exec(f'{repr(ob)}.{self.data_path} = {value}')

        #setattr(data, prop_name, value)
        bpy.ops.object.mode_set(mode='OBJECT')
        bpy.ops.object.mode_set(mode='POSE')

        if context.scene.tool_settings.use_keyframe_insert_auto:
            bone_name, _ = split_path(self.data_path)
            ob.keyframe_insert(data_path=self.data_path, group=bone_name)

        context.area.tag_redraw()

        return {'FINISHED'}


class RP_OT_reload_picker(Operator):
    """Reload the picker shapes"""

    bl_idname = "rigpicker.reload_picker"
    bl_label = 'Reload Picker'

    #@classmethod
    #def poll(cls, context):
    #    if not is_picker_space(context):
    #        return

    def execute(self, context):
        if context.object.type == 'ARMATURE':
            rig = context.object
        else:
            collection = get_picker_collection(context.object)
            rig = collection.rig_picker.rig

        #print('Reload', rig)
        load_picker_data(rig)

        for area in context.screen.areas:
            if is_picker_space(area.spaces.active):
                area.tag_redraw()
 

        return {"FINISHED"}


class RP_OT_toogle_bone_layer(Operator):
    """Toogle bone layer visibility when double clicking on a bone"""
    bl_idname = "rigpicker.toogle_bone_layer"
    bl_label = 'Toogle Bone Layer'

    @classmethod
    def poll(cls, context):
        if not is_picker_space(context.space_data):
            return

        ob = context.object
        picker = PICKERS.get(ob)

        if picker.hover_shape and picker.hover_shape.type == 'bone':
            return True

    def execute(self, context):
        ob = context.object
        picker = PICKERS.get(ob)

        bone = picker.hover_shape.bone
        hide = picker.hover_shape.hide

        if bone:
            for layer in bone.bone.collections:
                layer.is_visible = hide

        context.region.tag_redraw()

        return {"FINISHED"}


class RP_OT_context_menu_picker(Operator):
    """Display Menu with the custom properties of the hovered bone if any or the active bone"""
    bl_idname = "node.context_menu_picker"
    bl_label = 'Context Menu Picker'

    @classmethod
    def poll(cls, context):
        return is_picker_space(context.space_data)

    def execute(self, context):
        bpy.ops.wm.call_menu(name='RP_MT_context_menu')

        return {"FINISHED"}


class RP_OT_pack_picker(Operator):
    """Pack Unpack the picker on the rig"""
    bl_idname = "rigpicker.pack_picker"
    bl_label = 'Pack Picker'

    @classmethod
    def poll(cls, context):
        ob = context.object
        return (ob and ob.type == 'ARMATURE' and ob.data.rig_picker.sources)

    def execute(self, context):
        print('Pack Pickers...')
        
        rig = context.object

        if 'pickers' in rig.data.rig_picker.keys():
            unpack_picker(rig)
            self.report({"INFO"}, f'The picker is unpacked')
            return {"FINISHED"}

        pack_picker(rig)

        if not rig.data.rig_picker['pickers']:
            self.report({"ERROR"}, f'No picker have been packed')
            return {"CANCELLED"}    

        elif len(rig.data.rig_picker['pickers']) < len(rig.data.rig_picker.sources):
            self.report({"WARNING"}, f'No all pickers have been packed')
            return {"FINISHED"}    

        self.report({"INFO"}, f'The picker is packed')

        return {"FINISHED"}


class RP_OT_call_operator(Operator):
    bl_idname = "rigpicker.call_operator"
    bl_label = 'Call operator'

    operator : StringProperty()
    arguments : StringProperty()
    invoke: BoolProperty()
    view_3d : BoolProperty()

    @classmethod
    def poll(cls, context):
        return is_picker_space(context.space_data)

    @classmethod
    def description(cls, context, properties):
        try:
            op = eval_attr(bpy.ops, properties.operator).get_rna_type()
            return op.description
        except AttributeError:
            return 'Call operator'

    def execute(self, context):        
        #context.area.tag_redraw()

        override = {}
        if self.view_3d:
            override = get_view_3d_override()
        
        arguments = json.loads(self.arguments or "{}")

        with context.temp_override(**override):
            try:
                ops = eval_attr(bpy.ops, self.operator)
                if self.invoke:
                    ops('INVOKE_DEFAULT', **arguments)
                else:
                    ops(**arguments)

            except Exception as e:
                print(e)
                self.report({"ERROR"}, f'The operator {self.operator} cannot be called')
                return {"CANCELLED"}
        
        context.area.tag_redraw()

        return {"FINISHED"}


class RP_MT_context_menu(Menu):
    bl_label = "Context Menu"

    # Set the menu operators and draw functions
    def draw(self, context):
        layout = self.layout
        col = layout.column()
        col.operator_context = 'INVOKE_DEFAULT'
        #col.use_property_split = True

        ob = context.object
        picker = PICKERS.get(ob)

        if picker.hover_shape and picker.hover_shape.type == 'bone':
            bone = picker.hover_shape.bone
        else:
            bone = context.active_pose_bone

        # Draw Space Switch Operator
        if getattr(ob.data, 'space_switch'):
            space_switch = ob.data.space_switch

            data_paths = [f'pose.bones["{bone.name}"]["{k}"]' for k in bone.keys()]
            space_bone = next((s for s in space_switch.bones if s.data_path in data_paths), None)
            if space_bone:

                index = list(space_switch.bones).index(space_bone)
                value = ob.path_resolve(space_bone.data_path)
                space = next((s.name for s in space_bone.spaces if s.value == value), None)

                op = col.operator("spaceswitch.change_space", text=f'({space})', icon='PINNED')
                op.index=index
            col.separator()

        if bone:    
            for key in bone.keys():
                col.prop(bone,f'["{key}"]', slider=True)

        #layout.operator("rigpicker.show_bone_layer", text="Show Bone Layer", ).type = 'ACTIVE'
        #layout.operator("rigidbody.objects_add", text="B Add Passive").type = 'PASSIVE'


class RP_OT_add_picker_collection(Operator):
    bl_idname = "rigpicker.add_picker_collection"
    bl_label = "Add a Picker Collection"
    bl_description = "Add a Picker Collection"
    bl_options = {'UNDO', 'REGISTER'}

    @classmethod
    def poll(cls, context):
        ob = context.object
        return (ob and ob.type == 'ARMATURE')

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

        name = ob.name
        if name.endswith('_rig'):
            name = name[:-4]

        canvas_name = f'canevas_{name}'
        canvas_points = [(-0.5, 0.5, 0), (0.5, 0.5, 0), (0.5, -0.5, 0), (-0.5, -0.5, 0)]
        canvas_faces = [(0, 1, 2, 3)]
        
        canvas_mesh = bpy.data.meshes.new(canvas_name)
        canvas_mesh.from_pydata(canvas_points, [], canvas_faces)
        canvas_mesh.update(calc_edges=True)

        canvas_ob = bpy.data.objects.new(canvas_name, canvas_mesh)
        canvas_ob.rig_picker.shape_type = 'DISPLAY'

        col = bpy.data.collections.new(f'Picker {name}')
        col.rig_picker.enabled = True
        col.rig_picker.rig = ob
        col.rig_picker.canvas = canvas_ob

        col.objects.link(canvas_ob)

        scn.collection.children.link(col)

        self.report({"INFO"}, f"New Picker Collection {col.name} Created")

        return {'FINISHED'}


class RP_OT_add_picker_source(Operator):
    bl_idname = "rigpicker.add_picker_source"
    bl_label = "Add a Picker source"
    bl_description = "Add a Picker source"
    bl_options = {'UNDO', 'REGISTER'}

    def execute(self, context):
        arm = context.object.data
        arm.rig_picker.sources.add()

        return {'FINISHED'}


class RP_OT_remove_picker_source(Operator):
    bl_idname = "rigpicker.remove_picker_source"
    bl_label = "Delete a Picker source"
    bl_description = "Delete a Picker source"
    bl_options = {'UNDO', 'REGISTER'}

    index : IntProperty(default=-1)

    def execute(self, context):
        arm = context.object.data
        arm.rig_picker.sources.remove(self.index)

        return {'FINISHED'}


class RP_OT_fit_picker(Operator):
    bl_idname = "rigpicker.fit_picker"
    bl_label = "Fit Picker"
    bl_description = "Fit Picker in 2d view"
    bl_options = {'UNDO', 'REGISTER'}

    index : IntProperty()

    @classmethod
    def poll(cls, context):
        return is_picker_space(context.space_data) and PICKERS.get(context.object)

    def execute(self, context):
        ob = context.object
        picker_group = PICKERS[ob]

        if self.index >= len(picker_group.pickers):
            return {"CANCELLED"}

        picker = picker_group.pickers[self.index]
        view2d = context.region.view2d

        if self.index == -1:
            #print('Picker Group')
            picker = picker_group

        view_rect = [view2d.view_to_region(*co, clip=False) for co in picker.rect]
        bpy.ops.view2d.zoom_border(
                xmin=round(view_rect[3][0]), xmax=round(view_rect[1][0]), 
                ymin=round(view_rect[3][1]), ymax=round(view_rect[1][1]), 
                wait_for_input=False, zoom_out=False
        )

        region_center = Vector((context.region.width * 0.5, context.region.height * 0.5))
        view_center = view2d.region_to_view(*region_center)

        view_offset = Vector(view_center) + (picker.center - Vector(view_center))
        region_offset = view2d.view_to_region(*view_offset, clip=False)

        delta = Vector(region_offset) - region_center

        bpy.ops.view2d.pan(deltax=round(delta[0]), deltay=round(delta[1]))

        return {'FINISHED'}


keymaps = []
def register_keymaps():
    wm = bpy.context.window_manager

    km = wm.keyconfigs.addon.keymaps.new(name="Node Editor", space_type="NODE_EDITOR")

    for i in range(10):
        kmi = km.keymap_items.new("rigpicker.fit_picker", type=f"NUMPAD_{i}", value="PRESS")
        kmi.properties.index = i - 1
        keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="NUMPAD_PERIOD", value="PRESS")
    kmi.properties.operator = "view3d.view_selected"
    kmi.properties.view_3d = True
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS")
    kmi.properties.operator = "pose.select_all"
    kmi.properties.arguments = '{"action": "SELECT"}'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="A", value="PRESS", alt=True)
    kmi.properties.operator = "pose.select_all"
    kmi.properties.arguments = '{"action": "DESELECT"}'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.toogle_bone_layer", type="LEFTMOUSE", value="DOUBLE_CLICK")
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="X", value="PRESS")
    kmi.properties.operator = "animtoolbox.reset_bone"
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="K", value="PRESS")
    kmi.properties.operator = "animtoolbox.insert_keyframe"
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("anim.keyframe_delete_v3d", type="K", value="PRESS", alt=True)
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS")
    kmi.properties.operator = 'transform.translate'
    kmi.properties.view_3d = True
    kmi.properties.invoke = True
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="G", value="PRESS", alt=True)
    kmi.properties.operator = 'pose.loc_clear'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("node.picker_transform", type="R", value="PRESS")
    kmi.properties.mode = 'ROTATE'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="R", value="PRESS", alt=True)
    kmi.properties.operator = 'pose.rot_clear'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("node.picker_transform", type="S", value="PRESS")
    kmi.properties.mode = 'SCALE'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("rigpicker.call_operator", type="S", value="PRESS", alt=True)
    kmi.properties.operator = 'pose.scale_clear'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("node.context_menu_picker", type="RIGHTMOUSE", value="PRESS")
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS")
    kmi.properties.mode = 'SET'
    keymaps.append((km, kmi))

    kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", shift=True)
    kmi.properties.mode = 'EXTEND'
    keymaps.append((km, kmi))

    #kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", ctrl=True)
    kmi = km.keymap_items.new("node.rp_box_select", type="LEFTMOUSE", value="PRESS", alt=True)
    kmi.properties.mode = 'SUBSTRACT'
    keymaps.append((km, kmi))

    #km = wm.keyconfigs.addon.keymaps.new(name="View2D")
    #kmi = km.keymap_items.new("rigpicker.call_operator", type="MIDDLEMOUSE", value="PRESS")
    #kmi.properties.operator = "bpy.ops.view2d.pan('INVOKE_DEFAULT')"
    #keymaps.append((km, kmi))


def unregister_keymaps():
    for km, kmi in keymaps:
        km.keymap_items.remove(kmi)
    keymaps.clear()

classes = (
    RP_OT_box_select,
    RP_OT_toogle_property,
    RP_OT_reload_picker,
    RP_OT_toogle_bone_layer,
    RP_OT_call_operator,
    RP_MT_context_menu,
    RP_OT_picker_transform,
    RP_OT_context_menu_picker,
    RP_OT_pack_picker,
    RP_OT_add_picker_source,
    RP_OT_remove_picker_source,
    RP_OT_fit_picker,
    RP_OT_add_picker_collection
    #RP_OT_ui_draw
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    
    register_keymaps()


def unregister():
    unregister_keymaps()
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)