import bpy, os, time, blf, webbrowser, platform, numpy, bmesh
import math, subprocess, multiprocessing
from .. utility import utility
from .. utility import build
from .. utility.cycles import cache
from .. network import server

def setObjectLightmapByWeight(minimumRes, maximumRes, objWeight):
        
        availableResolutions = [32,64,128,256,512,1024,2048,4096,8192]
        
        minRes = minimumRes
        minResIdx = availableResolutions.index(minRes)
        maxRes = maximumRes
        maxResIdx = availableResolutions.index(maxRes)
        
        exampleWeight = objWeight
        
        if minResIdx == maxResIdx:
            pass
        else:
        
            increment = 1.0/(maxResIdx-minResIdx)
            
            assortedRange = []
            
            for a in numpy.arange(0.0, 1.0, increment):
                assortedRange.append(round(a, 2))
                
            assortedRange.append(1.0)
            nearestWeight = min(assortedRange, key=lambda x:abs(x - exampleWeight))
            return (availableResolutions[assortedRange.index(nearestWeight) + minResIdx])

class TLM_BuildLightmaps(bpy.types.Operator):
    bl_idname = "tlm.build_lightmaps"
    bl_label = "Build Lightmaps"
    bl_description = "Build Lightmaps"
    bl_options = {'REGISTER', 'UNDO'}

    def modal(self, context, event):

        #Add progress bar from 0.15

        print("MODAL")

        return {'PASS_THROUGH'}

    def invoke(self, context, event):

        if not bpy.app.background:

            build.prepare_build(self, False)

        else:

            print("Running in background mode. Contextual operator not available. Use command 'thelightmapper.addon.build.prepare_build()'")

        return {'RUNNING_MODAL'}

    def cancel(self, context):
        pass

    def draw_callback_px(self, context, event):
        pass

class TLM_CleanLightmaps(bpy.types.Operator):
    bl_idname = "tlm.clean_lightmaps"
    bl_label = "Clean Lightmaps"
    bl_description = "Clean Lightmaps"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        filepath = bpy.data.filepath
        dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)

        if not bpy.context.scene.TLM_SceneProperties.tlm_keep_baked_files:
            if os.path.isdir(dirpath):
                for file in os.listdir(dirpath):
                    os.remove(os.path.join(dirpath + "/" + file))

        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
                    cache.backup_material_restore(obj)

        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
                    cache.backup_material_rename(obj)

        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
                    for vertex_layer in obj.data.vertex_colors:
                        if vertex_layer.name == "TLM":
                            obj.data.vertex_colors.remove(vertex_layer)

        for mat in bpy.data.materials:
            if mat.users < 1:
                bpy.data.materials.remove(mat)

        for mat in bpy.data.materials:
            if mat.name.startswith("."):
                if "_Original" in mat.name:
                    bpy.data.materials.remove(mat)

        for image in bpy.data.images:
            if image.name.endswith("_baked"):
                bpy.data.images.remove(image, do_unlink=True)

        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:
                    if obj.TLM_ObjectProperties.tlm_postpack_object:

                        atlas = obj.TLM_ObjectProperties.tlm_postatlas_pointer
                        atlas_resize = False

                        for atlasgroup in scene.TLM_PostAtlasList:
                            if atlasgroup.name == atlas:
                                atlas_resize = True

                        if atlas_resize:

                            bpy.ops.object.select_all(action='DESELECT')
                            obj.select_set(True)
                            bpy.context.view_layer.objects.active = obj

                            uv_layers = obj.data.uv_layers

                            if not obj.TLM_ObjectProperties.tlm_use_default_channel:
                                uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
                            else:
                                uv_channel = "UVMap_Lightmap"
                            
                            for i in range(0, len(uv_layers)):
                                if uv_layers[i].name == uv_channel:
                                    uv_layers.active_index = i
                                    break

                            bpy.ops.object.mode_set(mode='EDIT')
                            bpy.ops.mesh.select_all(action='SELECT')
                            bpy.ops.uv.select_all(action='SELECT')
                            bpy.ops.uv.pack_islands(rotate=False, margin=0.001)
                            bpy.ops.uv.select_all(action='DESELECT')
                            bpy.ops.mesh.select_all(action='DESELECT')
                            bpy.ops.object.mode_set(mode='OBJECT')

                            if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
                                print("Resized for obj: " + obj.name)

                    if "Lightmap" in obj:
                        del obj["Lightmap"]

        if bpy.context.scene.TLM_SceneProperties.tlm_repartition_on_clean:

            mats = bpy.data.materials
            
            for obj in bpy.context.scene.objects:
                if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        print("Repartitioning materials")

                        for slt in obj.material_slots:
                            print("Repartitioning material: " + str(slt.name))
                            part = slt.name.rpartition('.')
                            if part[2].isnumeric() and part[0] in mats:
                                slt.material = mats.get(part[0])

                        for slt in obj.material_slots:
                            if slt.name.endswith(tuple(["001","002","003","004","005","006","007","008","009"])): #Do regex instead
                                if not slt.name[:-4] in mats:
                                    slt.material.name = slt.name[:-4]

        return {'FINISHED'}

class TLM_ExploreLightmaps(bpy.types.Operator):
    bl_idname = "tlm.explore_lightmaps"
    bl_label = "Explore Lightmaps"
    bl_description = "Explore Lightmaps"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene
        cycles = scene.cycles

        if not bpy.data.is_saved:
            self.report({'INFO'}, "Please save your file first")
            return {"CANCELLED"}

        filepath = bpy.data.filepath
        dirpath = os.path.join(os.path.dirname(bpy.data.filepath), scene.TLM_EngineProperties.tlm_lightmap_savedir)
        
        if platform.system() != "Linux":

            if os.path.isdir(dirpath):
                webbrowser.open('file://' + dirpath)
            else:
                os.mkdir(dirpath)
                webbrowser.open('file://' + dirpath)
        else:

            if os.path.isdir(dirpath):
                os.system('xdg-open "%s"' % dirpath)
                #webbrowser.open('file://' + dirpath)
            else:
                os.mkdir(dirpath)
                os.system('xdg-open "%s"' % dirpath)
                #webbrowser.open('file://' + dirpath)

        return {'FINISHED'}

