"""
地形材质和纹理系统
提供完整的地形材质和纹理管理功能，支持多层纹理混合、程序化材质生成等高级功能
"""

import os
from panda3d.core import Material, Texture, TextureStage, Vec4, PNMImage, Filename
from panda3d.core import GeoMipTerrain, Shader, ShaderAttrib

class TerrainMaterials:
    """
    地形材质和纹理系统类
    提供完整的地形材质和纹理管理功能
    """
    
    def __init__(self, world):
        self.world = world
        self.materials = {}  # 存储材质
        self.terrain_layers = {}  # 存储地形图层信息
        self.texture_atlases = {}  # 存储纹理图集
        
    def create_material(self, name, ambient=(0.5, 0.5, 0.5, 1.0), 
                       diffuse=(0.8, 0.8, 0.8, 1.0), 
                       specular=(0.2, 0.2, 0.2, 1.0), 
                       shininess=5.0,
                       emissive=(0.0, 0.0, 0.0, 1.0)):
        """
        创建新材质
        """
        material = Material()
        material.setName(name)
        material.setAmbient(ambient)
        material.setDiffuse(diffuse)
        material.setSpecular(specular)
        material.setShininess(shininess)
        material.setEmission(emissive)
        
        self.materials[name] = material
        print(f"创建材质: {name}")
        return material
        
    def apply_material_to_terrain(self, terrain_node, material_name):
        """
        为地形应用材质
        """
        if material_name in self.materials:
            material = self.materials[material_name]
            terrain_node.setMaterial(material)
            print(f"为地形应用材质: {material_name}")
            return True
        else:
            print(f"材质 {material_name} 不存在")
            return False
            
    def load_texture(self, texture_path):
        """
        加载纹理
        """
        try:
            # 检查文件是否存在
            if not os.path.exists(texture_path):
                print(f"纹理文件不存在: {texture_path}")
                return None
                
            texture = self.world.loader.loadTexture(texture_path)
            if texture:
                print(f"纹理加载成功: {texture_path}")
                return texture
            else:
                print(f"纹理加载失败: {texture_path}")
                return None
        except Exception as e:
            print(f"加载纹理时出错: {e}")
            return None
            
    def apply_texture_to_terrain(self, terrain_node, texture_path, texture_stage_name="diffuse"):
        """
        为地形应用纹理
        """
        texture = self.load_texture(texture_path)
        if texture:
            # 创建纹理阶段
            texture_stage = TextureStage(texture_stage_name)
            # 应用纹理到地形
            terrain_node.setTexture(texture_stage, texture)
            print(f"为地形应用纹理: {texture_path}")
            return True
        return False
        
    def apply_multi_texture_to_terrain(self, terrain_node, texture_paths, blend_factors=None):
        """
        为地形应用多纹理混合
        texture_paths: 纹理路径列表
        blend_factors: 混合因子列表，控制每个纹理的混合权重
        """
        if not texture_paths:
            print("纹理路径列表为空")
            return False
            
        # 如果没有提供混合因子，使用默认值
        if blend_factors is None:
            blend_factors = [1.0] * len(texture_paths)
            
        # 确保混合因子数量与纹理数量一致
        if len(blend_factors) != len(texture_paths):
            print("混合因子数量与纹理数量不匹配")
            return False
            
        # 应用每个纹理
        for i, (texture_path, blend_factor) in enumerate(zip(texture_paths, blend_factors)):
            texture = self.load_texture(texture_path)
            if texture:
                # 为每个纹理创建不同的纹理阶段
                texture_stage = TextureStage(f"texture_{i}")
                texture_stage.setSort(i)  # 设置排序
                
                # 应用纹理
                terrain_node.setTexture(texture_stage, texture)
                
                # 如果支持，设置混合因子
                if hasattr(texture_stage, 'setBlendFactor'):
                    texture_stage.setBlendFactor(blend_factor)
                    
        print(f"为地形应用 {len(texture_paths)} 个多纹理混合")
        return True
        
    def create_terrain_layer_system(self, terrain_node, layer_configs):
        """
        创建地形图层系统
        layer_configs: 图层配置列表，每个配置包含纹理路径、混合贴图等信息
        """
        try:
            # 清除现有的地形纹理
            terrain_node.clearTexture()
            
            # 为每个图层创建纹理阶段
            for i, layer_config in enumerate(layer_configs):
                # 加载漫反射纹理
                if 'diffuse' in layer_config:
                    diffuse_texture = self.load_texture(layer_config['diffuse'])
                    if diffuse_texture:
                        diffuse_stage = TextureStage(f"diffuse_{i}")
                        diffuse_stage.setSort(i * 10)  # 设置排序
                        terrain_node.setTexture(diffuse_stage, diffuse_texture)
                        
                # 加载法线纹理
                if 'normal' in layer_config:
                    normal_texture = self.load_texture(layer_config['normal'])
                    if normal_texture:
                        normal_stage = TextureStage(f"normal_{i}")
                        normal_stage.setMode(TextureStage.M_normal)  # 设置为法线贴图模式
                        normal_stage.setSort(i * 10 + 1)
                        terrain_node.setTexture(normal_stage, normal_texture)
                        
                # 加载混合纹理（控制图层混合）
                if 'blend' in layer_config:
                    blend_texture = self.load_texture(layer_config['blend'])
                    if blend_texture:
                        blend_stage = TextureStage(f"blend_{i}")
                        blend_stage.setMode(TextureStage.M_blend)  # 设置为混合模式
                        blend_stage.setSort(i * 10 + 2)
                        terrain_node.setTexture(blend_stage, blend_texture)
                        
            # 保存图层信息
            self.terrain_layers[terrain_node] = layer_configs
            
            print(f"为地形创建 {len(layer_configs)} 个图层系统")
            return True
            
        except Exception as e:
            print(f"创建地形图层系统时出错: {e}")
            return False
            
    def update_terrain_layer(self, terrain_node, layer_index, layer_config):
        """
        更新地形图层
        """
        if terrain_node not in self.terrain_layers:
            print("地形没有图层系统")
            return False
            
        if layer_index >= len(self.terrain_layers[terrain_node]):
            print("图层索引超出范围")
            return False
            
        # 更新图层配置
        self.terrain_layers[terrain_node][layer_index] = layer_config
        
        # 重新应用纹理
        return self.create_terrain_layer_system(terrain_node, self.terrain_layers[terrain_node])
        
    def create_procedural_terrain_material(self, terrain_node, 
                                          base_color=(0.5, 0.6, 0.3, 1.0),
                                          roughness=0.8,
                                          metallic=0.1,
                                          uv_scale=(1.0, 1.0),
                                          use_normal_map=True):
        """
        创建程序化地形材质
        """
        try:
            # 创建基础材质
            material = Material()
            material.setName(f"procedural_terrain_{id(terrain_node)}")
            material.setBaseColor(base_color)
            material.setRoughness(roughness)
            material.setMetallic(metallic)
            
            # 应用材质
            terrain_node.setMaterial(material)
            
            # 如果使用RenderPipeline，可以应用PBR效果
            if hasattr(self.world, 'render_pipeline'):
                try:
                    effect_options = {
                        "normal_mapping": use_normal_map,
                        "parallax_mapping": False,
                        "uv_scale": uv_scale
                    }
                    
                    self.world.render_pipeline.set_effect(
                        terrain_node,
                        "effects/terrain.yaml",  # 假设有专门的地形效果文件
                        effect_options,
                        10  # 排序值
                    )
                    print("应用PBR地形效果")
                except Exception as e:
                    print(f"应用PBR效果失败: {e}")
                    
            self.materials[material.getName()] = material
            print(f"创建程序化地形材质: {material.getName()}")
            return material
            
        except Exception as e:
            print(f"创建程序化地形材质时出错: {e}")
            return None
            
    def create_texture_atlas(self, atlas_name, texture_paths, atlas_size=(1024, 1024)):
        """
        创建纹理图集
        """
        try:
            # 创建新的PNMImage作为图集
            atlas_image = PNMImage(atlas_size[0], atlas_size[1])
            atlas_image.fill(0, 0, 0)  # 填充黑色背景
            
            # 计算每个纹理在图集中的位置
            num_textures = len(texture_paths)
            grid_size = int(math.ceil(math.sqrt(num_textures)))
            tile_width = atlas_size[0] // grid_size
            tile_height = atlas_size[1] // grid_size
            
            # 存储纹理坐标信息
            texture_coords = {}
            
            # 将每个纹理复制到图集中
            for i, texture_path in enumerate(texture_paths):
                if not os.path.exists(texture_path):
                    print(f"纹理文件不存在: {texture_path}")
                    continue
                    
                # 加载纹理图像
                texture_image = PNMImage(Filename.fromOsSpecific(texture_path))
                if texture_image.empty():
                    print(f"无法加载纹理图像: {texture_path}")
                    continue
                    
                # 计算在图集中的位置
                row = i // grid_size
                col = i % grid_size
                x_offset = col * tile_width
                y_offset = row * tile_height
                
                # 将纹理复制到图集
                atlas_image.blendSubImage(texture_image, x_offset, y_offset, 0, 0, 
                                         min(tile_width, texture_image.getXSize()), 
                                         min(tile_height, texture_image.getYSize()))
                
                # 计算纹理坐标
                u0 = x_offset / atlas_size[0]
                v0 = y_offset / atlas_size[1]
                u1 = (x_offset + tile_width) / atlas_size[0]
                v1 = (y_offset + tile_height) / atlas_size[1]
                
                texture_coords[os.path.basename(texture_path)] = (u0, v0, u1, v1)
                
            # 保存图集到临时文件
            temp_path = f"/tmp/{atlas_name}_atlas.png"
            atlas_image.write(Filename.fromOsSpecific(temp_path))
            
            # 加载图集纹理
            atlas_texture = self.world.loader.loadTexture(temp_path)
            if atlas_texture:
                self.texture_atlases[atlas_name] = {
                    'texture': atlas_texture,
                    'coords': texture_coords,
                    'size': atlas_size
                }
                print(f"创建纹理图集: {atlas_name}")
                return atlas_texture
                
        except Exception as e:
            print(f"创建纹理图集时出错: {e}")
            return None
            
    def apply_texture_atlas_to_terrain(self, terrain_node, atlas_name, texture_name):
        """
        为地形应用纹理图集中的特定纹理
        """
        if atlas_name not in self.texture_atlases:
            print(f"纹理图集 {atlas_name} 不存在")
            return False
            
        atlas_info = self.texture_atlases[atlas_name]
        if texture_name not in atlas_info['coords']:
            print(f"纹理 {texture_name} 不在图集中")
            return False
            
        # 应用图集纹理
        texture_stage = TextureStage("atlas_diffuse")
        terrain_node.setTexture(texture_stage, atlas_info['texture'])
        
        # 设置纹理坐标偏移（需要着色器支持）
        coords = atlas_info['coords'][texture_name]
        print(f"应用纹理图集 {atlas_name} 中的 {texture_name}")
        return True
        
    def update_material_property(self, material_name, property_name, value):
        """
        更新材质属性
        """
        if material_name in self.materials:
            material = self.materials[material_name]
            setter_name = f'set{property_name.capitalize()}'
            if hasattr(material, setter_name):
                # 特殊处理某些属性
                if property_name in ['ambient', 'diffuse', 'specular', 'emission'] and isinstance(value, (list, tuple)):
                    getattr(material, setter_name)(Vec4(*value))
                else:
                    getattr(material, setter_name)(value)
                print(f"更新材质 {material_name} 的属性 {property_name} 为 {value}")
                return True
        print(f"无法更新材质 {material_name} 的属性 {property_name}")
        return False
        
    def get_material_info(self, material_name):
        """
        获取材质信息
        """
        if material_name in self.materials:
            material = self.materials[material_name]
            return {
                "name": material.getName(),
                "ambient": material.getAmbient(),
                "diffuse": material.getDiffuse(),
                "specular": material.getSpecular(),
                "shininess": material.getShininess(),
                "emissive": material.getEmission()
            }
        return None
        
    def create_splatting_material(self, terrain_node, splat_map_path, texture_paths):
        """
        创建地形分层混合材质（Splating）
        splat_map_path: 控制混合的灰度图路径
        texture_paths: 纹理路径列表（通常4个：R、G、B、A通道分别控制）
        """
        try:
            # 加载混合图
            splat_texture = self.load_texture(splat_map_path)
            if not splat_texture:
                print("无法加载混合图")
                return False
                
            # 加载纹理图集
            for i, texture_path in enumerate(texture_paths):
                texture = self.load_texture(texture_path)
                if texture:
                    texture_stage = TextureStage(f"splat_texture_{i}")
                    texture_stage.setSort(i)
                    terrain_node.setTexture(texture_stage, texture)
                    
            # 应用混合图
            splat_stage = TextureStage("splat_map")
            splat_stage.setMode(TextureStage.M_blend)
            terrain_node.setTexture(splat_stage, splat_texture)
            
            print(f"创建分层混合材质，使用 {len(texture_paths)} 个纹理")
            return True
            
        except Exception as e:
            print(f"创建分层混合材质时出错: {e}")
            return False
            
    def apply_terrain_shader(self, terrain_node, shader_path, shader_params=None):
        """
        为地形应用自定义着色器
        """
        try:
            # 加载着色器
            shader = Shader.load(Shader.SL_GLSL, shader_path + ".vert", shader_path + ".frag")
            
            # 应用着色器
            terrain_node.setShader(shader)
            
            # 设置着色器参数
            if shader_params:
                for param_name, param_value in shader_params.items():
                    terrain_node.setShaderInput(param_name, param_value)
                    
            print(f"为地形应用着色器: {shader_path}")
            return True
            
        except Exception as e:
            print(f"应用着色器时出错: {e}")
            return False