"""
Generation operators for text texture creation.
Contains operators for generating textures and complete shaders.
"""

import bpy
from bpy.types import Operator
import os
import json
import re
from math import isfinite

# Import required functions from other modules
from ..core.generation_engine import generate_texture_image, generate_preview


def _sanitize_text_for_name(text):
    sanitized_text = re.sub(r'[^\w\s-]', '', text[:20])
    sanitized_text = re.sub(r'[\s_-]+', '_', sanitized_text).strip('_')
    return sanitized_text or "Texture"


def _resolve_material_name(props, sanitized_text):
    custom_name = props.custom_material_name.strip()
    if custom_name:
        return custom_name
    return f"TextTexture_Mat_{sanitized_text}"


class TEXT_TEXTURE_OT_generate(Operator):
    pass
    """Generate final texture and pack it"""
    bl_idname = "text_texture.generate"
    bl_label = "Generate Text Texture"
    bl_description = "Generate final high-quality texture and pack it into the blend file"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        pass
        try:
            pass
            props = context.scene.text_texture_props
            
            # IMPORTANT: Generate at EXACT user-specified dimensions
            final_width = props.texture_width
            final_height = props.texture_height
            
            print(f"Generating texture at {final_width}x{final_height}px")
            
            # Generate at FULL resolution specified by user
            result = generate_texture_image(props, final_width, final_height)
            
            if not result or result == (None, None) or result[0] is None:
                pass
                self.report({'ERROR'}, "Failed to generate texture image")
                return {'CANCELLED'}
            
            # Extract diffuse and normal map images
            if isinstance(result, tuple):
                pass
                img, normal_map_img = result
            else:
                pass
                # Backward compatibility
                img = result
                normal_map_img = None
            
            if not img:
                pass
                self.report({'ERROR'}, "Failed to generate diffuse image")
                return {'CANCELLED'}
            
            # Create diffuse texture with proper sanitization
            sanitized_text = _sanitize_text_for_name(props.text)
            img_name = f"TextTexture_{sanitized_text}"
            
            # Remove existing diffuse texture
            if img_name in bpy.data.images:
                pass
                bpy.data.images.remove(bpy.data.images[img_name])
            
            # Create new diffuse image with USER SPECIFIED dimensions
            blender_img = bpy.data.images.new(img_name, final_width, final_height, alpha=True)
            
            # Directly copy pixel data from generated image to Blender image
            # The generated image is already in the correct format, just copy it
            blender_img.pixels[:] = img.pixels[:]
            blender_img.pack()  # PACK FINAL IMAGE
            
            # Create normal map texture if enabled and generated
            normal_map_blender_img = None
            if normal_map_img and props.generate_normal_map:
                pass
                normal_map_name = f"TextTexture_Normal_{sanitized_text}"
                
                # Remove existing normal map texture
                if normal_map_name in bpy.data.images:
                    pass
                    bpy.data.images.remove(bpy.data.images[normal_map_name])
                
                # Create new normal map image
                normal_map_blender_img = bpy.data.images.new(normal_map_name, final_width, final_height, alpha=False)
                normal_map_blender_img.colorspace_settings.name = 'Non-Color'  # Important for normal maps
                
                # Directly copy normal map pixel data
                # Copy RGB channels and set alpha to 1.0
                temp_pixels = list(normal_map_img.pixels[:])
                for i in range(3, len(temp_pixels), 4):
                    pass
                    temp_pixels[i] = 1.0  # Set alpha to 1.0 for normal maps
                normal_map_blender_img.pixels[:] = temp_pixels
                normal_map_blender_img.pack()  # PACK NORMAL MAP
            
            # Add to shader
            if context.space_data and context.space_data.type == 'NODE_EDITOR':
                pass
                if context.space_data.tree_type == 'ShaderNodeTree':
                    pass
                    material = context.space_data.id
                    if material and material.use_nodes:
                        pass
                        nodes = material.node_tree.nodes
                        
                        # Add diffuse texture node
                        img_node = nodes.new(type='ShaderNodeTexImage')
                        img_node.image = blender_img
                        img_node.location = (0, 0)
                        img_node.select = True
                        nodes.active = img_node
                        
                        # Add normal map node if normal map was created
                        if normal_map_blender_img:
                            pass
                            normal_node = nodes.new(type='ShaderNodeTexImage')
                            normal_node.image = normal_map_blender_img
                            normal_node.location = (0, -300)
                            normal_node.label = "Normal Map"
            
            # Provide comprehensive feedback
            if normal_map_blender_img:
                pass
                self.report({'INFO'}, f"Generated and packed: {img_name} + {normal_map_blender_img.name} ({final_width}x{final_height}px)")
            else:
                pass
                self.report({'INFO'}, f"Generated and packed: {img_name} ({final_width}x{final_height}px)")
            return {'FINISHED'}
            
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Failed: {str(e)}")
            return {'CANCELLED'}