class TLM_EnableSet(bpy.types.Operator):
    """Enable for set"""
    bl_idname = "tlm.enable_set"
    bl_label = "Enable for set"
    bl_description = "Enable for set"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        weightList = {} #ObjName : [Dimension,Weight]
        max = 0

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":

                    print("Enabling for scene: " + obj.name)
                    
                    bpy.context.view_layer.objects.active = obj
                    obj.select_set(True)
                    bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                    
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                    
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
                    obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
                    obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object

                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
                        obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer

                    obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
                    
                    if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
                        obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
                        weightList[obj.name] = [obj_dimensions, 0]
                        if obj_dimensions > max:
                            max = obj_dimensions
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
                        bm = bmesh.new()
                        bm.from_mesh(obj.data)
                        area = sum(f.calc_area() for f in bm.faces)
                        weightList[obj.name] = [area, 0]
                        if area > max:
                            max = area
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
                        bm = bmesh.new()
                        bm.from_mesh(obj.data)
                        volume = float( bm.calc_volume())
                        weightList[obj.name] = [volume, 0]
                        if volume > max:
                            max = volume
        
        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":
                    
                    print("Enabling for selection: " + obj.name)
                    
                    bpy.context.view_layer.objects.active = obj
                    obj.select_set(True)
                    bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                    
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                    
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
                    obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
                    obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object

                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
                        obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer

                    obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
                    
                    if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
                        obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
                        weightList[obj.name] = [obj_dimensions, 0]
                        if obj_dimensions > max:
                            max = obj_dimensions
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
                        bm = bmesh.new()
                        bm.from_mesh(obj.data)
                        area = sum(f.calc_area() for f in bm.faces)
                        weightList[obj.name] = [area, 0]
                        if area > max:
                            max = area
                    elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
                        bm = bmesh.new()
                        bm.from_mesh(obj.data)
                        volume = float( bm.calc_volume())
                        weightList[obj.name] = [volume, 0]
                        if volume > max:
                            max = volume
        
        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        print("Enabling for designated: " + obj.name)
                        
                        bpy.context.view_layer.objects.active = obj
                        obj.select_set(True)
                        bpy.ops.object.transform_apply(location=False, rotation=True, scale=True)
                        
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                        
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = bpy.context.scene.TLM_SceneProperties.tlm_mesh_lightmap_unwrap_mode
                        obj.TLM_ObjectProperties.tlm_mesh_unwrap_margin = bpy.context.scene.TLM_SceneProperties.tlm_mesh_unwrap_margin
                        obj.TLM_ObjectProperties.tlm_postpack_object = bpy.context.scene.TLM_SceneProperties.tlm_postpack_object

                        if obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode == "AtlasGroupA":
                            obj.TLM_ObjectProperties.tlm_atlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_atlas_pointer

                        obj.TLM_ObjectProperties.tlm_postatlas_pointer = bpy.context.scene.TLM_SceneProperties.tlm_postatlas_pointer
                        
                        if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Single":
                            obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = scene.TLM_SceneProperties.tlm_mesh_lightmap_resolution
                        elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Dimension":
                            obj_dimensions = obj.dimensions.x * obj.dimensions.y * obj.dimensions.z
                            weightList[obj.name] = [obj_dimensions, 0]
                            if obj_dimensions > max:
                                max = obj_dimensions
                        elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Surface":
                            bm = bmesh.new()
                            bm.from_mesh(obj.data)
                            area = sum(f.calc_area() for f in bm.faces)
                            weightList[obj.name] = [area, 0]
                            if area > max:
                                max = area
                        elif bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight == "Volume":
                            bm = bmesh.new()
                            bm.from_mesh(obj.data)
                            volume = float( bm.calc_volume())
                            weightList[obj.name] = [volume, 0]
                            if volume > max:
                                max = volume

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
        
                    if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
                        for key in weightList:
                            weightList[obj.name][1] = weightList[obj.name][0] / max
                        a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
                        print(str(a) + "/" + str(weightList[obj.name][1]))
                        print("Scale: " + str(weightList[obj.name][0]))
                        print("Obj: " + obj.name)
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)

        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":

                    if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
                        for key in weightList:
                            weightList[obj.name][1] = weightList[obj.name][0] / max
                        a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
                        print(str(a) + "/" + str(weightList[obj.name][1]))
                        print("Scale: " + str(weightList[obj.name][0]))
                        print("Obj: " + obj.name)
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)


        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        if bpy.context.scene.TLM_SceneProperties.tlm_resolution_weight != "Single":
                            for key in weightList:
                                weightList[obj.name][1] = weightList[obj.name][0] / max
                            a = setObjectLightmapByWeight(int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_min), int(bpy.context.scene.TLM_SceneProperties.tlm_resolution_max), weightList[obj.name][1])
                            print(str(a) + "/" + str(weightList[obj.name][1]))
                            print("Scale: " + str(weightList[obj.name][0]))
                            print("Obj: " + obj.name)
                            print("")
                            obj.TLM_ObjectProperties.tlm_mesh_lightmap_resolution = str(a)
        
        return{'FINISHED'}

class TLM_DisableSelection(bpy.types.Operator):
    """Disable for set"""
    bl_idname = "tlm.disable_selection"
    bl_label = "Disable for set"
    bl_description = "Disable for selection"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        weightList = {} #ObjName : [Dimension,Weight]
        max = 0

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":

                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False

        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":

                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False


        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = False


        return{'FINISHED'}

