"""
导航网格序列化器
处理导航网格的导入导出和版本兼容性
"""

import json
import zlib
import base64
from typing import Dict, Any, List, Optional
from panda3d.core import Point3
import time

class NavMeshSerializer:
    """
    导航网格序列化器
    处理导航网格的导入导出和版本兼容性
    """
    
    def __init__(self):
        """初始化序列化器"""
        self.current_version = "2.0"
        self.supported_versions = ["1.0", "1.1", "2.0"]
        
    def serialize_navmesh(self, navmesh_manager) -> str:
        """
        序列化导航网格为字符串
        
        Args:
            navmesh_manager: 导航网格管理器
            
        Returns:
            序列化后的字符串
        """
        try:
            # 构建导航网格数据
            data = self._build_navmesh_data(navmesh_manager)
            
            # 转换为JSON字符串
            json_str = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
            
            return json_str
            
        except Exception as e:
            print(f"序列化导航网格失败: {e}")
            return ""
            
    def deserialize_navmesh(self, data_str: str, navmesh_manager) -> bool:
        """
        从字符串反序列化导航网格
        
        Args:
            data_str: 序列化数据字符串
            navmesh_manager: 导航网格管理器
            
        Returns:
            是否反序列化成功
        """
        try:
            # 解析JSON数据
            data = json.loads(data_str)
            
            # 处理版本兼容性
            data = self._handle_version_compatibility(data)
            
            # 应用数据到导航网格管理器
            self._apply_navmesh_data(data, navmesh_manager)
            
            return True
            
        except Exception as e:
            print(f"反序列化导航网格失败: {e}")
            return False
            
    def save_navmesh_to_file(self, navmesh_manager, filepath: str, 
                           compress: bool = False, pretty_print: bool = False) -> bool:
        """
        保存导航网格到文件
        
        Args:
            navmesh_manager: 导航网格管理器
            filepath: 文件路径
            compress: 是否压缩
            pretty_print: 是否美化输出
            
        Returns:
            是否保存成功
        """
        try:
            # 构建导航网格数据
            data = self._build_navmesh_data(navmesh_manager)
            
            # 添加保存时间戳
            data['metadata']['saved_at'] = time.strftime("%Y-%m-%d %H:%M:%S")
            
            # 转换为JSON字符串
            if pretty_print:
                json_str = json.dumps(data, ensure_ascii=False, indent=2)
            else:
                json_str = json.dumps(data, ensure_ascii=False, separators=(',', ':'))
                
            # 处理压缩
            if compress:
                # 压缩数据
                compressed_data = zlib.compress(json_str.encode('utf-8'))
                encoded_data = base64.b64encode(compressed_data).decode('ascii')
                
                # 保存压缩数据
                save_data = {
                    'version': self.current_version,
                    'compressed': True,
                    'data': encoded_data
                }
                with open(filepath, 'w', encoding='utf-8') as f:
                    json.dump(save_data, f, ensure_ascii=False, indent=2)
            else:
                # 直接保存JSON数据
                with open(filepath, 'w', encoding='utf-8') as f:
                    f.write(json_str)
                    
            return True
            
        except Exception as e:
            print(f"保存导航网格失败: {e}")
            return False
            
    def load_navmesh_from_file(self, filepath: str, navmesh_manager) -> bool:
        """
        从文件加载导航网格
        
        Args:
            filepath: 文件路径
            navmesh_manager: 导航网格管理器
            
        Returns:
            是否加载成功
        """
        try:
            # 读取文件内容
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read()
                
            # 尝试解析为JSON
            try:
                # 首先尝试解析为包装格式（可能包含压缩信息）
                wrapper_data = json.loads(content)
                if isinstance(wrapper_data, dict) and 'compressed' in wrapper_data:
                    if wrapper_data.get('compressed', False):
                        # 解压缩数据
                        encoded_data = wrapper_data['data']
                        compressed_data = base64.b64decode(encoded_data.encode('ascii'))
                        json_str = zlib.decompress(compressed_data).decode('utf-8')
                        data = json.loads(json_str)
                    else:
                        data = wrapper_data
                else:
                    # 直接使用内容
                    data = wrapper_data
            except json.JSONDecodeError:
                # 如果不是JSON格式，假设是直接的导航网格数据
                data = json.loads(content)
                
            # 处理版本兼容性
            data = self._handle_version_compatibility(data)
            
            # 应用数据到导航网格管理器
            self._apply_navmesh_data(data, navmesh_manager)
            
            return True
            
        except Exception as e:
            print(f"加载导航网格失败: {e}")
            return False
            
    def _build_navmesh_data(self, navmesh_manager) -> Dict[str, Any]:
        """
        构建导航网格数据
        
        Args:
            navmesh_manager: 导航网格管理器
            
        Returns:
            导航网格数据字典
        """
        # 构建元数据
        metadata = {
            'version': self.current_version,
            'generator': 'EG Navigation Mesh System',
            'created': time.strftime("%Y-%m-%d %H:%M:%S"),
            'polygon_count': len(navmesh_manager.polygons) if navmesh_manager.polygons else 0,
            'obstacle_count': len(navmesh_manager.obstacles) if navmesh_manager.obstacles else 0
        }
        
        # 添加设置信息
        settings = {
            'cell_size': getattr(navmesh_manager, 'cell_size', 0.5),
            'cell_height': getattr(navmesh_manager, 'cell_height', 0.2),
            'agent_height': getattr(navmesh_manager, 'agent_height', 1.8),
            'agent_radius': getattr(navmesh_manager, 'agent_radius', 0.3),
            'max_slope': getattr(navmesh_manager, 'max_slope', 45.0),
            'max_climb': getattr(navmesh_manager, 'max_climb', 0.2),
            'min_region_area': getattr(navmesh_manager, 'min_region_area', 3),
            'merge_region_area': getattr(navmesh_manager, 'merge_region_area', 20),
            'edge_max_len': getattr(navmesh_manager, 'edge_max_len', 12.0),
            'edge_max_error': getattr(navmesh_manager, 'edge_max_error', 1.3),
            'verts_per_poly': getattr(navmesh_manager, 'verts_per_poly', 6)
        }
        
        # 构建多边形数据
        polygons = []
        if navmesh_manager.polygons:
            for polygon_id, polygon in navmesh_manager.polygons.items():
                poly_data = {
                    'id': polygon_id,
                    'vertices': [[v.x, v.y, v.z] for v in polygon.vertices],
                    'neighbors': polygon.neighbors,
                    'portal_points': {},
                    'flags': getattr(polygon, 'flags', 0),
                    'area': getattr(polygon, 'area', 0.0)
                }
                
                # 添加门户点数据
                for neighbor_id, (left_point, right_point) in polygon.portal_points.items():
                    poly_data['portal_points'][str(neighbor_id)] = [
                        [left_point.x, left_point.y, left_point.z],
                        [right_point.x, right_point.y, right_point.z]
                    ]
                    
                polygons.append(poly_data)
                
        # 构建障碍物数据
        obstacles = []
        if navmesh_manager.obstacles:
            for obstacle_id, obstacle in navmesh_manager.obstacles.items():
                obs_data = {
                    'id': obstacle_id,
                    'position': [
                        obstacle['position'].x, 
                        obstacle['position'].y, 
                        obstacle['position'].z
                    ],
                    'radius': obstacle['radius'],
                    'height': obstacle['height'],
                    'type': obstacle.get('type', 'cylinder')
                }
                obstacles.append(obs_data)
                
        # 构建完整数据结构
        data = {
            'metadata': metadata,
            'settings': settings,
            'polygons': polygons,
            'obstacles': obstacles
        }
        
        return data
        
    def _handle_version_compatibility(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """
        处理版本兼容性
        
        Args:
            data: 导航网格数据
            
        Returns:
            处理后的数据
        """
        if 'metadata' not in data or 'version' not in data['metadata']:
            # 假设是旧版本数据
            data = self._upgrade_from_version_1_0(data)
            
        version = data['metadata']['version']
        
        # 根据版本进行升级
        if version == "1.0":
            data = self._upgrade_from_version_1_0(data)
        elif version == "1.1":
            data = self._upgrade_from_version_1_1(data)
        # 版本2.0不需要升级
            
        return data
        
    def _upgrade_from_version_1_0(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """
        从版本1.0升级
        
        Args:
            data: 版本1.0的数据
            
        Returns:
            升级后的数据
        """
        # 添加缺失的字段
        if 'metadata' not in data:
            data['metadata'] = {}
            
        data['metadata']['version'] = self.current_version
        
        # 确保所有必需字段存在
        if 'settings' not in data:
            data['settings'] = {}
            
        # 设置默认值
        default_settings = {
            'cell_size': 0.5,
            'cell_height': 0.2,
            'agent_height': 1.8,
            'agent_radius': 0.3,
            'max_slope': 45.0,
            'max_climb': 0.2,
            'min_region_area': 3,
            'merge_region_area': 20,
            'edge_max_len': 12.0,
            'edge_max_error': 1.3,
            'verts_per_poly': 6
        }
        
        for key, value in default_settings.items():
            if key not in data['settings']:
                data['settings'][key] = value
                
        return data
        
    def _upgrade_from_version_1_1(self, data: Dict[str, Any]) -> Dict[str, Any]:
        """
        从版本1.1升级
        
        Args:
            data: 版本1.1的数据
            
        Returns:
            升级后的数据
        """
        # 版本1.1到2.0的升级
        data['metadata']['version'] = self.current_version
        return data
        
    def _apply_navmesh_data(self, data: Dict[str, Any], navmesh_manager):
        """
        应用导航网格数据到管理器
        
        Args:
            data: 导航网格数据
            navmesh_manager: 导航网格管理器
        """
        from ..core.navmesh_manager import NavMeshPolygon
        
        # 清除现有数据
        navmesh_manager.polygons.clear()
        navmesh_manager.obstacles.clear()
        
        # 应用设置
        if 'settings' in data:
            settings = data['settings']
            for key, value in settings.items():
                if hasattr(navmesh_manager, key):
                    setattr(navmesh_manager, key, value)
                    
        # 解析多边形
        if 'polygons' in data:
            for poly_data in data['polygons']:
                polygon_id = poly_data['id']
                vertices = [Point3(v[0], v[1], v[2]) for v in poly_data['vertices']]
                
                polygon = NavMeshPolygon(polygon_id, vertices)
                
                # 添加邻居信息
                if 'neighbors' in poly_data:
                    for neighbor_id in poly_data['neighbors']:
                        polygon.add_neighbor(neighbor_id)
                        
                # 添加门户点信息
                if 'portal_points' in poly_data:
                    for neighbor_id, portal_data in poly_data['portal_points'].items():
                        left_point = Point3(portal_data[0][0], portal_data[0][1], portal_data[0][2])
                        right_point = Point3(portal_data[1][0], portal_data[1][1], portal_data[1][2])
                        polygon.set_portal_points(int(neighbor_id), left_point, right_point)
                        
                # 设置标志位
                if 'flags' in poly_data:
                    polygon.flags = poly_data['flags']
                    
                # 设置面积
                if 'area' in poly_data:
                    polygon.area = poly_data['area']
                    
                navmesh_manager.polygons[polygon_id] = polygon
                
        # 解析障碍物
        if 'obstacles' in data:
            for obs_data in data['obstacles']:
                obstacle_id = obs_data['id']
                navmesh_manager.obstacles[obstacle_id] = {
                    'position': Point3(obs_data['position'][0], obs_data['position'][1], obs_data['position'][2]),
                    'radius': obs_data['radius'],
                    'height': obs_data['height'],
                    'type': obs_data.get('type', 'cylinder')
                }
                
        # 更新统计信息
        navmesh_manager._update_stats()
        
        # 更新空间哈希
        navmesh_manager._update_spatial_hash()
        
    def export_to_standard_format(self, navmesh_manager, filepath: str, 
                                format_type: str = 'obj') -> bool:
        """
        导出为标准格式
        
        Args:
            navmesh_manager: 导航网格管理器
            filepath: 导出文件路径
            format_type: 导出格式 ('obj', 'ply')
            
        Returns:
            是否导出成功
        """
        try:
            if format_type.lower() == 'obj':
                return self._export_to_obj(navmesh_manager, filepath)
            elif format_type.lower() == 'ply':
                return self._export_to_ply(navmesh_manager, filepath)
            else:
                print(f"不支持的导出格式: {format_type}")
                return False
                
        except Exception as e:
            print(f"导出为标准格式失败: {e}")
            return False
            
    def _export_to_obj(self, navmesh_manager, filepath: str) -> bool:
        """
        导出为OBJ格式
        
        Args:
            navmesh_manager: 导航网格管理器
            filepath: 导出文件路径
            
        Returns:
            是否导出成功
        """
        try:
            with open(filepath, 'w', encoding='utf-8') as f:
                f.write("# Navigation Mesh Export\n")
                f.write(f"# Generated by EG Navigation Mesh System\n")
                f.write(f"# Polygons: {len(navmesh_manager.polygons)}\n\n")
                
                # 写入顶点
                vertex_index = 1
                vertex_mapping = {}  # 旧顶点索引到新顶点索引的映射
                
                for polygon_id, polygon in navmesh_manager.polygons.items():
                    for vertex in polygon.vertices:
                        vertex_key = (vertex.x, vertex.y, vertex.z)
                        if vertex_key not in vertex_mapping:
                            f.write(f"v {vertex.x} {vertex.y} {vertex.z}\n")
                            vertex_mapping[vertex_key] = vertex_index
                            vertex_index += 1
                            
                f.write("\n")
                
                # 写入面
                for polygon_id, polygon in navmesh_manager.polygons.items():
                    f.write(f"g polygon_{polygon_id}\n")
                    face_vertices = []
                    for vertex in polygon.vertices:
                        vertex_key = (vertex.x, vertex.y, vertex.z)
                        face_vertices.append(str(vertex_mapping[vertex_key]))
                    f.write(f"f {' '.join(face_vertices)}\n\n")
                    
            return True
            
        except Exception as e:
            print(f"导出为OBJ格式失败: {e}")
            return False
            
    def _export_to_ply(self, navmesh_manager, filepath: str) -> bool:
        """
        导出为PLY格式
        
        Args:
            navmesh_manager: 导航网格管理器
            filepath: 导出文件路径
            
        Returns:
            是否导出成功
        """
        try:
            # 收集所有唯一顶点
            vertices = []
            vertex_mapping = {}
            faces = []
            
            for polygon_id, polygon in navmesh_manager.polygons.items():
                face_indices = []
                for vertex in polygon.vertices:
                    vertex_key = (vertex.x, vertex.y, vertex.z)
                    if vertex_key not in vertex_mapping:
                        vertex_mapping[vertex_key] = len(vertices)
                        vertices.append(vertex)
                    face_indices.append(vertex_mapping[vertex_key])
                faces.append(face_indices)
                
            # 写入PLY文件
            with open(filepath, 'w', encoding='utf-8') as f:
                # PLY头部
                f.write("ply\n")
                f.write("format ascii 1.0\n")
                f.write(f"element vertex {len(vertices)}\n")
                f.write("property float x\n")
                f.write("property float y\n")
                f.write("property float z\n")
                f.write(f"element face {len(faces)}\n")
                f.write("property list uchar int vertex_indices\n")
                f.write("end_header\n")
                
                # 写入顶点
                for vertex in vertices:
                    f.write(f"{vertex.x} {vertex.y} {vertex.z}\n")
                    
                # 写入面
                for face in faces:
                    f.write(f"{len(face)} {' '.join(map(str, face))}\n")
                    
            return True
            
        except Exception as e:
            print(f"导出为PLY格式失败: {e}")
            return False
            
    def import_from_standard_format(self, filepath: str, navmesh_manager, 
                                  format_type: str = 'obj') -> bool:
        """
        从标准格式导入
        
        Args:
            filepath: 导入文件路径
            navmesh_manager: 导航网格管理器
            format_type: 导入格式 ('obj', 'ply')
            
        Returns:
            是否导入成功
        """
        try:
            if format_type.lower() == 'obj':
                return self._import_from_obj(filepath, navmesh_manager)
            elif format_type.lower() == 'ply':
                return self._import_from_ply(filepath, navmesh_manager)
            else:
                print(f"不支持的导入格式: {format_type}")
                return False
                
        except Exception as e:
            print(f"从标准格式导入失败: {e}")
            return False
            
    def _import_from_obj(self, filepath: str, navmesh_manager) -> bool:
        """
        从OBJ格式导入
        
        Args:
            filepath: 导入文件路径
            navmesh_manager: 导航网格管理器
            
        Returns:
            是否导入成功
        """
        try:
            vertices = []
            faces = []
            
            with open(filepath, 'r', encoding='utf-8') as f:
                for line in f:
                    line = line.strip()
                    if not line or line.startswith('#'):
                        continue
                        
                    parts = line.split()
                    if not parts:
                        continue
                        
                    if parts[0] == 'v':
                        # 顶点
                        if len(parts) >= 4:
                            x, y, z = float(parts[1]), float(parts[2]), float(parts[3])
                            vertices.append(Point3(x, y, z))
                    elif parts[0] == 'f':
                        # 面
                        face_indices = []
                        for part in parts[1:]:
                            # 处理索引（OBJ格式索引从1开始）
                            index = int(part.split('/')[0]) - 1
                            face_indices.append(index)
                        faces.append(face_indices)
                        
            # 创建多边形
            navmesh_manager.polygons.clear()
            for i, face_indices in enumerate(faces):
                polygon_vertices = [vertices[idx] for idx in face_indices if 0 <= idx < len(vertices)]
                if len(polygon_vertices) >= 3:
                    navmesh_manager.add_polygon(polygon_vertices)
                    
            return True
            
        except Exception as e:
            print(f"从OBJ格式导入失败: {e}")
            return False
            
    def _import_from_ply(self, filepath: str, navmesh_manager) -> bool:
        """
        从PLY格式导入
        
        Args:
            filepath: 导入文件路径
            navmesh_manager: 导航网格管理器
            
        Returns:
            是否导入成功
        """
        try:
            vertices = []
            faces = []
            
            with open(filepath, 'r', encoding='utf-8') as f:
                lines = f.readlines()
                
            # 解析PLY头部
            i = 0
            vertex_count = 0
            face_count = 0
            
            while i < len(lines):
                line = lines[i].strip()
                if not line:
                    i += 1
                    continue
                    
                if line == "end_header":
                    i += 1
                    break
                    
                parts = line.split()
                if len(parts) >= 3 and parts[0] == "element":
                    if parts[1] == "vertex":
                        vertex_count = int(parts[2])
                    elif parts[1] == "face":
                        face_count = int(parts[2])
                        
                i += 1
                
            # 读取顶点
            for j in range(vertex_count):
                if i + j < len(lines):
                    parts = lines[i + j].strip().split()
                    if len(parts) >= 3:
                        x, y, z = float(parts[0]), float(parts[1]), float(parts[2])
                        vertices.append(Point3(x, y, z))
                        
            i += vertex_count
            
            # 读取面
            for j in range(face_count):
                if i + j < len(lines):
                    parts = lines[i + j].strip().split()
                    if len(parts) >= 2:
                        count = int(parts[0])
                        face_indices = [int(idx) for idx in parts[1:1+count]]
                        faces.append(face_indices)
                        
            # 创建多边形
            navmesh_manager.polygons.clear()
            for face_indices in faces:
                polygon_vertices = [vertices[idx] for idx in face_indices if 0 <= idx < len(vertices)]
                if len(polygon_vertices) >= 3:
                    navmesh_manager.add_polygon(polygon_vertices)
                    
            return True
            
        except Exception as e:
            print(f"从PLY格式导入失败: {e}")
            return False
            
    def get_file_info(self, filepath: str) -> Dict[str, Any]:
        """
        获取导航网格文件信息
        
        Args:
            filepath: 文件路径
            
        Returns:
            文件信息字典
        """
        try:
            info = {
                'filepath': filepath,
                'file_size': 0,
                'version': 'unknown',
                'polygon_count': 0,
                'obstacle_count': 0,
                'compressed': False
            }
            
            import os
            info['file_size'] = os.path.getsize(filepath)
            
            # 读取文件内容
            with open(filepath, 'r', encoding='utf-8') as f:
                content = f.read(1024)  # 只读取前1024个字符
                
            # 尝试解析
            try:
                data = json.loads(content)
                if isinstance(data, dict):
                    if 'compressed' in data:
                        info['compressed'] = data['compressed']
                        info['version'] = data.get('version', 'unknown')
                    elif 'metadata' in data:
                        info['version'] = data['metadata'].get('version', 'unknown')
                        info['polygon_count'] = data['metadata'].get('polygon_count', 0)
                        info['obstacle_count'] = data['metadata'].get('obstacle_count', 0)
            except json.JSONDecodeError:
                pass
                
            return info
            
        except Exception as e:
            print(f"获取文件信息失败: {e}")
            return {}