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

"""
缩放位置测试 - 验证缩放标准化时位置是否正确保持

测试内容：
1. 创建模拟FBX层级结构
2. 验证缩放标准化前后的世界位置
3. 对比修复前后的效果

控制说明：
- I键：显示当前模型信息
- N键：切换缩放标准化
- R键：重置模型
- T键：运行位置测试
- Q键：退出
"""

import sys
import os
sys.path.append('..')  # 添加父目录到路径

from direct.showbase.ShowBase import ShowBase
from panda3d.core import (CardMaker, Vec3, Point3, Material, 
                         ModelRoot, PandaNode, LineSegs, ColorAttrib, RenderState,
                         DepthTestAttrib)
from PyQt5.QtWidgets import QApplication

def create_test_fbx_structure(world):
    """创建模拟FBX结构用于测试"""
    print("\n=== 创建模拟FBX结构 ===")
    
    # 创建根节点
    root = world.render.attachNewNode(ModelRoot("test_fbx_model"))
    root.setScale(1.0)  # 根节点缩放为1
    root.setPos(10, 5, 2)  # 给根节点一个偏移位置
    
    # 创建子节点1 - 模拟有大缩放值的子节点
    child1_node = PandaNode("child1")
    child1 = root.attachNewNode(child1_node)
    child1.setScale(100.0)  # 大缩放值
    child1.setPos(5, 0, 0)  # 相对于父节点的位置
    
    # 创建子节点2 - 另一个有大缩放值的子节点
    child2_node = PandaNode("child2")
    child2 = root.attachNewNode(child2_node)
    child2.setScale(100.0)  # 大缩放值
    child2.setPos(-3, 8, 1)  # 相对于父节点的位置
    
    # 创建孙子节点 - 嵌套结构
    grandchild_node = PandaNode("grandchild")
    grandchild = child1.attachNewNode(grandchild_node)
    grandchild.setScale(100.0)  # 大缩放值
    grandchild.setPos(2, 2, 1)  # 相对于父节点的位置
    
    # 创建可视化几何体
    def create_marker(node, color):
        """为节点创建可视化标记"""
        cm = CardMaker(f'marker_{node.getName()}')
        cm.setFrame(-0.5, 0.5, -0.5, 0.5)
        marker = node.attachNewNode(cm.generate())
        marker.setColor(*color)
        marker.setBillboardAxis()  # 始终面向相机
        return marker
    
    # 为各节点创建可视化标记
    create_marker(root, (1, 0, 0, 1))        # 红色 - 根节点
    create_marker(child1, (0, 1, 0, 1))      # 绿色 - 子节点1
    create_marker(child2, (0, 0, 1, 1))      # 蓝色 - 子节点2
    create_marker(grandchild, (1, 1, 0, 1))  # 黄色 - 孙子节点
    
    # 添加到模型列表
    world.scene_manager.models.append(root)
    
    print("✓ 模拟FBX结构创建完成")
    return root

def print_position_info(node, label, depth=0):
    """打印节点的位置和缩放信息"""
    indent = "  " * depth
    local_pos = node.getPos()
    world_pos = node.getPos(node.getTopParent())
    scale = node.getScale()
    
    print(f"{indent}{label}:")
    print(f"{indent}  本地位置: {local_pos}")
    print(f"{indent}  世界位置: {world_pos}")
    print(f"{indent}  缩放: {scale}")