class TLM_RemoveLightmapUV(bpy.types.Operator):
    """Remove Lightmap UV for set"""
    bl_idname = "tlm.remove_uv_selection"
    bl_label = "Remove Lightmap UV"
    bl_description = "Remove Lightmap UV for set"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":

                    uv_layers = obj.data.uv_layers

                    if not obj.TLM_ObjectProperties.tlm_use_default_channel:
                        uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
                    else:
                        uv_channel = "UVMap_Lightmap"

                    for uvlayer in uv_layers:
                        if uvlayer.name == uv_channel:
                            uv_layers.remove(uvlayer)

        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":

                    uv_layers = obj.data.uv_layers

                    if not obj.TLM_ObjectProperties.tlm_use_default_channel:
                        uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
                    else:
                        uv_channel = "UVMap_Lightmap"

                    for uvlayer in uv_layers:
                        if uvlayer.name == uv_channel:
                            uv_layers.remove(uvlayer)

        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        uv_layers = obj.data.uv_layers

                        if not obj.TLM_ObjectProperties.tlm_use_default_channel:
                            uv_channel = obj.TLM_ObjectProperties.tlm_uv_channel
                        else:
                            uv_channel = "UVMap_Lightmap"

                        for uvlayer in uv_layers:
                            if uvlayer.name == uv_channel:
                                uv_layers.remove(uvlayer)

        return{'FINISHED'}

class TLM_SelectLightmapped(bpy.types.Operator):
    """Select all objects for lightmapping"""
    bl_idname = "tlm.select_lightmapped_objects"
    bl_label = "Select lightmap objects"
    bl_description = "Remove Lightmap UV for selection"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        for obj in bpy.context.scene.objects:
            if obj.type == 'MESH' and obj.name in bpy.context.view_layer.objects:
                if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                    obj.select_set(True)

        return{'FINISHED'}

class TLM_GroupListNewItem(bpy.types.Operator):
    # Add a new item to the list
    bl_idname = "tlm_grouplist.new_item"
    bl_label = "Add a new lightmap group"
    bl_description = "Create a new lightmap group"

    def execute(self, context):
        scene = context.scene
        scene.TLM_GroupList.add()
        scene.TLM_GroupListItem = len(scene.TLM_GroupList) - 1

        scene.TLM_GroupList[len(scene.TLM_GroupList) - 1].name = "LightmapGroup"

class TLM_AtlasListNewItem(bpy.types.Operator):
    # Add a new item to the list
    bl_idname = "tlm_atlaslist.new_item"
    bl_label = "Add a new item"
    bl_description = "Create a new AtlasGroup"

    def execute(self, context):
        scene = context.scene
        scene.TLM_AtlasList.add()
        scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1

        scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = "AtlasGroup"

        return{'FINISHED'}

class TLM_PostAtlasListNewItem(bpy.types.Operator):
    # Add a new item to the list
    bl_idname = "tlm_postatlaslist.new_item"
    bl_label = "Add a new item"
    bl_description = "Create a new AtlasGroup"
    bl_description = ""

    def execute(self, context):
        scene = context.scene
        scene.TLM_PostAtlasList.add()
        scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1

        scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = "AtlasGroup"

        return{'FINISHED'}

class TLM_AtlastListDeleteItem(bpy.types.Operator):
    # Delete the selected item from the list
    bl_idname = "tlm_atlaslist.delete_item"
    bl_label = "Deletes an item"
    bl_description = "Delete an AtlasGroup"

    @classmethod
    def poll(self, context):
        """ Enable if there's something in the list """
        scene = context.scene
        return len(scene.TLM_AtlasList) > 0

    def execute(self, context):
        scene = context.scene
        list = scene.TLM_AtlasList
        index = scene.TLM_AtlasListItem

        for obj in bpy.context.scene.objects:

            atlasName = scene.TLM_AtlasList[index].name

            if obj.TLM_ObjectProperties.tlm_atlas_pointer == atlasName:
                obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "SmartProject"

        list.remove(index)

        if index > 0:
            index = index - 1

        scene.TLM_AtlasListItem = index
        return{'FINISHED'}

class TLM_PostAtlastListDeleteItem(bpy.types.Operator):
    # Delete the selected item from the list
    bl_idname = "tlm_postatlaslist.delete_item"
    bl_label = "Deletes an item"
    bl_description = "Delete an AtlasGroup"

    @classmethod
    def poll(self, context):
        """ Enable if there's something in the list """
        scene = context.scene
        return len(scene.TLM_PostAtlasList) > 0

    def execute(self, context):
        scene = context.scene
        list = scene.TLM_PostAtlasList
        index = scene.TLM_PostAtlasListItem

        for obj in bpy.context.scene.objects:

            atlasName = scene.TLM_PostAtlasList[index].name

            if obj.TLM_ObjectProperties.tlm_atlas_pointer == atlasName:
                obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "SmartProject"

        list.remove(index)

        if index > 0:
            index = index - 1

        scene.TLM_PostAtlasListItem = index
        return{'FINISHED'}

class TLM_AtlasListMoveItem(bpy.types.Operator):
    # Move an item in the list
    bl_idname = "tlm_atlaslist.move_item"
    bl_label = "Move an item in the list"
    bl_description = "Move an item in the list"
    direction: bpy.props.EnumProperty(
                items=(
                    ('UP', 'Up', ""),
                    ('DOWN', 'Down', ""),))

    def move_index(self):
        # Move index of an item render queue while clamping it
        scene = context.scene
        index = scene.TLM_AtlasListItem
        list_length = len(scene.TLM_AtlasList) - 1
        new_index = 0

        if self.direction == 'UP':
            new_index = index - 1
        elif self.direction == 'DOWN':
            new_index = index + 1

        new_index = max(0, min(new_index, list_length))
        scene.TLM_AtlasList.move(index, new_index)
        scene.TLM_AtlasListItem = new_index

    def execute(self, context):
        scene = context.scene
        list = scene.TLM_AtlasList
        index = scene.TLM_AtlasListItem

        if self.direction == 'DOWN':
            neighbor = index + 1
            self.move_index()

        elif self.direction == 'UP':
            neighbor = index - 1
            self.move_index()
        else:
            return{'CANCELLED'}
        return{'FINISHED'}

