"""
流体动力学插件性能优化模块
提供性能优化、内存管理和调试功能
"""

import time
import psutil
import threading
import numpy as np
from collections import deque, defaultdict
import gc

class FluidPerformanceOptimizer:
    """
    流体性能优化器
    提供性能监控、优化和调试功能，包括内存管理、多线程处理和自适应优化
    """
    
    def __init__(self, fluid_world):
        """
        初始化性能优化器
        
        Args:
            fluid_world: 流体世界对象的引用
        """
        self.fluid_world = fluid_world
        self.optimization_enabled = True
        
        # 性能监控
        self.frame_times = deque(maxlen=100)
        self.particle_counts = deque(maxlen=100)
        self.update_times = deque(maxlen=100)
        self.memory_usage = deque(maxlen=100)
        
        # 优化参数
        self.max_particles = 5000
        self.target_fps = 60
        self.adaptive_resolution = True
        self.quality_level = 1.0  # 1.0为最高质量，0.1为最低质量
        
        # 多线程支持
        self.use_multithreading = False
        self.worker_threads = []
        self.thread_lock = threading.Lock()
        self.thread_count = 2
        
        # 内存管理
        self.object_pools = defaultdict(list)  # 对象池
        self.max_pool_size = 1000  # 每个池的最大大小
        self.memory_limit_mb = 500  # 内存限制（MB）
        
        # 自适应优化
        self.adaptive_optimization = True
        self.optimization_history = deque(maxlen=50)  # 优化历史
        self.performance_trend = 0  # 性能趋势：正数表示改善，负数表示恶化
        
        # 缓存系统
        self.cache_enabled = True
        self.spatial_cache = {}  # 空间缓存
        self.cache_ttl = 5.0  # 缓存生存时间（秒）
        self.last_cache_cleanup = time.time()
        
        # 调试和分析
        self.debug_mode = False
        self.profiling_enabled = False
        self.profiling_data = defaultdict(list)
        
        print("流体性能优化器初始化完成")
        
    def update_performance_stats(self, update_time=0):
        """
        更新性能统计信息
        
        Args:
            update_time (float): 更新耗时
        """
        # 记录帧时间
        current_time = time.time()
        if hasattr(self, 'last_update_time'):
            frame_time = current_time - self.last_update_time
            self.frame_times.append(frame_time)
            
        self.last_update_time = current_time
        
        # 记录粒子数量
        particle_count = len(self.fluid_world.fluid_particles)
        self.particle_counts.append(particle_count)
        
        # 记录更新时间
        self.update_times.append(update_time)
        
        # 记录内存使用情况
        memory_mb = psutil.Process().memory_info().rss / 1024 / 1024
        self.memory_usage.append(memory_mb)
        
        # 更新优化历史
        self._update_optimization_history()
        
    def optimize_for_performance(self):
        """
        根据当前性能自适应优化
        """
        if not self.optimization_enabled:
            return
            
        # 获取当前性能指标
        avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
        current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
        current_particles = len(self.fluid_world.fluid_particles)
        current_memory = self.memory_usage[-1] if self.memory_usage else 0
        
        # 检查内存使用情况
        if current_memory > self.memory_limit_mb:
            self._trigger_memory_cleanup()
            
        # 检查缓存是否需要清理
        if time.time() - self.last_cache_cleanup > self.cache_ttl:
            self._cleanup_cache()
            
        # 根据FPS调整质量级别
        if current_fps < self.target_fps * 0.7:  # 性能严重不足
            self.quality_level = max(0.1, self.quality_level * 0.9)
            self._reduce_simulation_quality()
        elif current_fps > self.target_fps * 1.1:  # 性能良好
            self.quality_level = min(1.0, self.quality_level * 1.05)
            self._increase_simulation_quality()
            
        # 根据粒子数量调整
        if current_particles > self.max_particles * 0.9:
            self._reduce_particle_count()
        elif current_particles < self.max_particles * 0.5:
            self._increase_particle_count()
            
        # 根据系统资源调整
        cpu_percent = psutil.cpu_percent()
        memory_percent = psutil.virtual_memory().percent
        
        if cpu_percent > 85:
            self._reduce_computation_load()
        if memory_percent > 85:
            self._trigger_memory_cleanup()
            
    def _reduce_simulation_quality(self):
        """
        降低模拟质量以提高性能
        """
        # 降低求解器精度
        if hasattr(self.fluid_world, 'solver'):
            solver = self.fluid_world.solver
            solver.smoothing_radius *= 1.1  # 增大光滑半径减少计算量
            solver.time_step = min(solver.time_step * 1.1, 0.05)  # 增大时间步长
            
        # 降低可视化质量
        if hasattr(self.fluid_world, 'visualizer'):
            visualizer = self.fluid_world.visualizer
            visualizer.max_visible_particles = int(visualizer.max_visible_particles * 0.9)
            
    def _increase_simulation_quality(self):
        """
        提高模拟质量
        """
        # 提高求解器精度
        if hasattr(self.fluid_world, 'solver'):
            solver = self.fluid_world.solver
            solver.smoothing_radius = max(solver.smoothing_radius * 0.95, 0.5)
            solver.time_step = max(solver.time_step * 0.95, 0.001)
            
        # 提高可视化质量
        if hasattr(self.fluid_world, 'visualizer'):
            visualizer = self.fluid_world.visualizer
            visualizer.max_visible_particles = min(int(visualizer.max_visible_particles * 1.1), 20000)
            
    def _reduce_particle_count(self):
        """
        减少粒子数量
        """
        current_particles = len(self.fluid_world.fluid_particles)
        target_particles = int(current_particles * 0.9)
        
        # 移除多余的粒子
        while len(self.fluid_world.fluid_particles) > target_particles:
            if self.fluid_world.fluid_particles:
                # 回收粒子对象到对象池
                particle = self.fluid_world.fluid_particles.pop()
                self._recycle_object('particle', particle)
                
    def _increase_particle_count(self):
        """
        增加粒子数量
        """
        current_particles = len(self.fluid_world.fluid_particles)
        target_particles = int(current_particles * 1.1)
        
        # 从对象池获取粒子或创建新粒子
        for _ in range(target_particles - current_particles):
            particle = self._get_object('particle')
            if particle is None:
                # 创建新粒子
                particle = {
                    'position': Point3(0, 0, 0),
                    'velocity': Vec3(0, 0, 0),
                    'mass': 1.0,
                    'density': 1000.0,
                    'pressure': 0.0
                }
            self.fluid_world.fluid_particles.append(particle)
            
    def _reduce_computation_load(self):
        """
        减少计算负载
        """
        # 降低更新频率
        if hasattr(self.fluid_world, 'visualizer'):
            visualizer = self.fluid_world.visualizer
            visualizer.update_frequency = max(visualizer.update_frequency * 0.9, 15)
            
        # 简化物理计算
        if hasattr(self.fluid_world, 'solver'):
            solver = self.fluid_world.solver
            solver.stiffness *= 0.95  # 降低刚度
            solver.viscosity *= 0.95   # 降低粘度计算复杂度
            
    def adaptive_substepping(self, dt):
        """
        自适应子步长
        
        Args:
            dt (float): 帧时间增量
            
        Returns:
            int: 子步数
        """
        # 根据当前性能调整子步数
        avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
        current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
        
        # 如果性能良好，使用更多子步以提高精度
        if current_fps > self.target_fps * 0.9:
            return min(10, int(self.fluid_world.time_step / dt) + 1)
        else:
            # 如果性能不佳，减少子步以保持稳定
            return max(1, int(self.fluid_world.time_step / dt * 0.5))
            
    def get_performance_report(self):
        """
        获取详细的性能报告
        
        Returns:
            dict: 性能报告
        """
        avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
        current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
        avg_update_time = sum(self.update_times) / len(self.update_times) if self.update_times else 0
        
        # 获取系统资源使用情况
        cpu_percent = psutil.cpu_percent()
        memory_percent = psutil.virtual_memory().percent
        process_memory = psutil.Process().memory_info().rss / 1024 / 1024  # MB
        
        particle_count = len(self.fluid_world.fluid_particles)
        
        return {
            'performance': {
                'current_fps': current_fps,
                'avg_frame_time': avg_frame_time,
                'avg_update_time': avg_update_time,
                'quality_level': self.quality_level
            },
            'resources': {
                'cpu_percent': cpu_percent,
                'memory_percent': memory_percent,
                'process_memory_mb': process_memory
            },
            'fluid_objects_count': {
                'particles': particle_count,
                'domains': len(self.fluid_world.fluid_domains),
                'emitters': len(self.fluid_world.fluid_emitters),
                'obstacles': len(self.fluid_world.fluid_obstacles),
            },
            'optimization': {
                'enabled': self.optimization_enabled,
                'adaptive': self.adaptive_optimization,
                'multithreading': self.use_multithreading,
                'thread_count': len(self.worker_threads)
            },
            'memory': {
                'usage_mb': process_memory,
                'limit_mb': self.memory_limit_mb,
                'pool_sizes': {k: len(v) for k, v in self.object_pools.items()}
            },
            'recommended_actions': self._get_recommendations()
        }
        
    def _get_recommendations(self):
        """
        获取性能优化建议
        
        Returns:
            list: 优化建议列表
        """
        recommendations = []
        
        avg_frame_time = sum(self.frame_times) / len(self.frame_times) if self.frame_times else 0
        current_fps = 1.0 / avg_frame_time if avg_frame_time > 0 else 0
        particle_count = len(self.fluid_world.fluid_particles)
        process_memory = psutil.Process().memory_info().rss / 1024 / 1024  # MB
        
        if current_fps < self.target_fps * 0.7:
            recommendations.append(f"性能严重不足，当前FPS: {current_fps:.1f}")
            
        if particle_count > self.max_particles * 0.9:
            recommendations.append(f"粒子数量过多 ({particle_count})，考虑减少粒子数量")
            
        if process_memory > self.memory_limit_mb * 0.9:
            recommendations.append(f"内存使用接近限制 ({process_memory:.1f}MB / {self.memory_limit_mb}MB)")
            
        if psutil.cpu_percent() > 85:
            recommendations.append("CPU使用率过高，考虑启用多线程")
            
        if psutil.virtual_memory().percent > 85:
            recommendations.append("系统内存使用率过高")
            
        return recommendations
        
    def enable_multithreading(self, enabled=True, thread_count=2):
        """
        启用多线程处理
        
        Args:
            enabled (bool): 是否启用多线程
            thread_count (int): 线程数量
        """
        self.use_multithreading = enabled
        self.thread_count = thread_count
        
        if enabled:
            # 停止现有线程
            self._stop_worker_threads()
            
            # 创建工作线程
            for i in range(thread_count):
                thread = threading.Thread(target=self._worker_thread, args=(i,))
                thread.daemon = True
                thread.start()
                self.worker_threads.append(thread)
                
            print(f"已启用多线程处理，线程数: {thread_count}")
        else:
            # 停止所有工作线程
            self._stop_worker_threads()
            print("已禁用多线程处理")
            
    def _stop_worker_threads(self):
        """
        停止所有工作线程
        """
        self.use_multithreading = False
        for thread in self.worker_threads:
            thread.join(timeout=1.0)
        self.worker_threads.clear()
        
    def _worker_thread(self, thread_id):
        """
        工作线程函数
        
        Args:
            thread_id (int): 线程ID
        """
        while self.use_multithreading:
            try:
                # 处理流体粒子更新
                with self.thread_lock:
                    # 将粒子分配给不同线程处理
                    particles = self.fluid_world.fluid_particles
                    if particles:
                        particles_per_thread = max(1, len(particles) // len(self.worker_threads))
                        start_idx = thread_id * particles_per_thread
                        end_idx = min(start_idx + particles_per_thread, len(particles))
                        
                        # 更新分配给此线程的粒子
                        for i in range(start_idx, end_idx):
                            if i < len(particles):
                                particle = particles[i]
                                # 在这里执行粒子更新
                                # 例如：应用重力、更新位置等
                                pass
                                
                # 短暂休眠以避免过度占用CPU
                time.sleep(0.001)
            except Exception as e:
                if self.debug_mode:
                    print(f"工作线程 {thread_id} 出错: {e}")
                time.sleep(0.01)  # 出错后短暂休眠
                
    def _update_optimization_history(self):
        """
        更新优化历史记录
        """
        if self.frame_times:
            current_fps = 1.0 / self.frame_times[-1] if self.frame_times[-1] > 0 else 0
            self.optimization_history.append({
                'timestamp': time.time(),
                'fps': current_fps,
                'particle_count': len(self.fluid_world.fluid_particles),
                'memory_mb': self.memory_usage[-1] if self.memory_usage else 0
            })
            
            # 计算性能趋势
            if len(self.optimization_history) > 5:
                recent_fps = [record['fps'] for record in list(self.optimization_history)[-5:]]
                self.performance_trend = np.mean(np.diff(recent_fps)) if len(recent_fps) > 1 else 0
                
    def enable_profiling(self, enabled=True):
        """
        启用性能分析
        
        Args:
            enabled (bool): 是否启用性能分析
        """
        self.profiling_enabled = enabled
        if enabled:
            print("性能分析已启用")
        else:
            print("性能分析已禁用")
            
    def profile_function(self, func_name, duration):
        """
        记录函数执行时间
        
        Args:
            func_name (str): 函数名称
            duration (float): 执行时间（秒）
        """
        if self.profiling_enabled:
            self.profiling_data[func_name].append(duration)
            
    def get_profiling_report(self):
        """
        获取性能分析报告
        
        Returns:
            dict: 性能分析报告
        """
        if not self.profiling_enabled:
            return {}
            
        report = {}
        for func_name, durations in self.profiling_data.items():
            if durations:
                report[func_name] = {
                    'count': len(durations),
                    'total_time': sum(durations),
                    'avg_time': np.mean(durations),
                    'min_time': min(durations),
                    'max_time': max(durations)
                }
                
        return report
        
    def _trigger_memory_cleanup(self):
        """
        触发内存清理
        """
        # 清理对象池中过多的对象
        for obj_type, pool in self.object_pools.items():
            if len(pool) > self.max_pool_size:
                # 保留一部分对象，删除多余的
                del pool[self.max_pool_size:]
                
        # 强制进行垃圾回收
        gc.collect()
        
        if self.debug_mode:
            print("内存清理完成")
            
    def _cleanup_cache(self):
        """
        清理过期缓存
        """
        current_time = time.time()
        expired_keys = []
        
        for key, (value, timestamp) in self.spatial_cache.items():
            if current_time - timestamp > self.cache_ttl:
                expired_keys.append(key)
                
        for key in expired_keys:
            del self.spatial_cache[key]
            
        self.last_cache_cleanup = current_time
        
    def cache_spatial_query(self, query_key, result):
        """
        缓存空间查询结果
        
        Args:
            query_key: 查询键
            result: 查询结果
        """
        if self.cache_enabled:
            self.spatial_cache[query_key] = (result, time.time())
            
    def get_cached_spatial_query(self, query_key):
        """
        获取缓存的空间查询结果
        
        Args:
            query_key: 查询键
            
        Returns:
            查询结果或None
        """
        if not self.cache_enabled:
            return None
            
        if query_key in self.spatial_cache:
            result, timestamp = self.spatial_cache[query_key]
            if time.time() - timestamp <= self.cache_ttl:
                return result
            else:
                # 缓存过期，删除
                del self.spatial_cache[query_key]
                
        return None
        
    def _get_object(self, obj_type):
        """
        从对象池获取对象
        
        Args:
            obj_type (str): 对象类型
            
        Returns:
            对象或None
        """
        if obj_type in self.object_pools and self.object_pools[obj_type]:
            return self.object_pools[obj_type].pop()
        return None
        
    def _recycle_object(self, obj_type, obj):
        """
        回收对象到对象池
        
        Args:
            obj_type (str): 对象类型
            obj: 要回收的对象
        """
        if len(self.object_pools[obj_type]) < self.max_pool_size:
            # 重置对象状态
            if obj_type == 'particle':
                obj['position'] = Point3(0, 0, 0)
                obj['velocity'] = Vec3(0, 0, 0)
                obj['density'] = 1000.0
                obj['pressure'] = 0.0
                
            self.object_pools[obj_type].append(obj)
            
    def set_memory_limit(self, limit_mb):
        """
        设置内存限制
        
        Args:
            limit_mb (float): 内存限制（MB）
        """
        self.memory_limit_mb = limit_mb
        
    def set_quality_level(self, level):
        """
        设置质量级别
        
        Args:
            level (float): 质量级别 (0.1-1.0)
        """
        self.quality_level = max(0.1, min(1.0, level))
        
    def cleanup(self):
        """
        清理性能优化器资源
        """
        # 停止多线程
        self._stop_worker_threads()
        
        # 清理对象池
        self.object_pools.clear()
        
        # 清理缓存
        self.spatial_cache.clear()
        
        # 清理性能数据
        self.frame_times.clear()
        self.particle_counts.clear()
        self.update_times.clear()
        self.memory_usage.clear()
        self.optimization_history.clear()
        self.profiling_data.clear()
        
        print("流体性能优化器资源已清理")