# Copyright (C) 2023 Thomas Hoppe (h0bB1T). All rights reserved.
#
# Unauthorized copying of this file via any medium is strictly prohibited.
# Proprietary and confidential.

import bpy, traceback

from enum import Enum

from bpy.types import Operator, UILayout
from bpy.props import StringProperty, BoolProperty

from ..preferences import PreferencesPanel
from ..utils.blender import is_material_assignable
from ..registries.asset_registry import AssetEntry
from ..utils.asset_loader import load_material


class MaterialImportMode(Enum):
    CURRENT = 'current'
    SELECTED = 'selected'
    CURRENT_SLOT = 'current_slot'


class ASSET_OT_import_3d_material(Operator):
    """
    Import material and apply to object(s).
    """
    bl_idname = 'awp.import_3d_material'
    bl_label = ''
    bl_description = ''
    bl_options = {'REGISTER', 'UNDO'}

    reuse: BoolProperty() # type: ignore
    do_link: BoolProperty() # type: ignore
    filename: StringProperty() # type: ignore
    asset: StringProperty() # type: ignore
    desc: StringProperty() # type: ignore
    mode: StringProperty() # type: ignore


    def __assign_material(self, obj: bpy.types.Object, mat: bpy.types.Material):
        """
        Assign material to object. Create slot if none, otherweise assign to all slots.
        """
        if is_material_assignable(obj):
            if len(obj.material_slots) > 0:
                for s in obj.material_slots:
                    s.material = mat
            else:
                obj.data.materials.append(mat)

    @classmethod
    def description(self, context, properties):
        return {
                MaterialImportMode.CURRENT.value: 'Assign this Material to the current Object',
                MaterialImportMode.SELECTED.value: 'Assign this Material to all selected Objects',
                MaterialImportMode.CURRENT_SLOT.value: 'Assign this Material to the current Objects active Material Slot'
            }.get(properties.mode, 'ERROR')


    def execute(self, context: bpy.types.Context):
        try:
            # Load material, logic is in the loader.
            mat = load_material(
                self.filename, 
                self.asset, 
                self.do_link, 
                self.reuse,
                PreferencesPanel.get().import_relative_paths
            )
            raw_mode = MaterialImportMode[self.mode.upper()]

            # Validate if all conditions are met (in one place).
            if ASSET_OT_import_3d_material.mode_valid(raw_mode, context):
                # It seems so .. so we just act on mode.
                active = context.active_object
                if raw_mode == MaterialImportMode.CURRENT:
                    self.__assign_material(active, mat)
                elif raw_mode == MaterialImportMode.SELECTED:
                    assign_to = [ o for o in context.selected_editable_objects if is_material_assignable(o) ]
                    for o in assign_to:
                        self.__assign_material(o, mat)
                else: # MaterialImportMode.CURRENT_SLOT
                    active.material_slots[active.active_material_index].material = mat

            return {'FINISHED'}
        except Exception as ex:
            print(traceback.format_exc())
            self.report({'ERROR'}, f'Asset loading failed, see Console ({ex})')
            return {'FINISHED'}
   

    @staticmethod
    def mode_valid(mode: MaterialImportMode, context: bpy.types.Context) -> bool:
        """
        Returns true if all things are correct for the given mode.
        """
        active = context.active_object
        if mode == MaterialImportMode.CURRENT and is_material_assignable(active): return True
        if mode == MaterialImportMode.SELECTED and len([ o for o in context.selected_editable_objects if is_material_assignable(o) ]) > 0: return True
        if mode == MaterialImportMode.CURRENT_SLOT and is_material_assignable(active) and len(active.material_slots) > 0: return True
        return False


    @staticmethod
    def create_ui(l: UILayout, asset: AssetEntry, link: bool, reuse: bool, mode: MaterialImportMode):
        op = l.operator(
            ASSET_OT_import_3d_material.bl_idname, 
            icon = {
                MaterialImportMode.CURRENT: 'MESH_PLANE',
                MaterialImportMode.SELECTED: 'SELECT_SET',
                MaterialImportMode.CURRENT_SLOT: 'ALIGN_MIDDLE'
            }.get(mode, 'ERROR')
        ) # type: ASSET_OT_import_3d_material
        op.reuse = reuse
        op.do_link = link        
        op.filename = asset.blend
        op.asset = asset.name
        op.desc = asset.description
        op.mode = mode.value