class TLM_PostAtlasListMoveItem(bpy.types.Operator):
    # Move an item in the list
    bl_idname = "tlm_postatlaslist.move_item"
    bl_label = "Move an item in the list"
    bl_description = "Move an item in the list"
    direction: bpy.props.EnumProperty(
                items=(
                    ('UP', 'Up', ""),
                    ('DOWN', 'Down', ""),))

    def move_index(self):
        # Move index of an item render queue while clamping it
        scene = context.scene
        index = scene.TLM_PostAtlasListItem
        list_length = len(scene.TLM_PostAtlasList) - 1
        new_index = 0

        if self.direction == 'UP':
            new_index = index - 1
        elif self.direction == 'DOWN':
            new_index = index + 1

        new_index = max(0, min(new_index, list_length))
        scene.TLM_PostAtlasList.move(index, new_index)
        scene.TLM_PostAtlasListItem = new_index

    def execute(self, context):
        scene = context.scene
        list = scene.TLM_PostAtlasList
        index = scene.TLM_PostAtlasListItem

        if self.direction == 'DOWN':
            neighbor = index + 1
            self.move_index()

        elif self.direction == 'UP':
            neighbor = index - 1
            self.move_index()
        else:
            return{'CANCELLED'}
        return{'FINISHED'}

class TLM_StartServer(bpy.types.Operator):
    bl_idname = "tlm.start_server"
    bl_label = "Start Network Server"
    bl_description = "Start Network Server"
    bl_options = {'REGISTER', 'UNDO'}

    def modal(self, context, event):

        #Add progress bar from 0.15

        print("MODAL")

        return {'PASS_THROUGH'}

    def invoke(self, context, event):

        server.startServer()

        return {'RUNNING_MODAL'}

class TLM_BuildEnvironmentProbes(bpy.types.Operator):
    bl_idname = "tlm.build_environmentprobe"
    bl_label = "Build Environment Probes"
    bl_description = "Build all environment probes from reflection cubemaps"
    bl_options = {'REGISTER', 'UNDO'}

    def invoke(self, context, event):

        for obj in bpy.context.scene.objects:

            if obj.type == "LIGHT_PROBE":
                if obj.data.type == "CUBEMAP":

                    cam_name = "EnvPCam_" + obj.name
                    camera = bpy.data.cameras.new(cam_name)
                    camobj_name = "EnvPCamera_" + obj.name
                    cam_obj = bpy.data.objects.new(camobj_name, camera)
                    bpy.context.collection.objects.link(cam_obj)
                    cam_obj.location = obj.location
                    camera.angle = math.radians(90)

                    prevResx = bpy.context.scene.render.resolution_x
                    prevResy = bpy.context.scene.render.resolution_y
                    prevCam = bpy.context.scene.camera
                    prevEngine = bpy.context.scene.render.engine
                    bpy.context.scene.camera = cam_obj

                    bpy.context.scene.render.engine = bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_engine
                    bpy.context.scene.render.resolution_x = int(bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_resolution)
                    bpy.context.scene.render.resolution_y = int(bpy.context.scene.TLM_SceneProperties.tlm_environment_probe_resolution)

                    savedir = os.path.dirname(bpy.data.filepath)
                    directory = os.path.join(savedir, "Probes")

                    t = 90

                    inverted = bpy.context.scene.TLM_SceneProperties.tlm_invert_direction

                    if inverted:

                        positions = {
                                "xp" : (math.radians(t), 0, math.radians(0)),
                                "zp" : (math.radians(t), 0, math.radians(t)),
                                "xm" : (math.radians(t), 0, math.radians(t*2)),
                                "zm" : (math.radians(t), 0, math.radians(-t)),
                                "yp" : (math.radians(t*2), 0, math.radians(t)),
                                "ym" : (0, 0, math.radians(t))
                        }

                    else:

                        positions = {
                                "xp" : (math.radians(t), 0, math.radians(t*2)),
                                "zp" : (math.radians(t), 0, math.radians(-t)),
                                "xm" : (math.radians(t), 0, math.radians(0)),
                                "zm" : (math.radians(t), 0, math.radians(t)),
                                "yp" : (math.radians(t*2), 0, math.radians(-t)),
                                "ym" : (0, 0, math.radians(-t))
                        }



                    cam = cam_obj
                    image_settings = bpy.context.scene.render.image_settings
                    image_settings.file_format = "HDR"
                    image_settings.color_depth = '32'

                    for val in positions:
                        cam.rotation_euler = positions[val]
                        
                        filename = os.path.join(directory, val) + "_" + camobj_name + ".hdr"
                        bpy.context.scene.render.filepath = filename
                        print("Writing out: " + val)
                        bpy.ops.render.render(write_still=True)

                    cmft_path = bpy.path.abspath(os.path.join(os.path.dirname(bpy.data.filepath), bpy.context.scene.TLM_SceneProperties.tlm_cmft_path))

                    output_file_irr = camobj_name + ".hdr"

                    posx = directory + "/" + "xp_" + camobj_name + ".hdr"
                    negx = directory + "/" + "xm_" + camobj_name + ".hdr"
                    posy = directory + "/" + "yp_" + camobj_name + ".hdr"
                    negy = directory + "/" + "ym_" + camobj_name + ".hdr"
                    posz = directory + "/" + "zp_" + camobj_name + ".hdr"
                    negz = directory + "/" + "zm_" + camobj_name + ".hdr"
                    output = directory + "/" + camobj_name

                    if platform.system() == 'Windows':
                        envpipe = [cmft_path, 
                        '--inputFacePosX', posx, 
                        '--inputFaceNegX', negx, 
                        '--inputFacePosY', posy, 
                        '--inputFaceNegY', negy, 
                        '--inputFacePosZ', posz, 
                        '--inputFaceNegZ', negz, 
                        '--output0', output, 
                        '--output0params', 
                        'hdr,rgbe,latlong']
                        
                    else:
                        envpipe = [cmft_path + '--inputFacePosX' + posx 
                        + '--inputFaceNegX' + negx 
                        + '--inputFacePosY' + posy 
                        + '--inputFaceNegY' + negy 
                        + '--inputFacePosZ' + posz 
                        + '--inputFaceNegZ' + negz 
                        + '--output0' + output 
                        + '--output0params' + 'hdr,rgbe,latlong']

                    if bpy.context.scene.TLM_SceneProperties.tlm_verbose:
                        print("Calling CMFT with:" + str(envpipe))

                    if bpy.context.scene.TLM_SceneProperties.tlm_create_spherical:
                        subprocess.call(envpipe, shell=True)

                    input2 = output + ".hdr"
                    output2 = directory + "/" + camobj_name

                    if platform.system() == 'Windows':
                        envpipe2 = [cmft_path, 
                        '--input', input2, 
                        '--filter', 'shcoeffs', 
                        '--outputNum', '1', 
                        '--output0', output2]
                        
                    else:
                        envpipe2 = [cmft_path + 
                        '--input' + input2
                        + '-filter' + 'shcoeffs'
                        + '--outputNum' + '1'
                        + '--output0' + output2]
                        
                    if bpy.context.scene.TLM_SceneProperties.tlm_write_sh:
                        subprocess.call(envpipe2, shell=True)

                    if bpy.context.scene.TLM_SceneProperties.tlm_write_radiance:

                        use_opencl = 'false'
                        cpu_count = 2

                        # 4096 = 256 face
                        # 2048 = 128 face
                        # 1024 = 64 face
                        target_w = int(512)
                        face_size = target_w / 8
                        if target_w == 2048:
                            mip_count = 9
                        elif target_w == 1024:
                            mip_count = 8
                        else:
                            mip_count = 7

                        output_file_rad = directory + "/" + camobj_name + "_rad.hdr"
                        
                        if platform.system() == 'Windows':

                            envpipe3 = [
                                cmft_path,
                                '--input', input2,
                                '--filter', 'radiance',
                                '--dstFaceSize', str(face_size),
                                '--srcFaceSize', str(face_size),
                                '--excludeBase', 'false',
                                # '--mipCount', str(mip_count),
                                '--glossScale', '8',
                                '--glossBias', '3',
                                '--lightingModel', 'blinnbrdf',
                                '--edgeFixup', 'none',
                                '--numCpuProcessingThreads', str(cpu_count),
                                '--useOpenCL', use_opencl,
                                '--clVendor', 'anyGpuVendor',
                                '--deviceType', 'gpu',
                                '--deviceIndex', '0',
                                '--generateMipChain', 'true',
                                '--inputGammaNumerator', '1.0',
                                '--inputGammaDenominator', '1.0',
                                '--outputGammaNumerator', '1.0',
                                '--outputGammaDenominator', '1.0',
                                '--outputNum', '1',
                                '--output0', output_file_rad,
                                '--output0params', 'hdr,rgbe,latlong'
                            ]

                            subprocess.call(envpipe3)

                        else:

                            envpipe3 = cmft_path + \
                                ' --input "' + input2 + '"' + \
                                ' --filter radiance' + \
                                ' --dstFaceSize ' + str(face_size) + \
                                ' --srcFaceSize ' + str(face_size) + \
                                ' --excludeBase false' + \
                                ' --glossScale 8' + \
                                ' --glossBias 3' + \
                                ' --lightingModel blinnbrdf' + \
                                ' --edgeFixup none' + \
                                ' --numCpuProcessingThreads ' + str(cpu_count) + \
                                ' --useOpenCL ' + use_opencl + \
                                ' --clVendor anyGpuVendor' + \
                                ' --deviceType gpu' + \
                                ' --deviceIndex 0' + \
                                ' --generateMipChain true' + \
                                ' --inputGammaNumerator ' + '1.0' + \
                                ' --inputGammaDenominator 1.0' + \
                                ' --outputGammaNumerator 1.0' + \
                                ' --outputGammaDenominator 1.0' + \
                                ' --outputNum 1' + \
                                ' --output0 "' + output_file_rad + '"' + \
                                ' --output0params hdr,rgbe,latlong'

                            subprocess.call([envpipe3], shell=True)

                    for obj in bpy.context.scene.objects:
                        obj.select_set(False)

                    cam_obj.select_set(True)
                    bpy.ops.object.delete()
                    bpy.context.scene.render.resolution_x = prevResx
                    bpy.context.scene.render.resolution_y = prevResy
                    bpy.context.scene.camera = prevCam
                    bpy.context.scene.render.engine = prevEngine

                    print("Finished building environment probes")


        return {'RUNNING_MODAL'}