class TEXT_TEXTURE_OT_preview(Operator):
    pass
    """Generate basic preview for free version users"""
    bl_idname = "text_texture.preview"
    bl_label = "Generate Preview"
    bl_description = "Generate a basic preview of your text texture"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        pass
        try:
            pass
            # Import the preview function
            from ..core.generation_engine import generate_preview
            
            # Get properties and generate preview with correct parameters
            props = context.scene.text_texture_props
            preview_size = props.preview_size if hasattr(props, 'preview_size') else 512
            preview_width = preview_size
            preview_height = preview_size
            result = generate_preview(props, preview_width, preview_height)
            
            if result:
                pass
                self.report({'INFO'}, f"Preview generated at {preview_width}x{preview_height}px")
                
                # Always open a dedicated window to show the preview image
                try:
                    pass
                    bpy.ops.wm.window_new()
                    new_window = context.window_manager.windows[-1]
                    new_screen = new_window.screen

                    # Try to reuse an existing image editor in the new window
                    image_area = None
                    for area in new_screen.areas:
                        pass
                        if area.type == 'IMAGE_EDITOR':
                            pass
                            image_area = area
                            break

                    # Otherwise convert the largest area to an image editor
                    if not image_area and new_screen.areas:
                        pass
                        image_area = max(new_screen.areas, key=lambda a: a.width * a.height)
                        image_area.type = 'IMAGE_EDITOR'

                    if image_area:
                        pass
                        for space in image_area.spaces:
                            pass
                            if space.type == 'IMAGE_EDITOR':
                                pass
                                space.image = result
                                image_area.tag_redraw()
                                break
                    else:
                        pass
                        self.report({'WARNING'}, "Could not find area to display preview in new window")
                except Exception as window_err:
                    pass
                    self.report({'WARNING'}, f"Preview window open failed: {window_err}. Showing in current window.")

                    # Fallback to setting image in any existing image editor
                    for area in context.screen.areas:
                        pass
                        if area.type == 'IMAGE_EDITOR':
                            pass
                            for space in area.spaces:
                                pass
                                if space.type == 'IMAGE_EDITOR':
                                    pass
                                    space.image = result
                                    area.tag_redraw()
                                    break
                            break
                
                return {'FINISHED'}
            else:
                pass
                self.report({'ERROR'}, "Failed to generate preview - check console for details")
                return {'CANCELLED'}
                
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Preview failed: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'CANCELLED'}


