#!/usr/bin/env python3
# -*- coding: utf-8 -*-

"""
Scene地图到SMAP地图转换器
根据scene地图中的points信息，更新smap地图中对应的坐标位置
"""

import json
import os
from typing import Dict, List, Optional, Tuple


class SceneToSmapConverter:
    """Scene到SMAP转换器"""
    
    def __init__(self):
        self.scene_data = None
        self.smap_data = None
        self.point_mapping = {}  # scene point name -> scene point data
        self.route_mapping = {}  # scene route (from,to) -> scene route data
        self.conversion_log = []  # 转换日志
        
    def load_scene_file(self, scene_file_path: str) -> bool:
        """加载Scene文件"""
        try:
            with open(scene_file_path, 'r', encoding='utf-8') as f:
                self.scene_data = json.load(f)
            
            # 建立点位映射
            if 'points' in self.scene_data:
                for point in self.scene_data['points']:
                    if 'name' in point:
                        self.point_mapping[point['name']] = point
            
            # 建立路径映射：基于起点和终点名称
            if 'routes' in self.scene_data:
                for route in self.scene_data['routes']:
                    if 'from' in route and 'to' in route:
                        # 先根据ID找到对应的点名称
                        from_name = None
                        to_name = None
                        for point in self.scene_data.get('points', []):
                            if point.get('id') == route['from']:
                                from_name = point.get('name')
                            if point.get('id') == route['to']:
                                to_name = point.get('name')
                        
                        if from_name and to_name:
                            route_key = f"{from_name}-{to_name}"
                            self.route_mapping[route_key] = route
                        
            self.conversion_log.append(f"✓ 成功加载Scene文件，共 {len(self.point_mapping)} 个点位，{len(self.route_mapping)} 条路径")
            return True
            
        except Exception as e:
            self.conversion_log.append(f"✗ 加载Scene文件失败: {str(e)}")
            return False
    
    def load_smap_file(self, smap_file_path: str) -> bool:
        """加载SMAP文件"""
        try:
            with open(smap_file_path, 'r', encoding='utf-8') as f:
                self.smap_data = json.load(f)
            
            self.conversion_log.append("✓ 成功加载SMAP文件")
            return True
            
        except Exception as e:
            self.conversion_log.append(f"✗ 加载SMAP文件失败: {str(e)}")
            return False
    
    def update_advanced_point_list(self) -> int:
        """更新SMAP中的advancedPointList"""
        if not self.smap_data or 'advancedPointList' not in self.smap_data:
            self.conversion_log.append("✗ SMAP文件中未找到advancedPointList字段")
            return 0
        
        updated_count = 0
        
        for advanced_point in self.smap_data['advancedPointList']:
            if 'instanceName' in advanced_point and advanced_point['instanceName'] in self.point_mapping:
                scene_point = self.point_mapping[advanced_point['instanceName']]
                
                # 更新pos坐标
                if 'pos' in advanced_point and 'x' in scene_point and 'y' in scene_point:
                    old_pos = f"({advanced_point['pos']['x']}, {advanced_point['pos']['y']})"
                    advanced_point['pos']['x'] = scene_point['x']
                    advanced_point['pos']['y'] = scene_point['y']
                    new_pos = f"({scene_point['x']}, {scene_point['y']})"
                    
                    self.conversion_log.append(f"  - 更新 {advanced_point['instanceName']}: {old_pos} -> {new_pos}")
                    updated_count += 1
        
        self.conversion_log.append(f"✓ advancedPointList更新完成，共更新 {updated_count} 个点位")
        return updated_count
    
    def update_advanced_curve_list(self) -> int:
        """更新SMAP中路径的控制点controlPos"""
        if not self.smap_data or 'advancedCurveList' not in self.smap_data:
            self.conversion_log.append("✗ SMAP文件中未找到advancedCurveList字段")
            return 0
        
        updated_count = 0
        
        # 路径类型映射表
        path_type_map = {
            "StraightPath": "line",
            "BezierPath": "BezierPath", 
            "ArcPath": "bezier2",
            "DegenerateBezier": "bezier3",
            "NURBS6": "NURBS6"
        }
        
        for curve in self.smap_data['advancedCurveList']:
            curve_updated = False
            
            # 更新startPos
            if ('startPos' in curve and 'instanceName' in curve['startPos'] and 
                curve['startPos']['instanceName'] in self.point_mapping):
                
                scene_point = self.point_mapping[curve['startPos']['instanceName']]
                if 'pos' in curve['startPos'] and 'x' in scene_point and 'y' in scene_point:
                    curve['startPos']['pos']['x'] = scene_point['x']
                    curve['startPos']['pos']['y'] = scene_point['y']
                    curve_updated = True
            
            # 更新endPos
            if ('endPos' in curve and 'instanceName' in curve['endPos'] and 
                curve['endPos']['instanceName'] in self.point_mapping):
                
                scene_point = self.point_mapping[curve['endPos']['instanceName']]
                if 'pos' in curve['endPos'] and 'x' in scene_point and 'y' in scene_point:
                    curve['endPos']['pos']['x'] = scene_point['x']
                    curve['endPos']['pos']['y'] = scene_point['y']
                    curve_updated = True
            
            # 根据路径类型更新控制点
            if ('startPos' in curve and 'endPos' in curve and 
                'pos' in curve['startPos'] and 'pos' in curve['endPos']):
                
                # 尝试从Scene数据中获取对应的路径信息
                start_name = curve['startPos'].get('instanceName', '')
                end_name = curve['endPos'].get('instanceName', '')
                route_key = f"{start_name}-{end_name}"
                scene_route = self.route_mapping.get(route_key)
                
                # 获取路径类型
                class_name = curve.get('className', '')
                curve_type = curve.get('type', '')
                mapped_type = path_type_map.get(class_name, path_type_map.get(curve_type, "bezier3"))
                
                start_x = curve['startPos']['pos']['x']
                start_y = curve['startPos']['pos']['y']
                end_x = curve['endPos']['pos']['x']
                end_y = curve['endPos']['pos']['y']
                
                # 根据路径类型更新不同的控制点
                if mapped_type == "line":
                    # 直线不需要控制点，但如果存在则保持原值或删除
                    pass
                elif mapped_type == "bezier2":
                    # bezier2只需要controlPos1
                    if 'controlPos1' in curve:
                        if scene_route and 'c1' in scene_route:
                            # 使用Scene中的控制点信息
                            curve['controlPos1']['x'] = scene_route['c1']['x']
                            curve['controlPos1']['y'] = scene_route['c1']['y']
                        else:
                            # 使用中点作为控制点
                            curve['controlPos1']['x'] = (start_x + end_x) / 2
                            curve['controlPos1']['y'] = (start_y + end_y) / 2
                        curve_updated = True
                elif mapped_type in ["BezierPath", "bezier3"]:
                    # BezierPath和bezier3需要controlPos1和controlPos2
                    if 'controlPos1' in curve:
                        if scene_route and 'c1' in scene_route:
                            curve['controlPos1']['x'] = scene_route['c1']['x']
                            curve['controlPos1']['y'] = scene_route['c1']['y']
                        else:
                            curve['controlPos1']['x'] = start_x + (end_x - start_x) / 3
                            curve['controlPos1']['y'] = start_y + (end_y - start_y) / 3
                        curve_updated = True
                    
                    if 'controlPos2' in curve:
                        if scene_route and 'c2' in scene_route:
                            curve['controlPos2']['x'] = scene_route['c2']['x']
                            curve['controlPos2']['y'] = scene_route['c2']['y']
                        else:
                            curve['controlPos2']['x'] = start_x + 2 * (end_x - start_x) / 3
                            curve['controlPos2']['y'] = start_y + 2 * (end_y - start_y) / 3
                        curve_updated = True
                elif mapped_type == "NURBS6":
                    # NURBS6需要controlPos1, controlPos2, controlPos3, controlPos4
                    if 'controlPos1' in curve:
                        if scene_route and 'c1' in scene_route:
                            curve['controlPos1']['x'] = scene_route['c1']['x']
                            curve['controlPos1']['y'] = scene_route['c1']['y']
                            if 'z' in scene_route['c1']:
                                curve['controlPos1']['z'] = scene_route['c1']['z']
                        else:
                            curve['controlPos1']['x'] = start_x + (end_x - start_x) / 5
                            curve['controlPos1']['y'] = start_y + (end_y - start_y) / 5
                            if 'z' in curve['controlPos1']:
                                start_z = curve['startPos']['pos'].get('z', 0)
                                end_z = curve['endPos']['pos'].get('z', 0)
                                curve['controlPos1']['z'] = start_z + (end_z - start_z) / 5
                        curve_updated = True
                    
                    if 'controlPos2' in curve:
                        if scene_route and 'c2' in scene_route:
                            curve['controlPos2']['x'] = scene_route['c2']['x']
                            curve['controlPos2']['y'] = scene_route['c2']['y']
                            if 'z' in scene_route['c2']:
                                curve['controlPos2']['z'] = scene_route['c2']['z']
                        else:
                            curve['controlPos2']['x'] = start_x + 2 * (end_x - start_x) / 5
                            curve['controlPos2']['y'] = start_y + 2 * (end_y - start_y) / 5
                            if 'z' in curve['controlPos2']:
                                start_z = curve['startPos']['pos'].get('z', 0)
                                end_z = curve['endPos']['pos'].get('z', 0)
                                curve['controlPos2']['z'] = start_z + 2 * (end_z - start_z) / 5
                        curve_updated = True
                    
                    if 'controlPos3' in curve:
                        if scene_route and 'c3' in scene_route:
                            curve['controlPos3']['x'] = scene_route['c3']['x']
                            curve['controlPos3']['y'] = scene_route['c3']['y']
                            if 'z' in scene_route['c3']:
                                curve['controlPos3']['z'] = scene_route['c3']['z']
                        else:
                            curve['controlPos3']['x'] = start_x + 3 * (end_x - start_x) / 5
                            curve['controlPos3']['y'] = start_y + 3 * (end_y - start_y) / 5
                            if 'z' in curve['controlPos3']:
                                start_z = curve['startPos']['pos'].get('z', 0)
                                end_z = curve['endPos']['pos'].get('z', 0)
                                curve['controlPos3']['z'] = start_z + 3 * (end_z - start_z) / 5
                        curve_updated = True
                    
                    if 'controlPos4' in curve:
                        if scene_route and 'c4' in scene_route:
                            curve['controlPos4']['x'] = scene_route['c4']['x']
                            curve['controlPos4']['y'] = scene_route['c4']['y']
                            if 'z' in scene_route['c4']:
                                curve['controlPos4']['z'] = scene_route['c4']['z']
                        else:
                            curve['controlPos4']['x'] = start_x + 4 * (end_x - start_x) / 5
                            curve['controlPos4']['y'] = start_y + 4 * (end_y - start_y) / 5
                            if 'z' in curve['controlPos4']:
                                start_z = curve['startPos']['pos'].get('z', 0)
                                end_z = curve['endPos']['pos'].get('z', 0)
                                curve['controlPos4']['z'] = start_z + 4 * (end_z - start_z) / 5
                        curve_updated = True
            
            if curve_updated:
                updated_count += 1
        
        # 统计不同路径类型的数量
        type_counts = {}
        for curve in self.smap_data['advancedCurveList']:
            class_name = curve.get('className', '')
            curve_type = curve.get('type', '')
            mapped_type = path_type_map.get(class_name, path_type_map.get(curve_type, "bezier3"))
            type_counts[mapped_type] = type_counts.get(mapped_type, 0) + 1
        
        type_info = ", ".join([f"{t}: {c}" for t, c in type_counts.items()])
        self.conversion_log.append(f"✓ advancedCurveList更新完成，共更新 {updated_count} 条路径")
        self.conversion_log.append(f"  路径类型分布: {type_info}")
        return updated_count
    
    def update_bin_locations_list(self) -> int:
        """更新SMAP中binLocationsList中点位的stringValue参数"""
        if not self.smap_data or 'binLocationsList' not in self.smap_data:
            self.conversion_log.append("✗ SMAP文件中未找到binLocationsList字段")
            return 0
        
        updated_count = 0
        
        for bin_location_group in self.smap_data['binLocationsList']:
            if 'binLocationList' not in bin_location_group:
                continue
                
            for bin_location in bin_location_group['binLocationList']:
                if ('pointName' in bin_location and 
                    bin_location['pointName'] in self.point_mapping):
                    
                    scene_point = self.point_mapping[bin_location['pointName']]
                    
                    # 更新pos坐标
                    if 'pos' in bin_location and 'x' in scene_point and 'y' in scene_point:
                        bin_location['pos']['x'] = scene_point['x']
                        bin_location['pos']['y'] = scene_point['y']
                    
                    # 更新property中的points字段（stringValue）
                    if 'property' in bin_location:
                        for prop in bin_location['property']:
                            if (prop.get('key') == 'points' and 
                                prop.get('type') == 'json' and 
                                'stringValue' in prop):
                                
                                try:
                                    # 解析原来的points数据
                                    points_data = json.loads(prop['stringValue'])
                                    
                                    if isinstance(points_data, list) and len(points_data) > 0:
                                        # 计算偏移量（假设第一个点是基准点）
                                        if len(points_data) > 0 and 'x' in points_data[0] and 'y' in points_data[0]:
                                            # 计算中心点偏移
                                            center_x = sum(p['x'] for p in points_data) / len(points_data)
                                            center_y = sum(p['y'] for p in points_data) / len(points_data)
                                            
                                            offset_x = scene_point['x'] - center_x
                                            offset_y = scene_point['y'] - center_y
                                            
                                            # 更新所有点的坐标
                                            for point in points_data:
                                                point['x'] += offset_x
                                                point['y'] += offset_y
                                            
                                            # 更新stringValue
                                            prop['stringValue'] = json.dumps(points_data)
                                            updated_count += 1
                                            
                                            self.conversion_log.append(f"  - 更新 {bin_location['pointName']} 的区域坐标")
                                            
                                except (json.JSONDecodeError, KeyError, TypeError) as e:
                                    self.conversion_log.append(f"  - 解析 {bin_location['pointName']} 的points数据失败: {str(e)}")
        
        self.conversion_log.append(f"✓ binLocationsList更新完成，共更新 {updated_count} 个点位区域")
        return updated_count
    
    def convert(self, scene_file_path: str, smap_file_path: str, output_path: str = None) -> dict:
        """执行转换"""
        self.conversion_log = []
        
        # 加载文件
        if not self.load_scene_file(scene_file_path):
            return {"success": False, "log": self.conversion_log}
        
        if not self.load_smap_file(smap_file_path):
            return {"success": False, "log": self.conversion_log}
        
        # 执行转换
        total_updated = 0
        total_updated += self.update_advanced_point_list()
        total_updated += self.update_advanced_curve_list()
        total_updated += self.update_bin_locations_list()
        
        # 保存结果
        if output_path is None:
            base_name = os.path.splitext(os.path.basename(smap_file_path))[0]
            output_path = f"{base_name}_updated.smap"
        
        try:
            with open(output_path, 'w', encoding='utf-8') as f:
                json.dump(self.smap_data, f, ensure_ascii=False, indent=2)
            
            self.conversion_log.append(f"✓ 转换完成，共更新 {total_updated} 项")
            self.conversion_log.append(f"✓ 输出文件: {output_path}")
            
            return {
                "success": True,
                "output_file": output_path,
                "updated_count": total_updated,
                "log": self.conversion_log,
                "data": self.smap_data
            }
            
        except Exception as e:
            self.conversion_log.append(f"✗ 保存文件失败: {str(e)}")
            return {"success": False, "log": self.conversion_log}


def convert_scene_to_smap(scene_file_path: str, smap_file_path: str, output_path: str = None) -> dict:
    """Scene到SMAP转换的主入口函数"""
    converter = SceneToSmapConverter()
    return converter.convert(scene_file_path, smap_file_path, output_path)


if __name__ == "__main__":
    # 测试转换
    import sys
    
    if len(sys.argv) < 3:
        print("使用方法: python scene_to_smap_converter.py <scene_file> <smap_file> [output_file]")
        sys.exit(1)
    
    scene_file = sys.argv[1]
    smap_file = sys.argv[2]
    output_file = sys.argv[3] if len(sys.argv) > 3 else None
    
    result = convert_scene_to_smap(scene_file, smap_file, output_file)
    
    print("\n=== 转换结果 ===")
    for log_entry in result.get('log', []):
        print(log_entry)
    
    if result['success']:
        print(f"\n转换成功！输出文件: {result.get('output_file', 'N/A')}")
    else:
        print("\n转换失败！")
        sys.exit(1)
