import bpy
from pathlib import Path
from .. import core
from .. constants import PREFIX
from bpy.types import UIList, PropertyGroup, Menu
from bpy.props import (PointerProperty, 
                    IntProperty, 
                    BoolProperty,
                    StringProperty, 
                    EnumProperty, 
                    FloatProperty
                    )

class BPM_UL_bg_list(UIList):
    # order_by_distance : BoolProperty(default=True)

    show_items : EnumProperty(
        name="Show Items", description="Filter items to show, GP objects, Background or all",
        default='all', options={'HIDDEN'},
        items=(
            ('all', 'All', 'Show Background and Gp object', '', 0),
            ('obj', 'Gp Objects', 'Show only Gp object', '', 1),   
            ('bg', 'Backgrounds', 'Show only backgrounds', '', 2),   
            ))
        #(key, label, descr, id[, icon])

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
        # layout.alignment = 'CENTER'

        ## TODO: Find a better solution to get the collection
        ## Get Collection from plane name -> problem: prefix "BG_" and suffix
        # if not item.plane.get('is_background_holder'):
        if not item.plane.name.startswith(PREFIX): # (can check if has a parent using PREFIX)
            gp_ob = item.plane
            layout.prop(gp_ob, 'hide_viewport', text='', emboss=False, icon='HIDE_OFF')

            icon_col = layout.row()
            icon_col.label(text='', icon='OUTLINER_OB_GREASEPENCIL') # BLANK1
            icon_col.ui_units_x = context.scene.bg_props.ui_list_scale # 1.6

            row = layout.row()
            row.label(text=gp_ob.name)

            if gp_ob.data.users > 1:
                row.template_ID(item, "data")
            else:
                row.label(text='', icon='BLANK1')
            
            row.label(text='', icon='BLANK1')# <- add selection

            return

        layercol = context.view_layer.layer_collection.children['Background'].children.get(core.clean_image_name(item.plane.name[len(PREFIX):]))
        if not layercol:
            layout.label(text=f'{item.plane.name} (problem with name)', icon='ERROR')
            return

        # icon = 'HIDE_ON' if layercol.exclude  else 'HIDE_OFF'
        layout.prop(layercol, 'exclude', text='', emboss=False, icon='HIDE_OFF')
        
        if not item.plane.children:
            layout.label(text=f'{item.plane.name} (No children)', icon='ERROR')

        ## Image preview
        image = core.get_image(item.plane.children[0])
        # layout.label(icon_value=image.preview_ensure().icon_id)
        # layout.template_icon(icon_value=image.preview_ensure().icon_id)

        icon_col = layout.row()
        icon_col.template_icon(icon_value=image.preview_ensure().icon_id, scale=1.0)
        icon_col.ui_units_x = context.scene.bg_props.ui_list_scale # 1.6

        ## Name
        row = layout.row()
        row.enabled = not layercol.exclude

        # row.label(text=item.plane.name) # <- Object has BG_ prefix, trim or use collection name
        row.label(text=layercol.name)

        if context.scene.bg_props.show_distance:# and not layercol.exclude:
            row = layout.row()
            # row.enabled = not layercol.exclude
            row.prop(item.plane, '["distance"]', text='')
            # layout.prop(item.plane, 'location', index=2, text='') # , emboss=False

        ob = context.object
        if ob and ob.parent == item.plane:
            layout.label(text='', icon='DECORATE_LINKED') # select from prop group
        else:
            layout.label(text='', icon='BLANK1') # select from prop group
        
        if not layercol.exclude:
            icon = 'LAYER_ACTIVE' if item.plane.select_get() else 'LAYER_USED'
            layout.prop(item, 'select', icon=icon, text='', emboss=False) # select from prop group
        else:
            layout.label(text='', icon='BLANK1') # select from prop group

        

        ## note
        # You should always start your row layout by a label (icon + text), or a non-embossed text field,
        # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
        # We use icon_value of label, as our given icon is an integer value, not an enum ID.
        # Note "data" names should never be translated!

    def draw_filter(self, context, layout):
        row = layout.row()

        subrow = row.row(align=True)
        subrow.prop(self, "filter_name", text="") # Only show items matching this name (use ‘*’ as wildcard)
    
        # invert result
        # icon = 'ZOOM_OUT' if self.use_filter_invert else 'ZOOM_IN'
        # subrow.prop(self, "use_filter_invert", text="", icon=icon)

        # sort by name : ALPHA SORTING NOT WORKING, MUST CHANGE IN filter_items
        # subrow.prop(self, "use_filter_sort_alpha", text="", icon='SORTALPHA') # buit-in sort

        subrow.prop(self, "show_items", text="") # type enum filter # icon='DOWNARROW_HLT'

        # reverse order
        icon = 'SORT_DESC' if self.use_filter_sort_reverse else 'SORT_ASC'
        subrow.prop(self, "use_filter_sort_reverse", text="", icon=icon) # built-in reverse
 
    def filter_items(self, context, data, propname):
        helpers = bpy.types.UI_UL_list
        items = getattr(data, propname)
 
        filtered = []
        
        # ordered = [items[:].index(i) for i in sorted(items[:], key=lambda o: o.plane.location.z)]
        items = [(items[:].index(i), i) for i in items[:]]

        # needed ?
        filtered = [self.bitflag_filter_item] * len(items)

        ## Filter out out items thata
        for i, item in items:
            ## GP/BG Type filter
            if self.show_items != 'all':
                if self.show_items != item.type:
                    filtered[i] &= ~self.bitflag_filter_item
            ## Search filter
            if item.plane and self.filter_name.lower() not in item.plane.name.lower():
                filtered[i] &= ~self.bitflag_filter_item
            

        # if self.order_by_distance:
        cam = context.scene.objects.get('bg_cam') or context.scene.camera
        if not cam:
            return filtered, ordered
        
        ## Real Distance from bg_cam or active camera
        ordered = helpers.sort_items_helper(items, lambda o: (o[1].plane.matrix_world.to_translation() - cam.matrix_world.to_translation()).length)
        ## By distance attribute (only Backgrounds have distance attr)
        # ordered = helpers.sort_items_helper(items, lambda o: o[1].plane.get('distance'))

        return filtered, ordered

