"""
高级骨骼动画系统 - 优化和压缩模块
提供动画压缩、LOD、缓存优化等功能
"""

import math
import json
import os
from typing import Dict, List, Optional, Tuple, Any, Callable
from dataclasses import dataclass, field
from enum import Enum

from panda3d.core import *
from direct.actor.Actor import Actor

# 定义常量
OPTIMIZATION_SETTINGS = {
    'compression_threshold': 0.001,
    'lod_distance_thresholds': [10.0, 25.0, 50.0],
    'lod_frame_rates': [1.0, 0.5, 0.25, 0.1],
    'cache_max_size': 500,
    'cache_cleanup_interval': 30.0
}

# 枚举定义
class CompressionMethod(Enum):
    """压缩方法枚举"""
    KEYFRAME_REDUCTION = 0
    QUANTIZATION = 1
    DELTA_COMPRESSION = 2
    RUN_LENGTH_ENCODING = 3

class LODStrategy(Enum):
    """LOD策略枚举"""
    DISTANCE_BASED = 0
    PERFORMANCE_BASED = 1
    HYBRID = 2

class CachePolicy(Enum):
    """缓存策略枚举"""
    LRU = 0  # 最近最少使用
    LFU = 1  # 最不经常使用
    FIFO = 2  # 先进先出

@dataclass
class CompressedAnimationData:
    """压缩动画数据类"""
    name: str
    frame_rate: float
    duration: float
    bone_data: Dict[str, List[Tuple[float, Tuple[float, float, float, float, float, float, float]]]]  # 时间, (位置+旋转+缩放)
    compression_ratio: float = 1.0
    original_size: int = 0
    compressed_size: int = 0

@dataclass
class LODSettings:
    """LOD设置数据类"""
    strategy: LODStrategy
    distance_thresholds: List[float]
    frame_rates: List[float]
    enable_auto_lod: bool = True
    performance_threshold: float = 30.0  # FPS阈值

@dataclass
class CacheEntry:
    """缓存条目数据类"""
    key: str
    data: Any
    timestamp: float
    access_count: int = 1
    size: int = 0

