
import bpy
import shutil
from pathlib import Path
import os

import bpy.utils.previews
from bpy.types import PropertyGroup, AddonPreferences
from bpy.props import StringProperty, BoolProperty, CollectionProperty, \
FloatProperty, IntProperty, PointerProperty, EnumProperty, FloatVectorProperty


from bone_widget import ctx
from .ui import update_tab, draw_prefs
from .transform_utils import transform_mesh
from .shape_utils import symmetrize_bone_shape, get_flipped_bone


def undo_redo(self, context):
    wm = context.window_manager
    op_names = ('transform_widget','create_widget', 'match_transform', 'clean_widget')
    op_ids = [ctx.get_op_id(o) for o in op_names]

    if (ops and ops[-1].bl_idname in op_ids):
        bpy.ops.wm.undo()
        bpy.ops.wm.redo()

def transform_widgets(self, context):
    #bones = []
    shapes = ctx.shapes
    for shape, coords in shapes.items():
        #bones.append(ctx.get_bone(shape))
        #print(shape)
        transform_mesh(shape.data, self.loc, self.rot, self.scale, 
        self.xz_scale, self.size, self.slide, coords=coords)
    
    if ctx.prefs.auto_symmetrize:
        for bone in ctx.selected_bones:
            flipped_bone = get_flipped_bone(bone)
            if flipped_bone and flipped_bone.custom_shape in shapes:
                continue

            symmetrize_bone_shape(bone, prefix=ctx.prefs.prefix)
    

class BW_PG_transforms(PropertyGroup):
    size: FloatProperty(name='Size', default=1.0, min=0, update=transform_widgets)
    xz_scale: FloatProperty(name='XZ Scale', default=1.0, min=0, update=transform_widgets)
    slide: FloatProperty(name='Slide', default=0.0, update=transform_widgets)

    loc: FloatVectorProperty(name='Location', default=(0.0, 0.0, 0.0), 
    subtype='TRANSLATION', update=transform_widgets)

    rot: FloatVectorProperty(name = 'Rotation', default=(0.0, 0.0, 0.0), 
    subtype='EULER', step=100, precision=0, update=transform_widgets)

    scale: FloatVectorProperty(name='Scale', default=(1.0, 1.0, 1.0), 
    subtype='XYZ', update=transform_widgets)


def rename_widget(self, context):
    ctx.active_folder.rename_widget(self, self.name)

class BW_PG_widget(PropertyGroup):
    name: StringProperty(update=rename_widget)
    source_bone: StringProperty()
    #path: StringProperty(subtype='FILE_PATH')

def refresh(self, context):
    bpy.ops.bonewidget.refresh_folders()