class TLM_CleanBuildEnvironmentProbes(bpy.types.Operator): 
    bl_idname = "tlm.clean_environmentprobe"
    bl_label = "Clean Environment Probes"
    bl_description = "Clean Environment Probes"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        savedir = os.path.dirname(bpy.data.filepath)
        dirpath = os.path.join(savedir, "Probes")

        if os.path.isdir(dirpath):
            for file in os.listdir(dirpath):
                os.remove(os.path.join(dirpath + "/" + file))

        return {'FINISHED'}

class TLM_MergeAdjacentActors(bpy.types.Operator): 
    bl_idname = "tlm.merge_adjacent_actors"
    bl_label = "Merge adjacent actors"
    bl_description = "Merges the adjacent faces/vertices of selected objects"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        return {'FINISHED'}

class TLM_PrepareUVMaps(bpy.types.Operator): 
    bl_idname = "tlm.prepare_uvmaps"
    bl_label = "Prepare UV maps"
    bl_description = "Prepare UV lightmaps for selected objects"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        return {'FINISHED'}

class TLM_LoadLightmaps(bpy.types.Operator): 
    bl_idname = "tlm.load_lightmaps"
    bl_label = "Load Lightmaps"
    bl_description = "Load lightmaps from selected folder"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        utility.transfer_load()

        print("Transfer finished")

        build.finish_assemble(self, 1, 1)

        return {'FINISHED'}