class AnimationCompressor:
    """
    动画压缩系统
    支持多种压缩算法
    """
    
    def __init__(self):
        """初始化动画压缩系统"""
        self.settings = {
            'position_threshold': 0.001,
            'rotation_threshold': 0.01,
            'scale_threshold': 0.001,
            'quantization_bits': 16,
            'enable_quantization': True
        }
        
        print("动画压缩系统初始化完成")
        
    def compress_animation(self, actor: Actor, anim_name: str, 
                          method: CompressionMethod = CompressionMethod.KEYFRAME_REDUCTION) -> Optional[CompressedAnimationData]:
        """
        压缩指定动画
        :param actor: Actor对象
        :param anim_name: 动画名称
        :param method: 压缩方法
        :return: 压缩后的动画数据
        """
        if anim_name not in actor.getAnimNames():
            print(f"错误: 动画 {anim_name} 不存在")
            return None
            
        print(f"开始压缩动画: {anim_name} (方法: {method.name})")
        
        # 获取动画控制
        anim_control = actor.getAnimControl(anim_name)
        if not anim_control:
            print(f"错误: 无法获取动画控制 {anim_name}")
            return None
            
        # 获取动画数据
        num_frames = anim_control.getNumFrames()
        frame_rate = anim_control.getFrameRate()
        duration = anim_control.getDuration()
        
        print(f"  动画信息: {num_frames} 帧, {frame_rate} FPS, {duration:.2f} 秒")
        
        # 提取骨骼动画数据
        bone_data = self._extract_bone_animation_data(actor, anim_name, num_frames)
        
        # 根据方法进行压缩
        if method == CompressionMethod.KEYFRAME_REDUCTION:
            compressed_bone_data = self._compress_keyframe_reduction(bone_data)
        elif method == CompressionMethod.QUANTIZATION:
            compressed_bone_data = self._compress_quantization(bone_data)
        elif method == CompressionMethod.DELTA_COMPRESSION:
            compressed_bone_data = self._compress_delta(bone_data)
        elif method == CompressionMethod.RUN_LENGTH_ENCODING:
            compressed_bone_data = self._compress_rle(bone_data)
        else:
            compressed_bone_data = bone_data
            
        # 计算压缩比
        original_size = self._calculate_data_size(bone_data)
        compressed_size = self._calculate_data_size(compressed_bone_data)
        compression_ratio = original_size / compressed_size if compressed_size > 0 else 1.0
        
        # 创建压缩数据
        compressed_data = CompressedAnimationData(
            name=anim_name,
            frame_rate=frame_rate,
            duration=duration,
            bone_data=compressed_bone_data,
            compression_ratio=compression_ratio,
            original_size=original_size,
            compressed_size=compressed_size
        )
        
        print(f"  压缩完成: 原始大小={original_size}, 压缩大小={compressed_size}, 压缩比={compression_ratio:.2f}:1")
        return compressed_data
        
    def _extract_bone_animation_data(self, actor: Actor, anim_name: str, num_frames: int) -> Dict[str, List]:
        """
        提取骨骼动画数据
        :param actor: Actor对象
        :param anim_name: 动画名称
        :param num_frames: 帧数
        :return: 骨骼动画数据
        """
        bone_data = {}
        
        # 获取所有骨骼
        bundle = actor.getPartBundleDict().get('modelRoot')
        if not bundle:
            return bone_data
            
        # 遍历骨骼
        for i in range(bundle.getNumChildren()):
            child = bundle.getChild(i)
            if hasattr(child, 'getName'):
                bone_name = child.getName()
                bone_data[bone_name] = self._extract_bone_frames(actor, bone_name, anim_name, num_frames)
                
        return bone_data
        
    def _extract_bone_frames(self, actor: Actor, bone_name: str, anim_name: str, num_frames: int) -> List:
        """
        提取单个骨骼的帧数据
        :param actor: Actor对象
        :param bone_name: 骨骼名称
        :param anim_name: 动画名称
        :param num_frames: 帧数
        :return: 帧数据列表
        """
        frames = []
        
        # 为每一帧提取数据
        for frame in range(num_frames):
            # 设置到指定帧
            actor.pose(anim_name, frame)
            
            # 获取骨骼变换
            joint = actor.exposeJoint(None, "modelRoot", bone_name)
            if joint:
                pos = joint.getPos()
                hpr = joint.getHpr()
                scale = joint.getScale()
                
                # 存储为 (时间, (px, py, pz, rx, ry, rz, sx, sy, sz))
                time = frame / actor.getFrameRate(anim_name)
                frame_data = (
                    time,
                    (pos.x, pos.y, pos.z, 
                     hpr.x, hpr.y, hpr.z, 
                     scale.x, scale.y, scale.z)
                )
                frames.append(frame_data)
                
        return frames
        
    def _compress_keyframe_reduction(self, bone_data: Dict[str, List]) -> Dict[str, List]:
        """
        关键帧简化压缩
        :param bone_data: 骨骼数据
        :return: 压缩后的骨骼数据
        """
        compressed_data = {}
        
        for bone_name, frames in bone_data.items():
            if len(frames) <= 2:
                compressed_data[bone_name] = frames
                continue
                
            compressed_frames = [frames[0]]  # 总是保留第一帧
            last_frame = frames[0]
            
            for i in range(1, len(frames) - 1):
                current_frame = frames[i]
                next_frame = frames[i + 1]
                
                # 检查当前帧是否可以被简化
                if not self._is_frame_necessary(last_frame, current_frame, next_frame):
                    continue
                    
                compressed_frames.append(current_frame)
                last_frame = current_frame
                
            compressed_frames.append(frames[-1])  # 总是保留最后一帧
            compressed_data[bone_name] = compressed_frames
            
        return compressed_data
        
    def _is_frame_necessary(self, prev_frame: Tuple, current_frame: Tuple, next_frame: Tuple) -> bool:
        """
        判断帧是否必要
        :param prev_frame: 前一帧
        :param current_frame: 当前帧
        :param next_frame: 后一帧
        :return: 是否必要
        """
        prev_time, prev_data = prev_frame
        curr_time, curr_data = current_frame
        next_time, next_data = next_frame
        
        # 计算线性插值
        t = (curr_time - prev_time) / (next_time - prev_time) if (next_time - prev_time) > 0 else 0
        interpolated = tuple(prev_data[i] + t * (next_data[i] - prev_data[i]) for i in range(len(prev_data)))
        
        # 计算与实际值的差异
        total_diff = sum(abs(curr_data[i] - interpolated[i]) for i in range(len(curr_data)))
        
        return total_diff > self.settings['position_threshold'] * 3  # 简化阈值检查
        
    def _compress_quantization(self, bone_data: Dict[str, List]) -> Dict[str, List]:
        """
        量化压缩
        :param bone_data: 骨骼数据
        :return: 量化后的骨骼数据
        """
        # 量化实现 - 简化版本
        return bone_data  # 实际项目中需要实现具体的量化算法
        
    def _compress_delta(self, bone_data: Dict[str, List]) -> Dict[str, List]:
        """
        差分压缩
        :param bone_data: 骨骼数据
        :return: 差分压缩后的骨骼数据
        """
        # 差分压缩实现 - 简化版本
        return bone_data  # 实际项目中需要实现具体的差分算法
        
    def _compress_rle(self, bone_data: Dict[str, List]) -> Dict[str, List]:
        """
        游程编码压缩
        :param bone_data: 骨骼数据
        :return: RLE压缩后的骨骼数据
        """
        # RLE压缩实现 - 简化版本
        return bone_data  # 实际项目中需要实现具体的RLE算法
        
    def _calculate_data_size(self, bone_data: Dict[str, List]) -> int:
        """
        计算数据大小
        :param bone_data: 骨骼数据
        :return: 数据大小（字节）
        """
        size = 0
        for frames in bone_data.values():
            size += len(frames) * 100  # 简化估算，每帧约100字节
        return size
        
    def decompress_animation(self, compressed_data: CompressedAnimationData) -> Dict[str, List]:
        """
        解压缩动画数据
        :param compressed_data: 压缩数据
        :return: 解压缩后的骨骼数据
        """
        # 解压缩实现 - 简化版本
        return compressed_data.bone_data
        
    def get_compression_stats(self, compressed_data: CompressedAnimationData) -> Dict[str, Any]:
        """
        获取压缩统计信息
        :param compressed_data: 压缩数据
        :return: 统计信息
        """
        return {
            'name': compressed_data.name,
            'compression_ratio': compressed_data.compression_ratio,
            'original_size': compressed_data.original_size,
            'compressed_size': compressed_data.compressed_size,
            'space_saved': compressed_data.original_size - compressed_data.compressed_size,
            'percentage_saved': (1 - compressed_data.compressed_size / compressed_data.original_size) * 100 if compressed_data.original_size > 0 else 0
        }