class TEXT_TEXTURE_OT_generate_shader(Operator):
    pass
    """Generate complete shader with text texture"""
    bl_idname = "text_texture.generate_shader"
    bl_label = "Generate Shader"
    bl_description = "Generate a complete material shader with the text texture"
    bl_options = {'REGISTER', 'UNDO'}
    
    def execute(self, context):
        pass
        try:
            pass
            props = context.scene.text_texture_props
            
            # Generate texture first
            final_width = props.texture_width
            final_height = props.texture_height
            
            result = generate_texture_image(props, final_width, final_height)
            
            if not result or result == (None, None) or result[0] is None:
                pass
                self.report({'ERROR'}, "Failed to generate texture image")
                return {'CANCELLED'}
            
            # Extract diffuse and normal map images
            if isinstance(result, tuple):
                pass
                img, normal_map_img = result
            else:
                pass
                # Backward compatibility
                img = result
                normal_map_img = None
            
            if not img:
                pass
                self.report({'ERROR'}, "Failed to generate diffuse image")
                return {'CANCELLED'}
            
            
            # Create diffuse texture name with proper sanitization
            sanitized_text = _sanitize_text_for_name(props.text)
            img_name = f"TextTexture_{sanitized_text}"
            
            # Remove existing diffuse texture if it exists
            if img_name in bpy.data.images:
                pass
                bpy.data.images.remove(bpy.data.images[img_name])
            
            # Create new diffuse texture
            blender_img = bpy.data.images.new(img_name, final_width, final_height, alpha=True)
            
            # Directly copy pixel data from generated image to Blender image
            # The generated image is already in the correct format, just copy it
            blender_img.pixels[:] = img.pixels[:]
            blender_img.pack()
            
            # Create normal map texture if enabled and generated
            normal_map_blender_img = None
            if normal_map_img and props.generate_normal_map:
                pass
                # Use the same sanitized text for consistency
                normal_map_name = f"TextTexture_Normal_{sanitized_text}"
                
                # Remove existing normal map texture
                if normal_map_name in bpy.data.images:
                    pass
                    bpy.data.images.remove(bpy.data.images[normal_map_name])
                
                # Create new normal map image
                normal_map_blender_img = bpy.data.images.new(normal_map_name, final_width, final_height, alpha=False)
                normal_map_blender_img.colorspace_settings.name = 'Non-Color'  # Important for normal maps
                
                # Directly copy normal map pixel data
                # Copy RGB channels and set alpha to 1.0
                temp_pixels = list(normal_map_img.pixels[:])
                for i in range(3, len(temp_pixels), 4):
                    pass
                    temp_pixels[i] = 1.0  # Set alpha to 1.0 for normal maps
                normal_map_blender_img.pixels[:] = temp_pixels
                normal_map_blender_img.pack()
            else:
                pass
            
            # Create or get material using custom name or fallback
            material_name = _resolve_material_name(props, sanitized_text)

            if material_name in bpy.data.materials:
                pass
                mat = bpy.data.materials[material_name]
                # Clear existing nodes
                mat.node_tree.nodes.clear()
            else:
                pass
                mat = bpy.data.materials.new(name=material_name)
                mat.use_nodes = True
                mat.node_tree.nodes.clear()
            
            # Set up shader nodes
            nodes = mat.node_tree.nodes
            links = mat.node_tree.links
            
            # Create nodes
            output_node = nodes.new(type='ShaderNodeOutputMaterial')
            output_node.location = (400, 0)
            
            # Shader type based on settings
            if props.shader_type == 'PRINCIPLED':
                pass
                shader_node = nodes.new(type='ShaderNodeBsdfPrincipled')
                shader_node.location = (200, 0)
                
                # Create diffuse texture node
                tex_node = nodes.new(type='ShaderNodeTexImage')
                tex_node.image = blender_img
                tex_node.location = (-200, 0)
                
                # Connect texture to shader
                if props.shader_connection == 'BASE_COLOR':
                    pass
                    links.new(tex_node.outputs['Color'], shader_node.inputs['Base Color'])
                elif props.shader_connection == 'EMISSION':
                    links.new(tex_node.outputs['Color'], shader_node.inputs['Emission Color'])
                    shader_node.inputs['Emission Strength'].default_value = props.emission_strength
                elif props.shader_connection == 'ALPHA':
                    links.new(tex_node.outputs['Alpha'], shader_node.inputs['Alpha'])
                    # Also connect color for visibility
                    links.new(tex_node.outputs['Color'], shader_node.inputs['Base Color'])
                    mat.blend_method = 'BLEND'
                
                # Connect alpha if enabled
                if props.use_alpha:
                    pass
                    links.new(tex_node.outputs['Alpha'], shader_node.inputs['Alpha'])
                    mat.blend_method = 'BLEND'
                
                # Add normal map nodes if normal map was generated
                if normal_map_blender_img:
                    pass
                    # Create normal map texture node
                    normal_tex_node = nodes.new(type='ShaderNodeTexImage')
                    normal_tex_node.image = normal_map_blender_img
                    normal_tex_node.location = (-200, -300)
                    normal_tex_node.label = "Normal Map"
                    
                    # Create normal map node
                    normal_map_node = nodes.new(type='ShaderNodeNormalMap')
                    normal_map_node.location = (0, -300)
                    
                    # Connect normal map texture to normal map node
                    links.new(normal_tex_node.outputs['Color'], normal_map_node.inputs['Color'])
                    
                    # Connect normal map node to shader
                    links.new(normal_map_node.outputs['Normal'], shader_node.inputs['Normal'])
                    
                
                # Handle shadow/reflection disabling
                current_shader = shader_node
                final_x_location = 400
                
                # Disable shadows if enabled
                if props.disable_shadows:
                    pass
                    # Create light path node
                    light_path_node = nodes.new(type='ShaderNodeLightPath')
                    light_path_node.location = (50, 150)
                    
                    # Create transparent BSDF
                    transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    transparent_node.location = (200, 150)
                    
                    # Create mix shader for shadows
                    shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
                    shadow_mix_node.location = (350, 50)
                    
                    # Connect light path "Is Shadow Ray" to mix factor
                    links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
                    
                    # Mix principled BSDF with transparent BSDF
                    links.new(current_shader.outputs['BSDF'], shadow_mix_node.inputs[1])  # Shader 1 (not shadow)
                    links.new(transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2])  # Shader 2 (shadow)
                    
                    current_shader = shadow_mix_node
                    final_x_location = 500
                
                # Disable reflections if enabled
                if props.disable_reflections:
                    pass
                    # Create or reuse light path node
                    if not props.disable_shadows:
                        pass
                        light_path_node = nodes.new(type='ShaderNodeLightPath')
                        light_path_node.location = (50, 200)
                    
                    # Create transparent BSDF for reflections
                    reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    reflection_transparent_node.location = (200, 200)
                    
                    # Create mix shader for reflections
                    reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
                    reflection_mix_node.location = (final_x_location - 50, 100)
                    
                    # Connect light path "Is Reflection Ray" to mix factor
                    links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    links.new(current_shader.outputs['Shader' if props.disable_shadows else 'BSDF'], reflection_mix_node.inputs[1])  # Shader 1 (not reflection)
                    links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2])  # Shader 2 (reflection)
                    
                    current_shader = reflection_mix_node
                    final_x_location = final_x_location + 50
                
                # Disable backfacing if enabled
                if props.disable_backfacing:
                    pass
                    # Create or reuse light path node
                    if not props.disable_shadows and not props.disable_reflections:
                        pass
                        light_path_node = nodes.new(type='ShaderNodeLightPath')
                        light_path_node.location = (50, 250)
                    
                    # Create geometry node for backfacing detection
                    geometry_node = nodes.new(type='ShaderNodeNewGeometry')
                    geometry_node.location = (50, 300)
                    
                    # Create transparent BSDF for backfaces
                    backface_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    backface_transparent_node.location = (200, 250)
                    
                    # Create mix shader for backfaces
                    backface_mix_node = nodes.new(type='ShaderNodeMixShader')
                    backface_mix_node.location = (final_x_location - 50, 150)
                    
                    # Connect geometry "Backfacing" to mix factor
                    links.new(geometry_node.outputs['Backfacing'], backface_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    current_output = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'BSDF'
                    links.new(current_shader.outputs[current_output], backface_mix_node.inputs[1])  # Shader 1 (front face)
                    links.new(backface_transparent_node.outputs['BSDF'], backface_mix_node.inputs[2])  # Shader 2 (backface)
                    
                    current_shader = backface_mix_node
                    final_x_location = final_x_location + 50
                
                # Update output node position
                output_node.location = (final_x_location, 0)
                
                # Connect final shader to output
                output_socket = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'BSDF'
                links.new(current_shader.outputs[output_socket], output_node.inputs['Surface'])
                
            elif props.shader_type == 'EMISSION':
                shader_node = nodes.new(type='ShaderNodeEmission')
                shader_node.location = (200, 0)
                
                # Create texture node
                tex_node = nodes.new(type='ShaderNodeTexImage')
                tex_node.image = blender_img
                tex_node.location = (-200, 0)
                
                # Connect texture to emission
                links.new(tex_node.outputs['Color'], shader_node.inputs['Color'])
                shader_node.inputs['Strength'].default_value = props.emission_strength
                
                # Handle shadow/reflection disabling
                current_shader = shader_node
                final_x_location = 400
                
                # Disable shadows if enabled
                if props.disable_shadows:
                    pass
                    # Create light path node
                    light_path_node = nodes.new(type='ShaderNodeLightPath')
                    light_path_node.location = (50, 150)
                    
                    # Create transparent BSDF
                    transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    transparent_node.location = (200, 150)
                    
                    # Create mix shader for shadows
                    shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
                    shadow_mix_node.location = (350, 50)
                    
                    # Connect light path "Is Shadow Ray" to mix factor
                    links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
                    
                    # Mix emission with transparent BSDF
                    links.new(current_shader.outputs['Emission'], shadow_mix_node.inputs[1])  # Shader 1 (not shadow)
                    links.new(transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2])  # Shader 2 (shadow)
                    
                    current_shader = shadow_mix_node
                    final_x_location = 500
                
                # Disable reflections if enabled
                if props.disable_reflections:
                    pass
                    # Create or reuse light path node
                    if not props.disable_shadows:
                        pass
                        light_path_node = nodes.new(type='ShaderNodeLightPath')
                        light_path_node.location = (50, 200)
                    
                    # Create transparent BSDF for reflections
                    reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    reflection_transparent_node.location = (200, 200)
                    
                    # Create mix shader for reflections
                    reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
                    reflection_mix_node.location = (final_x_location - 50, 100)
                    
                    # Connect light path "Is Reflection Ray" to mix factor
                    links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    links.new(current_shader.outputs['Shader' if props.disable_shadows else 'Emission'], reflection_mix_node.inputs[1])  # Shader 1 (not reflection)
                    links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2])  # Shader 2 (reflection)
                    
                    current_shader = reflection_mix_node
                    final_x_location = final_x_location + 50
                
                # Update output node position
                output_node.location = (final_x_location, 0)
                
                # Connect final shader to output
                output_socket = 'Shader' if (props.disable_shadows or props.disable_reflections) else 'Emission'
                links.new(current_shader.outputs[output_socket], output_node.inputs['Surface'])
                
            elif props.shader_type == 'TRANSPARENT':
                shader_node = nodes.new(type='ShaderNodeBsdfTransparent')
                mix_node = nodes.new(type='ShaderNodeMixShader')
                principled_node = nodes.new(type='ShaderNodeBsdfPrincipled')
                
                shader_node.location = (200, 100)
                mix_node.location = (350, 0)
                principled_node.location = (200, -100)
                
                # Create texture node
                tex_node = nodes.new(type='ShaderNodeTexImage')
                tex_node.image = blender_img
                tex_node.location = (-200, 0)
                
                # Connect texture
                links.new(tex_node.outputs['Color'], principled_node.inputs['Base Color'])
                links.new(tex_node.outputs['Alpha'], mix_node.inputs['Fac'])
                
                # Connect shaders to mix
                links.new(shader_node.outputs['BSDF'], mix_node.inputs[1])
                links.new(principled_node.outputs['BSDF'], mix_node.inputs[2])
                
                # Handle shadow/reflection disabling
                current_shader = mix_node
                final_x_location = 500
                
                # Disable shadows if enabled
                if props.disable_shadows:
                    pass
                    # Create light path node
                    light_path_node = nodes.new(type='ShaderNodeLightPath')
                    light_path_node.location = (50, 200)
                    
                    # Create transparent BSDF for shadows (reuse existing transparent node)
                    shadow_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    shadow_transparent_node.location = (350, 150)
                    
                    # Create mix shader for shadows
                    shadow_mix_node = nodes.new(type='ShaderNodeMixShader')
                    shadow_mix_node.location = (500, 50)
                    
                    # Connect light path "Is Shadow Ray" to mix factor
                    links.new(light_path_node.outputs['Is Shadow Ray'], shadow_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    links.new(current_shader.outputs['Shader'], shadow_mix_node.inputs[1])  # Shader 1 (not shadow)
                    links.new(shadow_transparent_node.outputs['BSDF'], shadow_mix_node.inputs[2])  # Shader 2 (shadow)
                    
                    current_shader = shadow_mix_node
                    final_x_location = 650
                
                # Disable reflections if enabled
                if props.disable_reflections:
                    pass
                    # Create or reuse light path node
                    if not props.disable_shadows:
                        pass
                        light_path_node = nodes.new(type='ShaderNodeLightPath')
                        light_path_node.location = (50, 250)
                    
                    # Create transparent BSDF for reflections
                    reflection_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    reflection_transparent_node.location = (350, 200)
                    
                    # Create mix shader for reflections
                    reflection_mix_node = nodes.new(type='ShaderNodeMixShader')
                    reflection_mix_node.location = (final_x_location - 50, 100)
                    
                    # Connect light path "Is Reflection Ray" to mix factor
                    links.new(light_path_node.outputs['Is Reflection Ray'], reflection_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    links.new(current_shader.outputs['Shader'], reflection_mix_node.inputs[1])  # Shader 1 (not reflection)
                    links.new(reflection_transparent_node.outputs['BSDF'], reflection_mix_node.inputs[2])  # Shader 2 (reflection)
                    
                    current_shader = reflection_mix_node
                    final_x_location = final_x_location + 50
                
                # Disable backfacing if enabled
                if props.disable_backfacing:
                    pass
                    # Create or reuse light path node
                    if not props.disable_shadows and not props.disable_reflections:
                        pass
                        light_path_node = nodes.new(type='ShaderNodeLightPath')
                        light_path_node.location = (50, 300)
                    
                    # Create geometry node for backfacing detection
                    geometry_node = nodes.new(type='ShaderNodeNewGeometry')
                    geometry_node.location = (50, 350)
                    
                    # Create transparent BSDF for backfaces
                    backface_transparent_node = nodes.new(type='ShaderNodeBsdfTransparent')
                    backface_transparent_node.location = (350, 300)
                    
                    # Create mix shader for backfaces
                    backface_mix_node = nodes.new(type='ShaderNodeMixShader')
                    backface_mix_node.location = (final_x_location + 50, 150)
                    
                    # Connect geometry "Backfacing" to mix factor
                    links.new(geometry_node.outputs['Backfacing'], backface_mix_node.inputs['Fac'])
                    
                    # Mix current shader with transparent BSDF
                    links.new(current_shader.outputs['Shader'], backface_mix_node.inputs[1])  # Shader 1 (front face)
                    links.new(backface_transparent_node.outputs['BSDF'], backface_mix_node.inputs[2])  # Shader 2 (backface)
                    
                    current_shader = backface_mix_node
                    final_x_location = final_x_location + 150
                
                # Update output node position
                output_node.location = (final_x_location, 0)
                
                # Connect final shader to output
                links.new(current_shader.outputs['Shader'], output_node.inputs['Surface'])
                
                mat.blend_method = 'BLEND'
            
            # Assign material to active object if enabled and possible
            if props.auto_assign_material and context.active_object and context.active_object.type in ['MESH', 'CURVE']:
                pass
                obj = context.active_object
                # Check if material slot exists, reuse if found, otherwise create new one
                slot_found = False
                for i, slot in enumerate(obj.material_slots):
                    pass
                    if slot.material and slot.material.name == material_name:
                        pass
                        # Material already exists in a slot, update it
                        obj.material_slots[i].material = mat
                        slot_found = True
                        break
                
                if not slot_found:
                    pass
                    # Create new material slot if not found
                    obj.data.materials.append(mat)
                
                self.report({'INFO'}, f"Generated shader '{material_name}' and assigned to {obj.name}")
            else:
                pass
                self.report({'INFO'}, f"Generated shader '{material_name}' (assign manually to object)")
            
            # Switch to Shader Editor if available and set material
            for area in context.screen.areas:
                pass
                if area.type == 'NODE_EDITOR':
                    pass
                    for space in area.spaces:
                        pass
                        if space.type == 'NODE_EDITOR':
                            pass
                            space.shader_type = 'OBJECT'
                            # In Blender 5+, space.id is read-only.
                            # Instead, point the node_tree directly or
                            # set the active material on the object so
                            # the editor follows it automatically.
                            try:
                                space.node_tree = mat.node_tree
                            except (AttributeError, TypeError):
                                pass
                            # Also ensure the active object shows this
                            # material so the editor stays in sync.
                            if context.active_object and hasattr(context.active_object, 'active_material'):
                                context.active_object.active_material = mat
                            area.tag_redraw()
                            break
            
            return {'FINISHED'}
            
        except Exception as e:
            pass
            self.report({'ERROR'}, f"Shader generation failed: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'CANCELLED'}


class TEXT_TEXTURE_OT_generate_plane(Operator):
    """Generate shader and create a plane with matching aspect ratio"""
    bl_idname = "text_texture.generate_plane"
    bl_label = "Create Plane"
    bl_description = "Generate the material and add a plane matching the texture aspect ratio"
    bl_options = {'REGISTER', 'UNDO'}

    def execute(self, context):
        try:
            props = context.scene.text_texture_props

            result = bpy.ops.text_texture.generate_shader()
            if 'FINISHED' not in result:
                self.report({'ERROR'}, "Shader generation failed, plane not created")
                return {'CANCELLED'}

            sanitized_text = _sanitize_text_for_name(props.text)
            material_name = _resolve_material_name(props, sanitized_text)
            mat = bpy.data.materials.get(material_name)

            if not mat:
                self.report({'ERROR'}, f"Material '{material_name}' not found after generation")
                return {'CANCELLED'}

            width = float(getattr(props, "texture_width", 1) or 1)
            height = float(getattr(props, "texture_height", 1) or 1)
            aspect = width / height if height and isfinite(width / height) else 1.0

            bpy.ops.mesh.primitive_plane_add(size=1)
            plane = context.active_object

            if not plane or plane.type != 'MESH':
                self.report({'ERROR'}, "Failed to create plane object")
                return {'CANCELLED'}

            if aspect > 1.0:
                plane.scale.x *= aspect
            elif aspect > 0.0:
                plane.scale.y *= 1.0 / aspect

            subdiv = plane.modifiers.new(name="TTG Subdivision", type='SUBSURF')
            if hasattr(subdiv, "subdivision_type"):
                subdiv.subdivision_type = 'SIMPLE'
            subdiv.levels = 1
            subdiv.render_levels = 1

            if plane.data.materials:
                plane.data.materials[0] = mat
            else:
                plane.data.materials.append(mat)

            self.report({'INFO'}, f"Created plane '{plane.name}' with '{material_name}'")
            return {'FINISHED'}

        except Exception as e:
            self.report({'ERROR'}, f"Plane creation failed: {str(e)}")
            import traceback
            traceback.print_exc()
            return {'CANCELLED'}


class TEXT_TEXTURE_OT_reset_settings(Operator):
    """Reset all Text Texture settings to their defaults"""
    bl_idname = "text_texture.reset_settings"
    bl_label = "Reset Text Texture Settings"
    bl_description = "Restore all Text Texture settings to their default values"
    bl_options = {'REGISTER', 'UNDO'}

    @classmethod
    def poll(cls, context):
        return hasattr(context.scene, "text_texture_props")

    @staticmethod
    def _collect_property_names(props):
        names = set()

        cls = props.__class__
        annotations = getattr(cls, "__annotations__", {})
        names.update(annotations.keys())

        bl_rna = getattr(props, "bl_rna", None)
        if bl_rna and hasattr(bl_rna, "properties"):
            try:
                for prop in bl_rna.properties:
                    identifier = getattr(prop, "identifier", None)
                    if identifier:
                        names.add(identifier)
            except TypeError:
                pass
            except AttributeError:
                pass

        names.discard("rna_type")
        return names

    def execute(self, context):
        props = getattr(context.scene, "text_texture_props", None)
        if not props:
            self.report({'ERROR'}, "Text Texture properties not found")
            return {'CANCELLED'}

        property_names = self._collect_property_names(props)

        for name in property_names:
            attr = getattr(props, name, None)
            if hasattr(attr, "clear") and callable(attr.clear):
                try:
                    attr.clear()
                except Exception:
                    pass

            try:
                props.property_unset(name)
            except AttributeError:
                continue
            except TypeError:
                continue
            except ValueError:
                continue

        self.report({'INFO'}, "Text Texture settings reset to defaults")
        return {'FINISHED'}


# Export all operator classes for wildcard imports
__all__ = [
    'TEXT_TEXTURE_OT_generate',
    'TEXT_TEXTURE_OT_preview',
    'TEXT_TEXTURE_OT_generate_shader',
    'TEXT_TEXTURE_OT_generate_plane',
    'TEXT_TEXTURE_OT_reset_settings',
]