class TLM_ToggleTexelDensity(bpy.types.Operator): 
    bl_idname = "tlm.toggle_texel_density"
    bl_label = "Toggle Texel Density"
    bl_description = "Toggle visualize lightmap texel density for selected objects"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene

        for obj in bpy.context.selected_objects:
            if obj.type == "MESH":
                uv_layers = obj.data.uv_layers

                #if the object has a td_vis in the uv maps, toggle off
                #else toggle on

                if obj.TLM_ObjectProperties.tlm_use_default_channel:

                    for i in range(0, len(uv_layers)):
                        if uv_layers[i].name == 'UVMap_Lightmap':
                            uv_layers.active_index = i
                            break
                else:

                    for i in range(0, len(uv_layers)):
                        if uv_layers[i].name == obj.TLM_ObjectProperties.tlm_uv_channel:
                            uv_layers.active_index = i
                            break

                #filepath = r"C:\path\to\image.png"

                #img = bpy.data.images.load(filepath)

                for area in bpy.context.screen.areas:
                    if area.type == 'VIEW_3D':
                        space_data = area.spaces.active
                        bpy.ops.screen.area_dupli('INVOKE_DEFAULT')
                        new_window = context.window_manager.windows[-1]

                        area = new_window.screen.areas[-1]
                        area.type = 'VIEW_3D'
                        #bg = space_data.background_images.new()
                        print(bpy.context.object)
                        bpy.ops.object.bake_td_uv_to_vc()

                        #bg.image = img
                        break

                
                #set active uv_layer to 
                

        print("TLM_Viz_Toggle")

        return {'FINISHED'}

class TLM_DisableSpecularity(bpy.types.Operator): 
    bl_idname = "tlm.disable_specularity"
    bl_label = "Disable specularity"
    bl_description = "Disables specularity from set"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":

                    for slot in obj.material_slots:

                        mat = slot.material
                        
                        if mat.node_tree:

                            for node in mat.node_tree.nodes:

                                if node.type == "BSDF_PRINCIPLED":

                                    for inp in node.inputs:

                                        if inp.name == "Specular":

                                            inp.default_value = 0.0

                                            if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                                mat.node_tree.links.remove(inp.links[0])

        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":

                    for slot in obj.material_slots:

                        mat = slot.material
                        
                        if mat.node_tree:

                            for node in mat.node_tree.nodes:

                                if node.type == "BSDF_PRINCIPLED":

                                    for inp in node.inputs:

                                        if inp.name == "Specular":

                                            inp.default_value = 0.0

                                            if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                                mat.node_tree.links.remove(inp.links[0])

        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        for slot in obj.material_slots:

                            mat = slot.material
                            
                            if mat.node_tree:

                                for node in mat.node_tree.nodes:

                                    if node.type == "BSDF_PRINCIPLED":

                                        for inp in node.inputs:

                                            if inp.name == "Specular":

                                                inp.default_value = 0.0

                                                if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                                    mat.node_tree.links.remove(inp.links[0])

        return{'FINISHED'}

class TLM_DisableMetallic(bpy.types.Operator): 
    bl_idname = "tlm.disable_metallic"
    bl_label = "Disable metallic"
    bl_description = "Disables metallic from set"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        if bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Scene":
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":

                    for slot in obj.material_slots:

                        mat = slot.material

                        for node in mat.node_tree.nodes:

                            if node.type == "BSDF_PRINCIPLED":

                                for inp in node.inputs:

                                    if inp.name == "Metallic":

                                        inp.default_value = 0.0

                                        if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                            mat.node_tree.links.remove(inp.links[0])

        elif bpy.context.scene.TLM_SceneProperties.tlm_utility_set == "Selection":
            for obj in bpy.context.selected_objects:
                if obj.type == "MESH":

                    for slot in obj.material_slots:

                        mat = slot.material

                        for node in mat.node_tree.nodes:

                            if node.type == "BSDF_PRINCIPLED":

                                for inp in node.inputs:

                                    if inp.name == "Metallic":

                                        inp.default_value = 0.0

                                        if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                            mat.node_tree.links.remove(inp.links[0])

        else: #Enabled
            for obj in bpy.context.scene.objects:
                if obj.type == "MESH":
                    if obj.TLM_ObjectProperties.tlm_mesh_lightmap_use:

                        for slot in obj.material_slots:

                            mat = slot.material

                            for node in mat.node_tree.nodes:

                                if node.type == "BSDF_PRINCIPLED":

                                    for inp in node.inputs:

                                        if inp.name == "Metallic":

                                            inp.default_value = 0.0

                                            if inp.links and bpy.context.scene.TLM_SceneProperties.tlm_remove_met_spec_link:

                                                mat.node_tree.links.remove(inp.links[0])

        return{'FINISHED'}

class TLM_RemoveEmptyImages(bpy.types.Operator): 

    bl_idname = "tlm.remove_empty_images"
    bl_label = "Remove Empty Images"
    bl_description = "Removes empty images from scene materials"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        for mat in bpy.data.materials:

            nodetree = mat.node_tree

            if nodetree:

                for node in nodetree.nodes:

                    if node.name == "Baked Image":

                        print(node.name)

                        nodetree.nodes.remove(node)

        return{'FINISHED'}


class TLM_PostAtlasSpecialsMenu(bpy.types.Menu):
    bl_label = "Lightmap"
    bl_idname = "TLM_MT_PostAtlasListSpecials"

    def draw(self, context):
        layout = self.layout
        layout.operator("tlm.add_collections_post")
        layout.operator("tlm.add_selected_collections_post")