'''
class BPM_UL_bg_list(UIList):
    # order_by_distance : BoolProperty(default=True)

    def draw_item(self, context, layout, data, item, icon, active_data, active_propname):
        # draw_item must handle the three layout types... Usually 'DEFAULT' and 'COMPACT' can share the same code.
        # layout.alignment = 'CENTER'

        ## TODO: Find a better solution to get the collection
        ## Get Collection from plane name -> problem: prefix "BG_" and suffix

        layercol = context.view_layer.layer_collection.children['Background'].children.get(core.clean_image_name(item.plane.name[len(PREFIX):]))
        if not layercol:
            layout.label(text=f'{item.plane.name} (problem with name)', icon='ERROR')
            return

        # icon = 'HIDE_ON' if layercol.exclude  else 'HIDE_OFF'
        layout.prop(layercol, 'exclude', text='', emboss=False, icon='HIDE_OFF')
        
        if not item.plane.children:
            layout.label(text=f'{item.plane.name} (No children)', icon='ERROR')

        ## Image preview
        image = core.get_image(item.plane.children[0])
        # layout.label(icon_value=image.preview_ensure().icon_id)
        # layout.template_icon(icon_value=image.preview_ensure().icon_id)

        icon_col = layout.row()
        icon_col.template_icon(icon_value=image.preview_ensure().icon_id, scale=1.0)
        icon_col.ui_units_x = context.scene.bg_props.ui_list_scale # 1.6


        ## Name
        row = layout.row()
        row.enabled = not layercol.exclude

        # row.label(text=item.plane.name) # <- Object has BG_ prefix, trim or use collection name
        row.label(text=layercol.name)

        ob = context.object
        if ob and ob.parent == item.plane:
            layout.label(text='', icon='DECORATE_LINKED') # select from prop group
        else:
            layout.label(text='', icon='BLANK1') # select from prop group
        
        if not layercol.exclude:
            icon = 'LAYER_ACTIVE' if item.plane.select_get() else 'LAYER_USED'
            layout.prop(item, 'select', icon=icon, text='', emboss=False) # select from prop group
        else:
            layout.label(text='', icon='BLANK1') # select from prop group

        if context.scene.bg_props.show_distance:# and not layercol.exclude:
            row = layout.row()
            # row.enabled = not layercol.exclude
            row.prop(item.plane, '["distance"]', text='')
            # layout.prop(item.plane, 'location', index=2, text='') # , emboss=False
        

        ## note
        # You should always start your row layout by a label (icon + text), or a non-embossed text field,
        # this will also make the row easily selectable in the list! The later also enables ctrl-click rename.
        # We use icon_value of label, as our given icon is an integer value, not an enum ID.
        # Note "data" names should never be translated!

 
    def filter_items(self, context, data, propname):
        helpers = bpy.types.UI_UL_list
        items = getattr(data, propname)
 
        filtered = []
        
        # ordered = [items[:].index(i) for i in sorted(items[:], key=lambda o: o.plane.location.z)]
        items = [(items[:].index(i), i) for i in items[:]]

        # needed ?
        filtered = [self.bitflag_filter_item] * len(items)

        for i, item in items:
            if item.plane and self.filter_name.lower() not in item.plane.name.lower():
                filtered[i] &= ~self.bitflag_filter_item
            #else:


        # if self.order_by_distance:
        
        ordered = helpers.sort_items_helper(items, lambda o: o[1].plane.get('distance'))

        return filtered, ordered

    # def draw_filter(self, context, layout):
    #     """UI code for the filtering/sorting/search area."""
    #     col = layout.column(align=True)        
    #     row = col.row(align=True)
        
    #     # row.prop(self, 'order_by_distance', text='', icon='DRIVER_DISTANCE')
    #     # row.prop(self, 'use_filter_invert', text='', icon='ARROW_LEFTRIGHT')
'''