class BW_PG_folder(PropertyGroup):
    #icons = bpy.utils.previews.new()
    path: StringProperty(subtype='FILE_PATH', default='Default', update=refresh)
    expand: BoolProperty()
    widgets: CollectionProperty(type=BW_PG_widget)
    widget_index: IntProperty()

    @property
    def icons(self):
        prefs = ctx.prefs
        return prefs.get_folder_previews(self.as_pointer())

    @property
    def abspath(self):
        return ctx.abspath(self.path)

    @property
    def name(self):
        return Path(self.path).name

    def load_icon(self, widget):

        icon_name = self.get_icon_name(widget)
        icon = self.icons.get(icon_name)
        if icon:
            icon.reload()
        else:
            icon_path = self.get_icon_path(widget)
            self.icons.load(icon_name, str(icon_path), 'IMAGE', True)

    def get_icon_name(self, widget):
        return f'{Path(self.path).stem}_{widget.name}'

    def get_widget_icon(self, widget):
        return self.icons.get(self.get_icon_name(widget))

    def add_widget(self, name):
        name = self.get_widget_display_name(name)
        w = self.widgets.get(name)
        if w:
            self.load_icon(w)
            self.active_widget = w
            return

        w = self.widgets.add()
        w.name = name

        self.active_widget = w
        self.load_icon(w)

        return w

    def remove_widget(self, widget):
        index = self.widgets[:].index(widget)

        widget_path = self.get_widget_path(widget)
        icon_path = self.get_icon_path(widget)

        if widget_path.exists():
            os.remove(widget_path)
        if icon_path.exists():
            os.remove(icon_path)

        self.widgets.remove(index)
        self.widget_index = min(len(self.widgets)-1, index)

    @property
    def active_widget(self):
        if self.widgets and self.widget_index < len(self.widgets):
            return self.widgets[self.widget_index]

    @active_widget.setter
    def active_widget(self, widget):
        self.widget_index = list(self.widgets).index(widget) 

    def get_widget_clean_name(self, widget):
        name = widget if isinstance(widget, str) else widget.name
        name = name.split('-', 1)[-1]
        return bpy.path.clean_name(name).lower()

    def get_widget_display_name(self, widget):
        name = widget if isinstance(widget, str) else widget.name
        return name.replace('_', ' ').title()

    def get_widget_path(self, widget):
        name = self.get_widget_clean_name(widget)
        return (self.abspath/name).with_suffix('.blend')

    def get_icon_path(self, widget):
        name = self.get_widget_clean_name(widget)
        return (self.abspath/name).with_suffix('.png')

    def rename_widget(self, widget, name):
        if not widget.get('_name'):
            widget['_name'] = name
            return

        src_blend = self.get_widget_path(widget['_name'])
        dst_blend = self.get_widget_path(name)

        src_icon = self.get_icon_path(widget['_name'])
        dst_icon = self.get_icon_path(name)

        widget['_name'] = name

        if not src_blend:
            return

        if not src_blend.exists():
            self.remove_widget(widget)

        if src_blend != dst_blend:
            os.rename(str(src_blend), str(dst_blend))

        if src_icon != dst_icon:
            os.rename(str(src_icon), str(dst_icon))

        self.load_icon(widget)

    def load_widgets(self):
        self.widgets.clear()
        for widget_blend in self.abspath.glob('*.blend'):
            self.add_widget(widget_blend.stem)
    
#class BW_PG_settings(PropertyGroup):
#    pass




class BW_PG_bone_color(PropertyGroup):
    value: FloatVectorProperty(name='Color', subtype='COLOR', default=[0.0, 0.0, 0.0])
    name: StringProperty(name='Name')


class BW_prefs(AddonPreferences):
    bl_idname = __package__

    previews = {}

    default_folder: PointerProperty(type=BW_PG_folder)
    folders: CollectionProperty(type=BW_PG_folder)
    folder_index: IntProperty()
    folder_enum: EnumProperty(name='Folders', items=lambda s,c: ctx.folder_items)
    collection: StringProperty(name='Collection', default='Widgets', description='Name of the widget collection use {ob.name} to include the name of the rig')
    use_custom_collection: BoolProperty(name='Custom Collection', default=False, description='Allow to pick a collection from the scene')

    prefix: StringProperty(name='Prefix', default='WGT-', description='Prefix for the shape object and data name')
    category: StringProperty(name='Tab', default='Rigging', update=lambda s,c: update_tab())
    
    auto_symmetrize: BoolProperty(name='Symmetrize', default=True, update=undo_redo)
    auto_separate: BoolProperty(default=True, name="Separate", update=undo_redo)
    auto_match_transform: BoolProperty(default=True, name="Match Transform", update=undo_redo)
    auto_rename: BoolProperty(default=True, name="Rename", update=undo_redo)

    show_transforms: BoolProperty(default=False)
    show_preferences: BoolProperty(default=False)
    transforms: PointerProperty(type=BW_PG_transforms)

    #grid_view: BoolProperty(name='Grid View', default=True)

    #use_custom_colors: BoolProperty(name='Custom Colors', default=False, update=set_default_colors)

    def get_folder_previews(self, adress):
        if adress not in self.previews:
            self.previews[adress] = bpy.utils.previews.new()
        
        return self.previews[adress]

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

classes = (
    BW_PG_bone_color,
    BW_PG_widget,
    BW_PG_transforms,
    BW_PG_folder,
    BW_prefs
)




def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    
    bpy.types.Armature.widget_collection = PointerProperty(type=bpy.types.Collection)

    #update_folder_items()


def unregister():
    #bpy.utils.previews.remove(ctx.prefs.default_folder.icons)
    #for f in ctx.folders:
    #    bpy.utils.previews.remove(f.icons)

    del bpy.types.Armature.widget_collection

    for cls in classes:
        bpy.utils.unregister_class(cls)