class TLM_AddCollectionsPost(bpy.types.Operator): 
    bl_idname = "tlm.add_collections_post"
    bl_label = "Add collections"
    bl_description = "Adds all collections to atlases"
    bl_options = {'REGISTER', 'UNDO'}

    resolution : bpy.props.EnumProperty(
            items = [('32', '32', 'TODO'),
                    ('64', '64', 'TODO'),
                    ('128', '128', 'TODO'),
                    ('256', '256', 'TODO'),
                    ('512', '512', 'TODO'),
                    ('1024', '1024', 'TODO'),
                    ('2048', '2048', 'TODO'),
                    ('4096', '4096', 'TODO'),
                    ('8192', '8192', 'TODO')],
                    name = "Atlas Lightmap Resolution", 
                    description="Atlas lightmap resolution",
                    default='256')

    unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
                 ('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm')]

    if "blender_xatlas" in bpy.context.preferences.addons.keys():
        unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))

    unwrap : bpy.props.EnumProperty(
        items = unwrap_modes,
                name = "Unwrap Mode", 
                description="Atlas unwrapping method", 
                default='SmartProject')

    margin : bpy.props.FloatProperty(
        name="Unwrap Margin", 
        default=0.1, 
        min=0.0, 
        max=1.0, 
        subtype='FACTOR')

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):
        
        for collection in bpy.context.scene.collection.children:
            
            #Add a new atlas with collection name
            #Traverse before adding
            scene = bpy.context.scene
            scene.TLM_PostAtlasList.add()
            scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1

            scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = collection.name
            scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
            scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
            scene.TLM_PostAtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
            
            for obj in collection.objects:
                if obj.type == "MESH":
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                    obj.TLM_ObjectProperties.tlm_postpack_object = True
                    obj.TLM_ObjectProperties.tlm_postatlas_pointer = collection.name

        return{'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        row = self.layout
        row.prop(self, "unwrap", text="Unwrap mode")
        row.prop(self, "resolution", text="Resolution")
        row.prop(self, "margin", text="Margin")

class TLM_AddSelectedCollectionsPost(bpy.types.Operator): 
    bl_idname = "tlm.add_selected_collections_post"
    bl_label = "Add selected collections"
    bl_description = "Add the collections of the selected objects"
    bl_options = {'REGISTER', 'UNDO'}

    resolution : bpy.props.EnumProperty(
            items = [('32', '32', 'TODO'),
                    ('64', '64', 'TODO'),
                    ('128', '128', 'TODO'),
                    ('256', '256', 'TODO'),
                    ('512', '512', 'TODO'),
                    ('1024', '1024', 'TODO'),
                    ('2048', '2048', 'TODO'),
                    ('4096', '4096', 'TODO'),
                    ('8192', '8192', 'TODO')],
                    name = "Atlas Lightmap Resolution", 
                    description="Atlas lightmap resolution",
                    default='256')

    unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
                 ('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm')]

    if "blender_xatlas" in bpy.context.preferences.addons.keys():
        unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))

    unwrap : bpy.props.EnumProperty(
        items = unwrap_modes,
                name = "Unwrap Mode", 
                description="Atlas unwrapping method", 
                default='SmartProject')

    margin : bpy.props.FloatProperty(
        name="Unwrap Margin", 
        default=0.1, 
        min=0.0, 
        max=1.0, 
        subtype='FACTOR')

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):

        collections = []

        for obj in bpy.context.selected_objects:

            obj_collection = obj.users_collection[0]

            if obj_collection.name not in collections:

                collections.append(obj_collection.name)

        print("Collections:" + str(collections))
        
        for collection in bpy.context.scene.collection.children:

            if collection.name in collections:
                
                #Add a new atlas with collection name
                #Traverse before adding
                scene = bpy.context.scene
                scene.TLM_PostAtlasList.add()
                scene.TLM_PostAtlasListItem = len(scene.TLM_PostAtlasList) - 1

                scene.TLM_PostAtlasList[len(scene.TLM_PostAtlasList) - 1].name = collection.name
                scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
                scene.TLM_PostAtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
                scene.TLM_PostAtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
                
                for obj in collection.objects:
                    if obj.type == "MESH":
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                        obj.TLM_ObjectProperties.tlm_postpack_object = True
                        obj.TLM_ObjectProperties.tlm_postatlas_pointer = collection.name

        return{'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        row = self.layout
        row.prop(self, "unwrap", text="Unwrap mode")
        row.prop(self, "resolution", text="Resolution")
        row.prop(self, "margin", text="Margin")

class TLM_AtlasSpecialsMenu(bpy.types.Menu):
    bl_label = "Lightmap"
    bl_idname = "TLM_MT_AtlasListSpecials"

    def draw(self, context):
        layout = self.layout
        layout.operator("tlm.add_collections")
        layout.operator("tlm.add_selected_collections")

class TLM_AddCollections(bpy.types.Operator): 
    bl_idname = "tlm.add_collections"
    bl_label = "Add all collections"
    bl_description = "Adds all collections to atlases"
    bl_options = {'REGISTER', 'UNDO'}

    resolution : bpy.props.EnumProperty(
            items = [('32', '32', 'TODO'),
                    ('64', '64', 'TODO'),
                    ('128', '128', 'TODO'),
                    ('256', '256', 'TODO'),
                    ('512', '512', 'TODO'),
                    ('1024', '1024', 'TODO'),
                    ('2048', '2048', 'TODO'),
                    ('4096', '4096', 'TODO'),
                    ('8192', '8192', 'TODO')],
                    name = "Atlas Lightmap Resolution", 
                    description="Atlas lightmap resolution",
                    default='256')

    unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
                 ('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm'),
                 ('Copy', 'Copy existing', 'Use the existing UV channel')]

    if "blender_xatlas" in bpy.context.preferences.addons.keys():
        unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))

    unwrap : bpy.props.EnumProperty(
        items = unwrap_modes,
                name = "Unwrap Mode", 
                description="Atlas unwrapping method", 
                default='SmartProject')

    margin : bpy.props.FloatProperty(
        name="Unwrap Margin", 
        default=0.1, 
        min=0.0, 
        max=1.0, 
        subtype='FACTOR')

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):

        for collection in bpy.context.scene.collection.children:
            
            #Add a new atlas with collection name
            #Traverse before adding
            scene = bpy.context.scene
            scene.TLM_AtlasList.add()
            scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1

            scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = collection.name
            scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
            scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
            scene.TLM_AtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
            
            for obj in collection.objects:
                if obj.type == "MESH":
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                    obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "AtlasGroupA"
                    obj.TLM_ObjectProperties.tlm_atlas_pointer = collection.name

        return{'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        row = self.layout
        row.prop(self, "unwrap", text="Unwrap mode")
        row.prop(self, "resolution", text="Resolution")
        row.prop(self, "margin", text="Margin")

class TLM_AddSelectedCollections(bpy.types.Operator): 
    bl_idname = "tlm.add_selected_collections"
    bl_label = "Add the collections of the selected objects"
    bl_description = "Add the collections of the selected objects"
    bl_options = {'REGISTER', 'UNDO'}

    resolution : bpy.props.EnumProperty(
            items = [('32', '32', 'TODO'),
                    ('64', '64', 'TODO'),
                    ('128', '128', 'TODO'),
                    ('256', '256', 'TODO'),
                    ('512', '512', 'TODO'),
                    ('1024', '1024', 'TODO'),
                    ('2048', '2048', 'TODO'),
                    ('4096', '4096', 'TODO'),
                    ('8192', '8192', 'TODO')],
                    name = "Atlas Lightmap Resolution", 
                    description="Atlas lightmap resolution",
                    default='256')

    unwrap_modes = [('Lightmap', 'Lightmap', 'Use Blender Lightmap Pack algorithm'),
                 ('SmartProject', 'Smart Project', 'Use Blender Smart Project algorithm'),
                 ('Copy', 'Copy existing', 'Use the existing UV channel')]

    if "blender_xatlas" in bpy.context.preferences.addons.keys():
        unwrap_modes.append(('Xatlas', 'Xatlas', 'Use Xatlas addon packing algorithm'))

    unwrap : bpy.props.EnumProperty(
        items = unwrap_modes,
                name = "Unwrap Mode", 
                description="Atlas unwrapping method", 
                default='SmartProject')

    margin : bpy.props.FloatProperty(
        name="Unwrap Margin", 
        default=0.1, 
        min=0.0, 
        max=1.0, 
        subtype='FACTOR')

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):

        collections = []

        for obj in bpy.context.selected_objects:

            obj_collection = obj.users_collection[0]

            if obj_collection.name not in collections:

                collections.append(obj_collection.name)

        print("Collections:" + str(collections))

        for collection in bpy.context.scene.collection.children:

            if collection.name in collections:
                
                #Add a new atlas with collection name
                #Traverse before adding
                scene = bpy.context.scene
                scene.TLM_AtlasList.add()
                scene.TLM_AtlasListItem = len(scene.TLM_AtlasList) - 1

                scene.TLM_AtlasList[len(scene.TLM_AtlasList) - 1].name = collection.name
                scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_unwrap_mode = self.unwrap
                scene.TLM_AtlasList[collection.name].tlm_atlas_lightmap_resolution = self.resolution
                scene.TLM_AtlasList[collection.name].tlm_atlas_unwrap_margin = self.margin
                
                for obj in collection.objects:
                    if obj.type == "MESH":
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_use = True
                        obj.TLM_ObjectProperties.tlm_mesh_lightmap_unwrap_mode = "AtlasGroupA"
                        obj.TLM_ObjectProperties.tlm_atlas_pointer = collection.name

        return{'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_props_dialog(self)

    def draw(self, context):
        row = self.layout
        row.prop(self, "unwrap", text="Unwrap mode")
        row.prop(self, "resolution", text="Resolution")
        row.prop(self, "margin", text="Margin")
        
#Atlas disable objects

class TLM_Reset(bpy.types.Operator):
    bl_idname = "tlm.reset"
    bl_label = "Resets all UI and settings"
    bl_description = "Reset UI and objects"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):
        self.report({'INFO'}, "YES!")
        return {'FINISHED'}

    def invoke(self, context, event):
        return context.window_manager.invoke_confirm(self, event)