### --- updates

def get_plane_targets(context):
    selection = [i.plane for i in context.scene.bg_props.planes if i.plane.select_get()]
    if selection:
        return selection
    ## active (even not selected)
    return [context.scene.bg_props.planes[context.scene.bg_props.index].plane]

def update_opacity(self, context):
    pool = get_plane_targets(context)
    for ob in pool:
        if not ob or not ob.children:
            continue
        core.set_opacity(ob.children[0], opacity=context.scene.bg_props.opacity)

def update_on_index_change(self, context):
    # print('index change:', context.scene.bg_props.index)
    props = context.scene.bg_props
    planes_list = props.planes
    item = planes_list[props.index]
    if not item.plane:
        ## remove slot !
        print(f'bg_props.planes: No plane/object at index {props.index}, removing item')
        planes_list.remove(props.index)
        return

    if item.type == 'bg':
        plane = item.plane
        if not plane.children:
            return
        opacity = core.get_opacity(plane.children[0])
        if not opacity:
            return
        props['opacity'] = opacity
    
    elif item.type == 'obj':
        core.gp_transfer_mode(item.plane, context)


def update_select(self, context):
    # print('index change:', context.scene.bg_props.index)
    plane = self.plane
    plane.select_set(not plane.select_get()) 
    context.scene.bg_props['index'] = context.scene.bg_props.planes[:].index(self)

class BPM_more_option(Menu):
    bl_idname = "BPM_MT_more_option"
    bl_label = "Options"

    def draw(self, context):
        layout = self.layout
        layout.operator("bpm.reload_list", text="Refresh", icon="FILE_REFRESH")
        layout.operator("bpm.open_bg_folder", icon="FILE_FOLDER")
        layout.operator("bpm.import_bg_images", text="Import Planes", icon="IMPORT")
        layout.operator("bpm.convert_planes", text="Convert Planes", icon="OUTLINER_OB_IMAGE")
        layout.separator()
        layout.prop(context.scene.bg_props, 'show_distance', icon='DRIVER_DISTANCE')
        layout.prop(context.scene.bg_props, 'ui_list_scale')

# Create sniptool property group
class BPM_bg_list_prop(PropertyGroup):
    plane : PointerProperty(type=bpy.types.Object)
    select: BoolProperty(update=update_select) # use and update to set the plane selection
    type: StringProperty(default='bg')
    # is_bg: BoolProperty(default=True)


class BPM_bg_settings(PropertyGroup):
    index : IntProperty(update=update_on_index_change)
    planes : bpy.props.CollectionProperty(type=BPM_bg_list_prop)
    show_distance : BoolProperty(name='Show Distance', default=False)
    opacity : FloatProperty(name='Opacity', default=1.0, min=0.0, max=1.0, update=update_opacity)
    move_hided : BoolProperty(name='Move hided', default=True)
    ui_list_scale : FloatProperty(name='UI Item Y Scale', default=1.6, min=0.6, soft_min=1.0, max=2.0)
    # distance : FloatProperty(name='Distance', default=0.0, update=update_distance)


### --- REGISTER ---

classes=(
# prop and UIlist
BPM_bg_list_prop,
BPM_bg_settings,
BPM_UL_bg_list,
BPM_more_option,
)


def register():
    for cls in classes:
        bpy.utils.register_class(cls)
    bpy.types.Scene.bg_props = bpy.props.PointerProperty(type=BPM_bg_settings)

def unregister():
    for cls in reversed(classes):
        bpy.utils.unregister_class(cls)
    del bpy.types.Scene.bg_props