class AnimationLODSystem:
    """
    动画LOD系统
    根据距离和性能自动调整动画质量
    """
    
    def __init__(self, world):
        """
        初始化LOD系统
        :param world: 世界对象
        """
        self.world = world
        self.settings = LODSettings(
            strategy=LODStrategy.DISTANCE_BASED,
            distance_thresholds=[10.0, 25.0, 50.0],
            frame_rates=[1.0, 0.5, 0.25, 0.1],
            enable_auto_lod=True,
            performance_threshold=30.0
        )
        self.actor_lod_levels = {}  # {actor_id: lod_level}
        self.performance_monitor = {
            'last_fps': 60.0,
            'frame_times': [],
            'last_update': 0.0
        }
        self.camera_node = None
        
        print("动画LOD系统初始化完成")
        
    def set_lod_strategy(self, strategy: LODStrategy) -> None:
        """
        设置LOD策略
        :param strategy: LOD策略
        """
        self.settings.strategy = strategy
        print(f"LOD策略设置为: {strategy.name}")
        
    def set_distance_thresholds(self, thresholds: List[float]) -> None:
        """
        设置距离阈值
        :param thresholds: 距离阈值列表
        """
        self.settings.distance_thresholds = sorted(thresholds)
        print(f"距离阈值设置为: {thresholds}")
        
    def set_frame_rates(self, frame_rates: List[float]) -> None:
        """
        设置帧率级别
        :param frame_rates: 帧率列表
        """
        self.settings.frame_rates = frame_rates
        print(f"帧率级别设置为: {frame_rates}")
        
    def enable_auto_lod(self, enabled: bool = True) -> None:
        """
        启用/禁用自动LOD
        :param enabled: 是否启用
        """
        self.settings.enable_auto_lod = enabled
        status = "启用" if enabled else "禁用"
        print(f"自动LOD已{status}")
        
    def set_performance_threshold(self, threshold: float) -> None:
        """
        设置性能阈值
        :param threshold: FPS阈值
        """
        self.settings.performance_threshold = threshold
        print(f"性能阈值设置为: {threshold} FPS")
        
    def set_camera_node(self, camera_node) -> None:
        """
        设置摄像机节点
        :param camera_node: 摄像机节点
        """
        self.camera_node = camera_node
        
    def update_lod_levels(self, actors: Dict[str, Any]) -> None:
        """
        更新所有Actor的LOD级别
        :param actors: Actor字典 {actor_id: actor_info}
        """
        if not self.settings.enable_auto_lod:
            return
            
        current_time = globalClock.getRealTime()
        
        # 每隔一定时间更新一次
        if current_time - self.performance_monitor['last_update'] < 0.5:
            return
            
        self.performance_monitor['last_update'] = current_time
        
        # 获取摄像机位置
        camera_pos = Point3(0, 0, 0)
        if self.camera_node:
            camera_pos = self.camera_node.getPos()
        elif hasattr(self.world, 'camera') and self.world.camera:
            camera_pos = self.world.camera.getPos()
            
        # 根据策略更新LOD
        if self.settings.strategy == LODStrategy.DISTANCE_BASED:
            self._update_distance_based_lod(actors, camera_pos)
        elif self.settings.strategy == LODStrategy.PERFORMANCE_BASED:
            self._update_performance_based_lod(actors)
        elif self.settings.strategy == LODStrategy.HYBRID:
            self._update_hybrid_lod(actors, camera_pos)
            
    def _update_distance_based_lod(self, actors: Dict[str, Any], camera_pos: Point3) -> None:
        """
        更新基于距离的LOD
        :param actors: Actor字典
        :param camera_pos: 摄像机位置
        """
        for actor_id, actor_info in actors.items():
            actor = actor_info.actor
            
            # 计算距离
            distance = (actor.getPos() - camera_pos).length()
            
            # 确定LOD级别
            lod_level = 0
            for i, threshold in enumerate(self.settings.distance_thresholds):
                if distance > threshold:
                    lod_level = i + 1
                    
            # 应用LOD设置
            self._apply_lod_settings(actor_info, lod_level)
            self.actor_lod_levels[actor_id] = lod_level
            
    def _update_performance_based_lod(self, actors: Dict[str, Any]) -> None:
        """
        更新基于性能的LOD
        :param actors: Actor字典
        """
        # 根据FPS调整LOD
        fps = self.performance_monitor['last_fps']
        if fps < self.settings.performance_threshold:
            # 降低所有Actor的LOD级别
            for actor_id, actor_info in actors.items():
                current_lod = self.actor_lod_levels.get(actor_id, 0)
                new_lod = min(current_lod + 1, len(self.settings.frame_rates) - 1)
                self._apply_lod_settings(actor_info, new_lod)
                self.actor_lod_levels[actor_id] = new_lod
                
    def _update_hybrid_lod(self, actors: Dict[str, Any], camera_pos: Point3) -> None:
        """
        更新混合LOD
        :param actors: Actor字典
        :param camera_pos: 摄像机位置
        """
        # 结合距离和性能的LOD策略
        self._update_distance_based_lod(actors, camera_pos)
        self._update_performance_based_lod(actors)
        
    def _apply_lod_settings(self, actor_info: Any, lod_level: int) -> None:
        """
        应用LOD设置到Actor
        :param actor_info: Actor信息
        :param lod_level: LOD级别
        """
        actor = actor_info.actor
        frame_rate_multiplier = self.settings.frame_rates[lod_level] if lod_level < len(self.settings.frame_rates) else 0.1
        
        # 调整所有动画的播放速度
        for anim_name in actor.getAnimNames():
            current_rate = actor.getPlayRate(anim_name)
            new_rate = current_rate * frame_rate_multiplier
            actor.setPlayRate(new_rate, anim_name)
            
        # 更新Actor信息中的LOD级别
        actor_info.lod_level = lod_level
        
    def update_performance_stats(self, current_fps: float, frame_time: float) -> None:
        """
        更新性能统计
        :param current_fps: 当前FPS
        :param frame_time: 帧时间
        """
        self.performance_monitor['last_fps'] = current_fps
        self.performance_monitor['frame_times'].append(frame_time)
        
        # 保持最近30帧的统计数据
        if len(self.performance_monitor['frame_times']) > 30:
            self.performance_monitor['frame_times'] = self.performance_monitor['frame_times'][-30:]
            
    def get_lod_stats(self) -> Dict[str, Any]:
        """
        获取LOD统计信息
        :return: 统计信息
        """
        lod_counts = {}
        for lod_level in self.actor_lod_levels.values():
            if lod_level not in lod_counts:
                lod_counts[lod_level] = 0
            lod_counts[lod_level] += 1
            
        return {
            'actor_lod_distribution': lod_counts,
            'current_fps': self.performance_monitor['last_fps'],
            'average_frame_time': sum(self.performance_monitor['frame_times']) / len(self.performance_monitor['frame_times']) 
                                 if self.performance_monitor['frame_times'] else 0,
            'lod_settings': {
                'strategy': self.settings.strategy.name,
                'distance_thresholds': self.settings.distance_thresholds,
                'frame_rates': self.settings.frame_rates
            }
        }

