"""
粒子渲染器
负责粒子的可视化渲染
"""

import math
import numpy as np
from typing import List, Dict
from panda3d.core import (
    GeomNode, Geom, GeomVertexFormat, GeomVertexData, GeomVertexWriter,
    GeomPoints, GeomTriangles, RenderState, ColorBlendAttrib, 
    TransparencyAttrib, DepthWriteAttrib, Vec3, Vec4, Point3,
    CardMaker, Texture, TextureStage, Material, LColor, NodePath
)
from direct.showbase.ShowBaseGlobal import globalClock

from .particle_emitter import ParticleEmitter, Particle

class ParticleRenderer:
    """
    粒子渲染器
    使用Panda3D的几何体系统高效渲染大量粒子
    """
    
    def __init__(self, world):
        """
        初始化粒子渲染器
        
        Args:
            world: 3D世界对象
        """
        self.world = world
        self.enabled = False
        
        # 渲染节点
        self.render_root = None
        self.particle_nodes: Dict[str, GeomNode] = {}
        
        # 渲染设置
        self.use_billboards = True  # 使用广告牌渲染
        self.use_point_sprites = False  # 使用点精灵
        self.batch_rendering = True  # 批量渲染
        self.max_particles_per_batch = 1000  # 每批次最大粒子数
        
        # 材质和纹理
        self.default_material = None
        self.particle_textures = {}
        self.custom_textures = {}  # 用户自定义纹理
        
        # 渲染统计
        self.render_stats = {
            'draw_calls': 0,
            'vertices_rendered': 0,
            'triangles_rendered': 0,
            'render_time': 0.0
        }
        
        # 注册的发射器
        self.registered_emitters: Dict[str, ParticleEmitter] = {}
        
        # 渲染优化
        self.use_vertex_colors = True
        self.use_alpha_fading = True
        
        print("✓ 粒子渲染器初始化完成")
        
    def enable(self):
        """启用渲染器"""
        if self.enabled:
            return
            
        self.enabled = True
        
        # 创建渲染根节点
        self.render_root = self.world.render.attachNewNode("particle_system")
        
        # 设置渲染状态
        self._setup_render_states()
        
        # 创建默认材质
        self._create_default_material()
        
        print("✓ 粒子渲染器已启用")
        
    def disable(self):
        """禁用渲染器"""
        if not self.enabled:
            return
            
        self.enabled = False
        
        # 清理渲染节点
        if self.render_root:
            self.render_root.removeNode()
            self.render_root = None
            
        self.particle_nodes.clear()
        
        print("✓ 粒子渲染器已禁用")
        
    def cleanup(self):
        """清理资源"""
        self.disable()
        self.registered_emitters.clear()
        self.particle_textures.clear()
        self.custom_textures.clear()
        
    def _setup_render_states(self):
        """设置渲染状态"""
        if not self.render_root:
            return
            
        # 启用透明度
        self.render_root.setTransparency(TransparencyAttrib.MAlpha)
        
        # 设置混合模式（加法混合用于发光效果）
        self.render_root.setAttrib(ColorBlendAttrib.make(
            ColorBlendAttrib.MAdd,
            ColorBlendAttrib.OIncomingAlpha,
            ColorBlendAttrib.OOne
        ))
        
        # 禁用深度写入（但保持深度测试）
        self.render_root.setAttrib(DepthWriteAttrib.make(DepthWriteAttrib.MOff))
        
        # 设置渲染顺序（在透明物体之后渲染）
        self.render_root.setBin("fixed", 100)
        
    def _create_default_material(self):
        """创建默认材质"""
        self.default_material = Material()
        self.default_material.setEmission(LColor(1, 1, 1, 1))
        self.default_material.setAmbient(LColor(0.2, 0.2, 0.2, 1))
        self.default_material.setDiffuse(LColor(0.8, 0.8, 0.8, 1))
        self.default_material.setSpecular(LColor(0, 0, 0, 1))
        self.default_material.setShininess(1.0)
        
    def register_emitter(self, emitter: ParticleEmitter):
        """注册粒子发射器"""
        self.registered_emitters[emitter.emitter_id] = emitter
        
        # 为发射器创建渲染节点
        self._create_emitter_render_node(emitter)
        
    def unregister_emitter(self, emitter: ParticleEmitter):
        """注销粒子发射器"""
        emitter_id = emitter.emitter_id
        
        if emitter_id in self.registered_emitters:
            del self.registered_emitters[emitter_id]
            
        # 移除渲染节点
        if emitter_id in self.particle_nodes:
            node = self.particle_nodes[emitter_id]
            if node.getParent(0):
                node.getParent(0).removeNode()
            del self.particle_nodes[emitter_id]
            
    def _create_emitter_render_node(self, emitter: ParticleEmitter):
        """为发射器创建渲染节点"""
        if not self.enabled or not self.render_root:
            return
            
        emitter_id = emitter.emitter_id
        
        # 创建几何节点
        geom_node = GeomNode(f"particles_{emitter_id}")
        node_path = self.render_root.attachNewNode(geom_node)
        
        # 应用材质
        if self.default_material:
            node_path.setMaterial(self.default_material)
            
        # 根据粒子类型设置纹理
        self._apply_particle_texture(node_path, emitter.emitter_type)
        
        self.particle_nodes[emitter_id] = geom_node
        
    def _apply_particle_texture(self, node_path, particle_type: str):
        """应用粒子纹理"""
        # 这里可以根据粒子类型加载不同的纹理
        # 目前使用程序生成的简单纹理
        
        if particle_type == 'fire':
            # 火焰效果：橙红色渐变
            node_path.setColor(1.0, 0.5, 0.1, 0.8)
        elif particle_type == 'smoke':
            # 烟雾效果：灰色
            node_path.setColor(0.6, 0.6, 0.6, 0.5)
        elif particle_type == 'explosion':
            # 爆炸效果：亮黄色
            node_path.setColor(1.0, 0.8, 0.2, 0.9)
        elif particle_type == 'water':
            # 水花效果：蓝色
            node_path.setColor(0.2, 0.6, 1.0, 0.7)
        else:
            # 默认：白色
            node_path.setColor(1.0, 1.0, 1.0, 0.8)
            
    def update(self, dt: float):
        """更新渲染"""
        if not self.enabled:
            return
            
        import time
        start_time = time.time()
            
        # 重置统计信息
        self.render_stats['draw_calls'] = 0
        self.render_stats['vertices_rendered'] = 0
        self.render_stats['triangles_rendered'] = 0
        
        # 更新每个发射器的渲染
        for emitter_id, emitter in self.registered_emitters.items():
            if emitter_id in self.particle_nodes:
                self._update_emitter_rendering(emitter)
                
        self.render_stats['render_time'] = time.time() - start_time
        
    def _update_emitter_rendering(self, emitter: ParticleEmitter):
        """更新发射器的渲染"""
        emitter_id = emitter.emitter_id
        geom_node = self.particle_nodes.get(emitter_id)
        
        if not geom_node:
            return
            
        # 获取活跃粒子
        particles = emitter.get_particles()
        
        if not particles:
            # 清空几何体
            geom_node.removeAllGeoms()
            return
            
        # 分批渲染大量粒子
        if len(particles) > self.max_particles_per_batch:
            # 分批处理
            for i in range(0, len(particles), self.max_particles_per_batch):
                batch_particles = particles[i:i + self.max_particles_per_batch]
                if self.use_billboards:
                    self._create_billboard_geometry(geom_node, batch_particles)
                else:
                    self._create_point_geometry(geom_node, batch_particles)
                self.render_stats['draw_calls'] += 1
        else:
            # 单次渲染
            if self.use_billboards:
                self._create_billboard_geometry(geom_node, particles)
            else:
                self._create_point_geometry(geom_node, particles)
            self.render_stats['draw_calls'] += 1
        
    def _create_billboard_geometry(self, geom_node: GeomNode, particles: List[Particle]):
        """创建广告牌几何体"""
        if not particles:
            return
            
        # 清除现有几何体
        geom_node.removeAllGeoms()
        
        # 创建顶点格式
        if self.use_vertex_colors:
            vertex_format = GeomVertexFormat.getV3n3c4t2()
        else:
            vertex_format = GeomVertexFormat.getV3n3t2()
            
        vertex_data = GeomVertexData("particles", vertex_format, Geom.UHStatic)
        vertex_data.setNumRows(len(particles) * 4)  # 每个粒子4个顶点
        
        # 创建顶点写入器
        vertex_writer = GeomVertexWriter(vertex_data, "vertex")
        normal_writer = GeomVertexWriter(vertex_data, "normal")
        if self.use_vertex_colors:
            color_writer = GeomVertexWriter(vertex_data, "color")
        texcoord_writer = GeomVertexWriter(vertex_data, "texcoord")
        
        # 获取摄像机位置（用于广告牌对齐）
        camera_pos = self.world.camera.getPos()
        
        # 为每个粒子创建四边形
        for i, particle in enumerate(particles):
            if not particle.is_alive():
                continue
                
            # 计算广告牌方向
            to_camera = camera_pos - particle.position
            to_camera.normalize()
            
            # 创建局部坐标系（简化版本）
            up = Vec3(0, 0, 1)
            right = to_camera.cross(up)
            if right.length() < 0.001:  # 避免与向上向量平行
                right = Vec3(1, 0, 0)
            else:
                right.normalize()
            up = right.cross(to_camera)
            up.normalize()
            
            # 计算四个顶点位置
            half_size = particle.size * 0.5
            positions = [
                particle.position + (-right - up) * half_size,  # 左下
                particle.position + (right - up) * half_size,   # 右下
                particle.position + (right + up) * half_size,   # 右上
                particle.position + (-right + up) * half_size   # 左上
            ]
            
            # 纹理坐标
            texcoords = [
                (0, 0), (1, 0), (1, 1), (0, 1)
            ]
            
            # 计算透明度
            alpha = 1.0
            if self.use_alpha_fading:
                alpha = 1.0 - particle.age / particle.lifetime  # 透明度随生命周期变化
                
            # 写入顶点数据
            for j, (pos, texcoord) in enumerate(zip(positions, texcoords)):
                vertex_writer.addData3f(pos.x, pos.y, pos.z)
                normal_writer.addData3f(to_camera.x, to_camera.y, to_camera.z)
                if self.use_vertex_colors:
                    color_writer.addData4f(
                        particle.color.x, 
                        particle.color.y, 
                        particle.color.z, 
                        alpha
                    )
                texcoord_writer.addData2f(texcoord[0], texcoord[1])
                
        # 创建几何体
        geom = Geom(vertex_data)
        
        # 创建三角形索引
        triangles = GeomTriangles(Geom.UHStatic)
        
        for i in range(len(particles)):
            base_index = i * 4
            
            # 第一个三角形
            triangles.addVertex(base_index)
            triangles.addVertex(base_index + 1)
            triangles.addVertex(base_index + 2)
            
            # 第二个三角形
            triangles.addVertex(base_index)
            triangles.addVertex(base_index + 2)
            triangles.addVertex(base_index + 3)
            
        triangles.closePrimitive()
        geom.addPrimitive(triangles)
        
        # 添加到节点
        geom_node.addGeom(geom)
        
        # 更新统计信息
        self.render_stats['vertices_rendered'] += len(particles) * 4
        self.render_stats['triangles_rendered'] += len(particles) * 2
        
    def _create_point_geometry(self, geom_node: GeomNode, particles: List[Particle]):
        """创建点几何体"""
        if not particles:
            return
            
        # 清除现有几何体
        geom_node.removeAllGeoms()
        
        # 创建顶点格式
        if self.use_vertex_colors:
            vertex_format = GeomVertexFormat.getV3c4()
        else:
            vertex_format = GeomVertexFormat.getV3()
            
        vertex_data = GeomVertexData("particles", vertex_format, Geom.UHStatic)
        vertex_data.setNumRows(len(particles))
        
        # 创建顶点写入器
        vertex_writer = GeomVertexWriter(vertex_data, "vertex")
        if self.use_vertex_colors:
            color_writer = GeomVertexWriter(vertex_data, "color")
        
        # 写入粒子数据
        for particle in particles:
            if not particle.is_alive():
                continue
                
            vertex_writer.addData3f(
                particle.position.x, 
                particle.position.y, 
                particle.position.z
            )
            
            if self.use_vertex_colors:
                # 计算透明度
                alpha = 1.0
                if self.use_alpha_fading:
                    alpha = 1.0 - particle.age / particle.lifetime
                    
                color_writer.addData4f(
                    particle.color.x, 
                    particle.color.y, 
                    particle.color.z, 
                    alpha
                )
            
        # 创建几何体
        geom = Geom(vertex_data)
        
        # 创建点图元
        points = GeomPoints(Geom.UHStatic)
        for i in range(len(particles)):
            points.addVertex(i)
        points.closePrimitive()
        
        geom.addPrimitive(points)
        geom_node.addGeom(geom)
        
        # 更新统计信息
        self.render_stats['vertices_rendered'] += len(particles)
        
    def clear_all(self):
        """清除所有渲染内容"""
        for geom_node in self.particle_nodes.values():
            geom_node.removeAllGeoms()
            
    def get_render_stats(self) -> Dict:
        """获取渲染统计信息"""
        return self.render_stats.copy()
        
    def set_billboard_mode(self, enabled: bool):
        """设置广告牌模式"""
        self.use_billboards = enabled
        
    def set_point_sprite_mode(self, enabled: bool):
        """设置点精灵模式"""
        self.use_point_sprites = enabled
        
    def set_batch_rendering(self, enabled: bool):
        """设置批量渲染"""
        self.batch_rendering = enabled
        
    def set_max_particles_per_batch(self, count: int):
        """设置每批次最大粒子数"""
        self.max_particles_per_batch = max(1, count)
        
    def load_custom_texture(self, texture_name: str, texture_path: str):
        """加载自定义纹理"""
        try:
            texture = self.world.loader.loadTexture(texture_path)
            if texture:
                self.custom_textures[texture_name] = texture
                print(f"✓ 加载自定义纹理: {texture_name}")
                return True
        except Exception as e:
            print(f"✗ 纹理加载失败 {texture_name}: {e}")
            
        return False
        
    def apply_custom_texture(self, emitter_id: str, texture_name: str):
        """应用自定义纹理到发射器"""
        if texture_name not in self.custom_textures:
            print(f"⚠ 纹理未找到: {texture_name}")
            return False
            
        if emitter_id in self.particle_nodes:
            node_path = NodePath(self.particle_nodes[emitter_id])
            texture = self.custom_textures[texture_name]
            node_path.setTexture(texture, 1)
            print(f"✓ 应用纹理 {texture_name} 到发射器 {emitter_id}")
            return True
            
        return False