# class TLM_Reset2(bpy.types.Operator):
#     bl_idname = "tlm.reset2"
#     bl_label = "Do you really want to do that?"
#     bl_options = {'REGISTER', 'INTERNAL'}

#     prop1: bpy.props.BoolProperty()
#     prop2: bpy.props.BoolProperty()

#     @classmethod
#     def poll(cls, context):
#         return True

#     def execute(self, context):
#         self.report({'INFO'}, "YES!")
#         return {'FINISHED'}

#     def invoke(self, context, event):
#         return context.window_manager.invoke_props_dialog(self)

#     def draw(self, context):
#         row = self.layout
#         row.prop(self, "prop1", text="Property A")
#         row.prop(self, "prop2", text="Property B")

def TLM_DoubleResolution():
    pass

def TLM_HalfResolution():
    pass

def TLM_DivideLMGroups():
    pass

class TLM_CalcTexDex(bpy.types.Operator):
    bl_idname = "tlm.calctexdex"
    bl_label = "Calculate Texel Density"
    bl_description = "Calculates Texel Density of selected object"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return True

    def execute(self, context):
        return {'FINISHED'}

class TLM_AddGLTFNode(bpy.types.Operator):
    bl_idname = "tlm.add_gltf_node"
    bl_label = "Add GLTF Node"
    bl_description = "Add to GLTF node to active material and connect lightmap if present"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene
        cycles = scene.cycles
        material = bpy.context.active_object.active_material

        nodes = material.node_tree.nodes
        # create group data
        gltf_settings = bpy.data.node_groups.get('glTF Settings')
        if gltf_settings is None:
            bpy.data.node_groups.new('glTF Settings', 'ShaderNodeTree')
        
        # add group to node tree
        gltf_settings_node = nodes.get('glTF Settings')
        if gltf_settings_node is None:
            gltf_settings_node = nodes.new('ShaderNodeGroup')
            gltf_settings_node.name = 'glTF Settings'
            gltf_settings_node.node_tree = bpy.data.node_groups['glTF Settings']

        # create group inputs
        if gltf_settings_node.inputs.get('Occlusion') is None:
            gltf_settings_node.inputs.new('NodeSocketFloat','Occlusion')

        #return gltf_settings_node

        return {'FINISHED'}

class TLM_ShiftMultiplyLinks(bpy.types.Operator):
    bl_idname = "tlm.shift_multiply_links"
    bl_label = "Shift multiply links"
    bl_description = "Shift multiply links for active material"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):

        scene = context.scene
        cycles = scene.cycles
        material = bpy.context.active_object.active_material

        nodes = material.node_tree.nodes

        LM_Node = nodes.get("TLM_Lightmap")
        Multi_Node = nodes.get("Lightmap_Multiplication")
        Base_Node = nodes.get("Lightmap_BasecolorNode_A")

        material.node_tree.links.remove(LM_Node.outputs[0].links[0])
        material.node_tree.links.remove(Base_Node.outputs[0].links[0])

        material.node_tree.links.new(LM_Node.outputs[0], Multi_Node.inputs[2])
        material.node_tree.links.new(Base_Node.outputs[0], Multi_Node.inputs[1])

        return {'FINISHED'}