def run_position_test(world, model):
    """运行位置测试"""
    print("\n=== 位置测试开始 ===")
    
    # 收集所有节点
    nodes = []
    def collect_nodes(node):
        nodes.append(node)
        for i in range(node.getNumChildren()):
            collect_nodes(node.getChild(i))
    
    collect_nodes(model)
    
    # 记录标准化前的位置
    print("\n--- 标准化前的位置信息 ---")
    before_positions = {}
    for i, node in enumerate(nodes):
        label = f"节点{i+1}({node.getName()})"
        print_position_info(node, label)
        before_positions[node.getName()] = {
            'local': node.getPos(),
            'world': node.getPos(world.render),
            'scale': node.getScale()
        }
    
    # 应用缩放标准化
    print("\n--- 应用缩放标准化 ---")
    world.scene_manager._normalizeModelScales(model)
    
    # 记录标准化后的位置
    print("\n--- 标准化后的位置信息 ---")
    after_positions = {}
    for i, node in enumerate(nodes):
        label = f"节点{i+1}({node.getName()})"
        print_position_info(node, label)
        after_positions[node.getName()] = {
            'local': node.getPos(),
            'world': node.getPos(world.render),
            'scale': node.getScale()
        }
    
    # 分析位置和缩放变化
    print("\n--- 位置和缩放变化分析 ---")
    for name in before_positions:
        before = before_positions[name]
        after = after_positions[name]
        
        scale_change = after['scale'] - before['scale']
        local_pos_change = after['local'] - before['local']
        world_pos_change = after['world'] - before['world']
        
        local_pos_distance = local_pos_change.length()
        world_pos_distance = world_pos_change.length()
        
        print(f"\n{name}:")
        print(f"  缩放变化: {before['scale']} -> {after['scale']}")
        print(f"  本地位置变化: {before['local']} -> {after['local']}")
        print(f"  本地位置变化距离: {local_pos_distance:.6f}")
        print(f"  世界位置变化距离: {world_pos_distance:.6f}")
        
        # 检查是否按比例缩放
        if before['scale'].x > 10:  # 如果原来有大缩放
            expected_scale_factor = 0.01  # 期望的缩放因子
            actual_scale_factor = after['scale'].x / before['scale'].x if before['scale'].x != 0 else 0
            expected_pos = before['local'] * expected_scale_factor
            pos_error = (after['local'] - expected_pos).length()
            
            print(f"  期望缩放因子: {expected_scale_factor}")
            print(f"  实际缩放因子: {actual_scale_factor:.6f}")
            print(f"  位置缩放误差: {pos_error:.6f}")
            
            if abs(actual_scale_factor - expected_scale_factor) < 0.001 and pos_error < 0.01:
                print(f"  ✓ 缩放和位置标准化正确")
            else:
                print(f"  ⚠ 缩放或位置标准化可能有问题")
        else:
            print(f"  ℹ 未标准化（缩放值正常）")
    
    print("\n=== 位置测试完成 ===")

class ScalePositionTest(ShowBase):
    def __init__(self):
        ShowBase.__init__(self)
        
        # 导入我们的模块
        from main import MyWorld
        
        # 创建世界实例
        self.world = MyWorld()
        
        # 设置相机
        self.cam.setPos(0, -30, 10)
        self.cam.lookAt(0, 0, 0)
        
        # 创建测试模型
        self.test_model = create_test_fbx_structure(self.world)
        
        # 设置键盘事件
        self.setupKeyEvents()
        
        print("\n=== 缩放位置测试程序启动 ===")
        print("控制说明：")
        print("I键：显示当前模型信息")
        print("N键：应用缩放标准化")
        print("R键：重置模型")
        print("T键：运行完整位置测试")
        print("Q键：退出程序")
        print("================================")
    
    def setupKeyEvents(self):
        """设置键盘事件"""
        self.accept('i', self.showModelInfo)
        self.accept('n', self.applyNormalization)
        self.accept('r', self.resetModel)
        self.accept('t', self.runPositionTest)
        self.accept('q', self.quit)
        self.accept('escape', self.quit)
    
    def showModelInfo(self):
        """显示模型信息"""
        print("\n=== 当前模型信息 ===")
        if self.test_model:
            def show_node_info(node, depth=0):
                indent = "  " * depth
                print(f"{indent}节点: {node.getName()}")
                print(f"{indent}  本地位置: {node.getPos()}")
                print(f"{indent}  世界位置: {node.getPos(self.world.render)}")
                print(f"{indent}  缩放: {node.getScale()}")
                
                for i in range(node.getNumChildren()):
                    child = node.getChild(i)
                    show_node_info(child, depth + 1)
            
            show_node_info(self.test_model)
        print("==================")
    
    def applyNormalization(self):
        """应用缩放标准化"""
        print("\n=== 应用缩放标准化 ===")
        if self.test_model:
            self.world.scene_manager._normalizeModelScales(self.test_model)
            print("✓ 缩放标准化完成")
        else:
            print("× 没有找到测试模型")
    
    def resetModel(self):
        """重置模型"""
        print("\n=== 重置模型 ===")
        if self.test_model:
            self.test_model.removeNode()
        
        # 重新创建
        self.test_model = create_test_fbx_structure(self.world)
        print("✓ 模型重置完成")
    
    def runPositionTest(self):
        """运行完整位置测试"""
        if self.test_model:
            run_position_test(self.world, self.test_model)
        else:
            print("× 没有找到测试模型")
    
    def quit(self):
        """退出程序"""
        print("\n退出缩放位置测试程序")
        sys.exit()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    test = ScalePositionTest()
    test.run() 