class AnimationCacheManager:
    """
    动画缓存管理器
    提供高效的动画数据缓存
    """
    
    def __init__(self, max_size: int = 500):
        """
        初始化缓存管理器
        :param max_size: 最大缓存大小
        """
        self.max_size = max_size
        self.cache: Dict[str, CacheEntry] = {}
        self.policy = CachePolicy.LRU
        self.cleanup_interval = 30.0
        self.last_cleanup_time = 0.0
        
        print(f"动画缓存管理器初始化完成 (最大大小: {max_size})")
        
    def set_cache_policy(self, policy: CachePolicy) -> None:
        """
        设置缓存策略
        :param policy: 缓存策略
        """
        self.policy = policy
        print(f"缓存策略设置为: {policy.name}")
        
    def set_max_size(self, max_size: int) -> None:
        """
        设置最大缓存大小
        :param max_size: 最大大小
        """
        self.max_size = max_size
        self._cleanup_cache()
        print(f"最大缓存大小设置为: {max_size}")
        
    def put(self, key: str, data: Any, size: int = 0) -> None:
        """
        放入缓存项
        :param key: 键
        :param data: 数据
        :param size: 数据大小
        """
        current_time = globalClock.getRealTime()
        
        if key in self.cache:
            # 更新现有条目
            entry = self.cache[key]
            entry.data = data
            entry.timestamp = current_time
            entry.access_count += 1
            entry.size = size or entry.size
        else:
            # 添加新条目
            entry = CacheEntry(
                key=key,
                data=data,
                timestamp=current_time,
                access_count=1,
                size=size
            )
            self.cache[key] = entry
            
        # 检查是否需要清理缓存
        self._check_cleanup()
        
    def get(self, key: str) -> Optional[Any]:
        """
        获取缓存项
        :param key: 键
        :return: 数据或None
        """
        if key in self.cache:
            entry = self.cache[key]
            entry.access_count += 1
            entry.timestamp = globalClock.getRealTime()
            return entry.data
        return None
        
    def remove(self, key: str) -> bool:
        """
        移除缓存项
        :param key: 键
        :return: 是否成功移除
        """
        if key in self.cache:
            del self.cache[key]
            return True
        return False
        
    def clear(self) -> None:
        """清空缓存"""
        self.cache.clear()
        print("动画缓存已清空")
        
    def _check_cleanup(self) -> None:
        """
        检查是否需要清理缓存
        """
        current_time = globalClock.getRealTime()
        if current_time - self.last_cleanup_time > self.cleanup_interval:
            self._cleanup_cache()
            self.last_cleanup_time = current_time
            
    def _cleanup_cache(self) -> None:
        """
        清理缓存
        """
        if len(self.cache) <= self.max_size:
            return
            
        # 根据策略选择要移除的条目
        entries_to_remove = len(self.cache) - self.max_size
        
        if self.policy == CachePolicy.LRU:
            # 移除最近最少使用的条目
            sorted_entries = sorted(self.cache.values(), key=lambda x: x.timestamp)
        elif self.policy == CachePolicy.LFU:
            # 移除最不经常使用的条目
            sorted_entries = sorted(self.cache.values(), key=lambda x: x.access_count)
        else:  # FIFO
            # 移除最早添加的条目
            sorted_entries = sorted(self.cache.values(), key=lambda x: x.timestamp)
            
        # 移除条目
        for i in range(min(entries_to_remove, len(sorted_entries))):
            entry = sorted_entries[i]
            del self.cache[entry.key]
            
        print(f"缓存清理完成，移除了 {entries_to_remove} 个条目")
        
    def get_cache_stats(self) -> Dict[str, Any]:
        """
        获取缓存统计信息
        :return: 统计信息
        """
        total_size = sum(entry.size for entry in self.cache.values())
        total_access = sum(entry.access_count for entry in self.cache.values())
        avg_access = total_access / len(self.cache) if self.cache else 0
        
        return {
            'size': len(self.cache),
            'max_size': self.max_size,
            'total_size': total_size,
            'hit_rate': 0,  # 简化实现，实际项目中需要跟踪命中率
            'average_access_count': avg_access,
            'policy': self.policy.name
        }

