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

from typing import Union
import bpy

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


class UI_OT_support_assign_material(Operator):
    """
    Depending on current mode, assign given material (by name)
    to either selected objects(object mode) or faces(edit mode).
    In case of objects, all slots of the objects are removed
    and replaced.
    In case of faces, the material is eventually added (if not present)
    and assigned to all faces.
    """
    bl_idname = 'snw.assign_material'
    bl_label = 'Assign Material'
    bl_options = {'REGISTER', 'UNDO'}


    material_name: StringProperty() # type: ignore
    source_name: StringProperty() # type: ignore


    def __material_by_full_name(self, name: str) -> Union[bpy.types.Material, None]:
        for m in bpy.data.materials:
            if m.name_full == name:
                return m


    @classmethod
    def description(cls, context: bpy.types.Context, properties):
        if properties.source_name:
            return 'Start Replacement'
        else:
            if context.mode == 'OBJECT':
                return 'Assign Material to all selected Objects'
            else:
                return 'Assign Material to all selected Faces'


    def execute(self, context: bpy.types.Context):
        if not self.source_name: # Replace mode?
            # Nope, just set.
            material = None
            if not self.material_name:
                material = bpy.data.materials.new(name='Material')
                material.use_nodes = True
            else:
                material = self.__material_by_full_name(self.material_name)
                if not material:
                    self.report({'ERROR'}, 'Cant find Material')
            
            if material:
                valid_objects = [ o for o in context.selected_editable_objects if o.type in { 'MESH', 'CURVE' } ]
                if context.mode == 'OBJECT':
                    # Just remove all and set the requested.
                    for m in valid_objects:
                        m.data.materials.clear()
                        m.data.materials.append(material)
                elif context.mode.startswith('EDIT_'): # EDIT_MESH, EDIT_CURVE
                    for m in valid_objects:
                        # Check if this material is already is in one slot ..
                        found_index = -1
                        for i, sl in enumerate(m.material_slots):
                            if sl.material == material:
                                found_index = i
                                break

                        if found_index >= 0:
                            # Yes, set as active slot.
                            m.active_material_index = found_index
                        else:
                            # Nope, add material and set active index to last entry.
                            m.data.materials.append(material)
                            m.active_material_index = len(m.material_slots) - 1

                        # Assign to all selected faces. 
                        bpy.ops.object.material_slot_assign()

        else:
            replace_material = self.__material_by_full_name(self.material_name)
            if not replace_material:
                self.report({'ERROR'}, 'Cant find Replace Material')
            source_material = self.__material_by_full_name(self.source_name)
            if not source_material:
                self.report({'ERROR'}, 'Cant find Source Material')

            if replace_material and source_material:
                valid_objects = [ o for o in context.selected_editable_objects if o.type in { 'MESH', 'CURVE' } ]
                m: bpy.types.Object
                for m in valid_objects:
                    ms: bpy.types.MaterialSlot
                    for ms in m.material_slots:
                        if ms.material == source_material:
                            ms.material = replace_material

        return {'FINISHED'}


    @staticmethod
    def create_ui(l: UILayout, text: str, icon: str, icon_id: int, material: bpy.types.Material):
        if icon:
            op = l.operator(UI_OT_support_assign_material.bl_idname, text=text, icon=icon) # type: UI_OT_support_assign_material
        else:
            op = l.operator(UI_OT_support_assign_material.bl_idname, text=text, icon_value=icon_id) # type: UI_OT_support_assign_material
        op.material_name = material.name_full if material else ''
        op.source_name = ''


    @staticmethod
    def create_ui_replace(l: UILayout, text: str, icon: str, source_material: str, replace_material: str):
        op = l.operator(UI_OT_support_assign_material.bl_idname, text=text, icon=icon) # type: UI_OT_support_assign_material
        op.material_name = replace_material
        op.source_name = source_material