# 使用示例和测试代码
def example_compression_usage(actor: Actor):
    """
    动画压缩使用示例
    """
    print("=== 动画压缩使用示例 ===")
    
    compressor = AnimationCompressor()
    
    # 压缩所有动画
    for anim_name in actor.getAnimNames():
        print(f"压缩动画: {anim_name}")
        compressed_data = compressor.compress_animation(
            actor, 
            anim_name, 
            CompressionMethod.KEYFRAME_REDUCTION
        )
        if compressed_data:
            stats = compressor.get_compression_stats(compressed_data)
            print(f"  压缩统计: {stats}")
            
    print("动画压缩示例完成")

def example_lod_usage(world, actors: Dict[str, Any]):
    """
    LOD系统使用示例
    """
    print("=== 动画LOD使用示例 ===")
    
    lod_system = AnimationLODSystem(world)
    
    # 设置LOD参数
    lod_system.set_lod_strategy(LODStrategy.HYBRID)
    lod_system.set_distance_thresholds([15.0, 30.0, 60.0])
    lod_system.set_frame_rates([1.0, 0.7, 0.4, 0.1])
    
    # 更新LOD级别
    lod_system.update_lod_levels(actors)
    
    # 更新性能统计
    lod_system.update_performance_stats(45, 0.022)  # 45 FPS, 22ms per frame
    
    # 获取LOD统计
    stats = lod_system.get_lod_stats()
    print(f"  LOD统计: {stats}")
    
    print("动画LOD示例完成")

def example_cache_usage():
    """
    缓存管理使用示例
    """
    print("=== 动画缓存管理使用示例 ===")
    
    cache_manager = AnimationCacheManager(max_size=100)
    
    # 设置缓存策略
    cache_manager.set_cache_policy(CachePolicy.LRU)
    
    # 添加缓存项
    dummy_data = {'frames': 100, 'bones': 20, 'data': 'animation_data'}
    cache_manager.put("actor_1_walk", dummy_data, size=1024)
    cache_manager.put("actor_1_run", dummy_data, size=2048)
    cache_manager.put("actor_2_idle", dummy_data, size=512)
    
    # 获取缓存项
    data = cache_manager.get("actor_1_walk")
    if data:
        print("  成功获取缓存数据")
        
    # 查看缓存统计
    stats = cache_manager.get_cache_stats()
    print(f"  缓存统计: {stats}")
    
    print("动画缓存管理示例完成")

if __name__ == "__main__":
    print("动画优化和压缩模块加载完成")