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

"""
GUI预览窗口 - 使用现有ShowBase创建额外窗口，专门用来显示GUI元素
解决Qt集成中2D GUI显示问题，避免多个ShowBase实例错误
"""

import warnings
warnings.filterwarnings("ignore", category=DeprecationWarning)

from direct.gui.DirectGui import *
from panda3d.core import *
from direct.showbase.ShowBase import ShowBase

class GUIPreviewWindow:
    """使用现有ShowBase的额外窗口进行GUI预览"""
    
    def __init__(self):
        self.preview_window = None
        self.chinese_font = None
        self.gui_elements = []
        self.main_world = None  # 主程序world对象的引用
        self.sync_timer = None
        
        # 预览窗口的渲染节点
        self.preview_render2d = None
        self.preview_camera2d = None
        self.preview_aspect2d = None
        
        print("✓ GUI预览窗口类已初始化")
        
    def set_main_world(self, world):
        """设置主程序world引用并初始化预览窗口"""
        self.main_world = world
        if world:
            self.init_preview_window()
            self.start_sync_timer()
        
    def init_preview_window(self):
        """使用现有ShowBase创建预览窗口"""
        try:
            if not self.main_world:
                print("错误: 没有主程序world引用")
                return False
                
            # 使用主程序的base创建额外窗口
            base = self.main_world  # MyWorld继承自ShowBase
            
            # 尝试从Qt部件获取渲染区域的实际尺寸
            main_width = 800   # 默认宽度
            main_height = 600  # 默认高度
            
            # 检查是否有Qt应用程序实例
            try:
                from PyQt5.QtWidgets import QApplication
                app = QApplication.instance()
                if app:
                    # 查找主窗口
                    for widget in app.topLevelWidgets():
                        if hasattr(widget, 'centralWidget'):
                            central_widget = widget.centralWidget()
                            if central_widget and hasattr(central_widget, 'world'):
                                # 找到了Panda3D部件
                                qt_width = central_widget.width()
                                qt_height = central_widget.height()
                                if qt_width > 0 and qt_height > 0:
                                    main_width = qt_width
                                    main_height = qt_height
                                    print(f"从Qt部件获取尺寸: {main_width} x {main_height}")
                                    break
                    else:
                        print("未找到Qt Panda3D部件，使用默认尺寸")
                else:
                    print("未找到Qt应用程序实例，使用默认尺寸")
            except ImportError:
                print("未找到PyQt5，使用默认尺寸")
            except Exception as e:
                print(f"从Qt获取尺寸失败: {str(e)}，使用默认尺寸")
            
            # 如果Qt方法失败，尝试从Panda3D窗口获取
            if main_width == 800 and main_height == 600:
                main_window = base.win
                if main_window and main_window.hasSize():
                    panda_width = main_window.getXSize()
                    panda_height = main_window.getYSize()
                    if panda_width > 0 and panda_height > 0:
                        main_width = panda_width
                        main_height = panda_height
                        print(f"从Panda3D窗口获取尺寸: {main_width} x {main_height}")
                else:
                    print(f"Panda3D窗口尺寸无效，使用默认尺寸: {main_width} x {main_height}")
            
            # 设置窗口属性，使用获取到的尺寸
            wp = WindowProperties()
            wp.setSize(main_width, main_height)
            wp.setTitle("GUI预览窗口 - 2D GUI专用显示")
            wp.setOrigin(main_width + 50, 50)  # 放在主窗口右侧
            wp.setUndecorated(False)  # 保持窗口装饰
            # 注意：WindowProperties没有setMaximized和setMinimized方法
            
            print(f"设置预览窗口属性: 尺寸={main_width}x{main_height}, 位置=({main_width + 50}, 50)")
            
            # 获取图形管道
            pipe = base.pipe
            if not pipe:
                print("错误: 无法获取图形管道")
                return False
            
            # 创建帧缓冲属性
            fbp = FrameBufferProperties.getDefault()
            
            # 使用makeOutput创建窗口，强制要求窗口类型
            self.preview_window = base.graphicsEngine.makeOutput(
                pipe, "GUI预览窗口", 0,
                fbp, wp, GraphicsPipe.BFRequireWindow
            )
            
            if not self.preview_window:
                print("错误: 无法创建预览窗口")
                return False
            
            print(f"预览窗口创建成功，类型: {type(self.preview_window).__name__}")
            
            # 强制打开窗口
            base.graphicsEngine.openWindows()
            
            # 验证创建的窗口尺寸
            if hasattr(self.preview_window, 'getXSize'):
                actual_width = self.preview_window.getXSize()
                actual_height = self.preview_window.getYSize()
                print(f"实际创建的预览窗口尺寸: {actual_width} x {actual_height}")
                
                if actual_width != main_width or actual_height != main_height:
                    print(f"警告: 窗口尺寸不匹配！期望: {main_width}x{main_height}, 实际: {actual_width}x{actual_height}")
                    
                    # 尝试重新设置尺寸
                    new_props = WindowProperties()
                    new_props.setSize(main_width, main_height)
                    self.preview_window.requestProperties(new_props)
                    base.graphicsEngine.renderFrame()
                    
                    # 再次检查
                    final_width = self.preview_window.getXSize()
                    final_height = self.preview_window.getYSize()
                    print(f"重新设置后的尺寸: {final_width} x {final_height}")
            
            # 设置预览窗口的背景色
            base.setBackgroundColor(0.15, 0.15, 0.25, 1.0, self.preview_window)
            
            # 创建预览窗口的2D渲染节点
            self.preview_render2d = NodePath('preview_render2d')
            
            # 为预览窗口创建2D相机，使用正确的宽高比
            aspect_ratio = float(main_width) / float(main_height)
            self.preview_camera2d = base.makeCamera2d(self.preview_window)
            self.preview_camera2d.reparentTo(self.preview_render2d)
            
            # 设置2D渲染属性
            self.preview_render2d.setDepthWrite(0)
            self.preview_render2d.setMaterialOff(1)
            self.preview_render2d.setTwoSided(1)
            
            # 检查窗口类型并设置输入
            print(f"预览窗口类型: {type(self.preview_window)}")
            print(f"是否为GraphicsWindow: {hasattr(self.preview_window, 'getNumInputDevices')}")
            
            # 只为真正的GraphicsWindow设置输入
            if hasattr(self.preview_window, 'getNumInputDevices') and self.preview_window.getNumInputDevices() > 0:
                # 设置鼠标和键盘输入
                mk = base.dataRoot.attachNewNode(MouseAndKeyboard(self.preview_window, 0, 'previewMouse'))
                mw = mk.attachNewNode(MouseWatcher('previewMouseWatcher'))
                self.preview_aspect2d = self.preview_render2d.attachNewNode(PGTop('previewAspect2d'))
                self.preview_aspect2d.node().setMouseWatcher(mw.node())
                print("✓ 已设置预览窗口的鼠标和键盘输入")
            else:
                print("⚠ 跳过鼠标/键盘设置（窗口不支持或没有输入设备）")
                # 创建没有输入的aspect2d节点
                self.preview_aspect2d = self.preview_render2d.attachNewNode(PGTop('previewAspect2d'))
            
            # 加载中文字体
            self.load_font()
            
            # 创建界面说明
            self.create_ui_info()
            
            # 最终验证
            if hasattr(self.preview_window, 'getXSize'):
                final_width = self.preview_window.getXSize()
                final_height = self.preview_window.getYSize()
                print(f"✓ GUI预览窗口已成功创建（最终尺寸: {final_width}x{final_height}）")
            else:
                print(f"✓ GUI预览窗口已成功创建（期望尺寸: {main_width}x{main_height}）")
            
            return True
            
        except Exception as e:
            print(f"创建GUI预览窗口失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return False
    
    def load_font(self):
        """加载中文字体"""
        try:
            if self.main_world and hasattr(self.main_world, 'loader'):
                self.chinese_font = self.main_world.loader.loadFont('/usr/share/fonts/truetype/wqy/wqy-microhei.ttc')
                if not self.chinese_font:
                    print("预览窗口: 无法加载中文字体，将使用默认字体")
        except:
            print("预览窗口: 无法加载中文字体，将使用默认字体")
            self.chinese_font = None
    
    def create_ui_info(self):
        """创建界面说明"""
        if not self.preview_aspect2d:
            return
            
        # # 创建欢迎信息
        # self.welcome_text = OnscreenText(
        #     text="GUI预览窗口\n\n这个窗口使用现有的ShowBase\n创建额外的显示窗口\n\n可以正确显示2D GUI元素",
        #     parent=self.preview_aspect2d,
        #     pos=(0, 0.2),
        #     scale=0.08,
        #     fg=(1, 1, 1, 1),
        #     align=TextNode.ACenter,
        #     font=self.chinese_font if self.chinese_font else None
        # )
        
        # # 创建提示信息
        # self.tip_text = OnscreenText(
        #     text="这个窗口与主程序共享ShowBase，避免了多实例错误",
        #     parent=self.preview_aspect2d,
        #     pos=(0, -0.7),
        #     scale=0.05,
        #     fg=(0.8, 0.8, 0.8, 1),
        #     align=TextNode.ACenter,
        #     font=self.chinese_font if self.chinese_font else None
        # )
        
        # # 创建同步状态显示
        # self.sync_text = OnscreenText(
        #     text="同步状态: 等待检测",
        #     parent=self.preview_aspect2d,
        #     pos=(-0.9, 0.8),
        #     scale=0.04,
        #     fg=(0, 1, 0, 1),
        #     align=TextNode.ALeft,
        #     font=self.chinese_font if self.chinese_font else None
        # )
    
    def start_sync_timer(self):
        """启动同步定时器"""
        if self.main_world and hasattr(self.main_world, 'taskMgr'):
            # 每0.5秒检查一次GUI元素变化
            self.main_world.taskMgr.doMethodLater(0.5, self.sync_gui_elements, 'sync_preview_gui')
            print("✓ GUI同步定时器已启动")
    
    def sync_gui_elements(self, task=None):
        """同步主程序的GUI元素到预览窗口"""
        try:
            if not self.main_world or not self.preview_aspect2d:
                return task.again if task else None
                
            # 获取主程序中的GUI元素
            main_gui_elements = getattr(self.main_world, 'gui_elements', [])
            
            # 检查是否需要重新创建所有元素
            current_count = len(main_gui_elements)
            preview_count = len(self.gui_elements)
            
            need_full_sync = False
            
            # 如果数量有变化，需要完全重新同步
            if current_count != preview_count:
                need_full_sync = True
                print(f"GUI数量变化: {preview_count} -> {current_count}")
            else:
                # 检查现有元素的属性是否有变化
                for i, main_element in enumerate(main_gui_elements):
                    if i < len(self.gui_elements):
                        if self.has_element_changed(main_element, self.gui_elements[i]):
                            need_full_sync = True
                            print(f"GUI元素 {i} 属性发生变化")
                            break
            
            # 执行同步
            if need_full_sync:
                print("执行完整同步...")
                self.clear_preview_elements()
                self.create_preview_elements(main_gui_elements)
                
                # 更新同步状态显示
                if hasattr(self, 'sync_text'):
                    self.sync_text.setText(f"同步状态: 已同步 {current_count} 个GUI元素")
                
                print(f"同步完成: {current_count} 个GUI元素")
            
            return task.again if task else None
            
        except Exception as e:
            print(f"同步GUI元素失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return task.again if task else None
    
    def has_element_changed(self, main_element, preview_element):
        """检查主程序GUI元素是否发生了变化"""
        try:
            if not hasattr(main_element, 'getTag') or not hasattr(preview_element, 'getTag'):
                return True
            
            # 检查文本是否变化
            main_text = main_element.getTag("gui_text")
            preview_text = getattr(preview_element, '_last_synced_text', "")
            if main_text != preview_text:
                return True
            
            # 检查位置是否变化
            if hasattr(main_element, 'getPos') and hasattr(preview_element, 'getPos'):
                main_pos = main_element.getPos()
                preview_pos = getattr(preview_element, '_last_synced_pos', Point3(999, 999, 999))
                if (abs(main_pos.getX() - preview_pos.getX()) > 0.001 or 
                    abs(main_pos.getY() - preview_pos.getY()) > 0.001 or 
                    abs(main_pos.getZ() - preview_pos.getZ()) > 0.001):
                    return True
            
            # 检查缩放是否变化
            if hasattr(main_element, 'getScale') and hasattr(preview_element, 'getScale'):
                main_scale = main_element.getScale().getX()
                preview_scale = getattr(preview_element, '_last_synced_scale', -1)
                if abs(main_scale - preview_scale) > 0.001:
                    return True
            
            return False
            
        except Exception as e:
            print(f"检查元素变化失败: {str(e)}")
            return True  # 出错时强制同步
    
    def clear_preview_elements(self):
        """清空预览窗口中的GUI元素"""
        for element in self.gui_elements:
            try:
                if hasattr(element, 'removeNode'):
                    element.removeNode()
                elif hasattr(element, 'destroy'):
                    element.destroy()
            except:
                pass
        
        self.gui_elements.clear()
        
        # 如果没有GUI元素了，显示说明文本
        if hasattr(self, 'welcome_text') and self.welcome_text:
            self.welcome_text.show()
        if hasattr(self, 'tip_text') and self.tip_text:
            self.tip_text.show()
    
    def create_preview_elements(self, main_gui_elements):
        """在预览窗口中创建GUI元素"""
        if not main_gui_elements or not self.preview_aspect2d:
            return
            
        # 隐藏说明文本
        if hasattr(self, 'welcome_text') and self.welcome_text:
            self.welcome_text.hide()
        if hasattr(self, 'tip_text') and self.tip_text:
            self.tip_text.hide()
        
        for main_element in main_gui_elements:
            try:
                preview_element = self.create_preview_element(main_element)
                if preview_element:
                    self.gui_elements.append(preview_element)
            except Exception as e:
                print(f"创建预览元素失败: {str(e)}")
    
    def create_preview_element(self, main_element):
        """根据主程序GUI元素创建对应的预览元素"""
        try:
            if not hasattr(main_element, 'getTag'):
                return None
                
            gui_type = main_element.getTag("gui_type")
            gui_text = main_element.getTag("gui_text")
            
            if not gui_type:
                return None
            
            # 获取位置和缩放
            pos = main_element.getPos() if hasattr(main_element, 'getPos') else (0, 0, 0)
            scale = main_element.getScale().getX() if hasattr(main_element, 'getScale') else 0.1
            
            preview_element = None
            
            if gui_type == "button":
                preview_element = DirectButton(
                    parent=self.preview_aspect2d,
                    text=gui_text or "按钮",
                    pos=pos,
                    scale=scale,
                    frameColor=(0.2, 0.6, 0.8, 1),
                    text_font=self.chinese_font if self.chinese_font else None,
                    command=lambda: print(f"预览按钮点击: {gui_text}")
                )
                
            elif gui_type == "label":
                preview_element = DirectLabel(
                    parent=self.preview_aspect2d,
                    text=gui_text or "标签",
                    pos=pos,
                    scale=scale,
                    frameColor=(0, 0, 0, 0),
                    text_fg=(1, 1, 1, 1),
                    text_font=self.chinese_font if self.chinese_font else None
                )
                
            elif gui_type == "entry":
                preview_element = DirectEntry(
                    parent=self.preview_aspect2d,
                    text="",
                    pos=pos,
                    scale=scale,
                    initialText=gui_text or "输入框",
                    numLines=1,
                    width=12,
                    focus=0,
                    command=lambda text: print(f"预览输入框: {text}")
                )
                
            elif gui_type == "slider":
                preview_element = DirectSlider(
                    parent=self.preview_aspect2d,
                    pos=pos,
                    scale=scale,
                    range=(0, 100),
                    value=50,
                    frameColor=(0.6, 0.6, 0.6, 1),
                    thumbColor=(0.2, 0.8, 0.2, 1),
                    command=lambda: print("预览滑块值变化")
                )
                
            elif gui_type == "3d_text":
                # 3D文本需要特殊处理，在3D空间中显示
                textNode = TextNode(f'preview-3d-text-{len(self.gui_elements)}')
                textNode.setText(gui_text or "3D文本")
                textNode.setAlign(TextNode.ACenter)
                if self.chinese_font:
                    textNode.setFont(self.chinese_font)
                
                # 需要一个3D渲染节点
                if not hasattr(self, 'preview_render3d'):
                    self.preview_render3d = NodePath('preview_render3d')
                    # 为预览窗口创建3D相机
                    camera3d = self.main_world.makeCamera(self.preview_window)
                    camera3d.reparentTo(self.preview_render3d)
                    camera3d.setPos(0, -10, 0)
                    camera3d.lookAt(0, 0, 0)
                
                preview_element = self.preview_render3d.attachNewNode(textNode)
                preview_element.setPos(*pos)
                preview_element.setScale(scale)
                preview_element.setColor(1, 1, 0, 1)
                preview_element.setBillboardAxis()
                
            elif gui_type == "virtual_screen":
                # 虚拟屏幕也需要3D渲染
                if not hasattr(self, 'preview_render3d'):
                    self.preview_render3d = NodePath('preview_render3d')
                    camera3d = self.main_world.makeCamera(self.preview_window)
                    camera3d.reparentTo(self.preview_render3d)
                    camera3d.setPos(0, -10, 0)
                    camera3d.lookAt(0, 0, 0)
                
                cm = CardMaker(f"preview-virtual-screen-{len(self.gui_elements)}")
                cm.setFrame(-1, 1, -0.5, 0.5)
                preview_element = self.preview_render3d.attachNewNode(cm.generate())
                preview_element.setPos(*pos)
                preview_element.setColor(0.2, 0.2, 0.2, 0.8)
                preview_element.setTransparency(TransparencyAttrib.MAlpha)
                
                # 在虚拟屏幕上添加文本
                screenText = TextNode(f'preview-screen-text-{len(self.gui_elements)}')
                screenText.setText(gui_text or "虚拟屏幕")
                screenText.setAlign(TextNode.ACenter)
                if self.chinese_font:
                    screenText.setFont(self.chinese_font)
                screenTextNP = preview_element.attachNewNode(screenText)
                screenTextNP.setPos(0, 0.01, 0)
                screenTextNP.setScale(0.3)
                screenTextNP.setColor(0, 1, 0, 1)
            
            # 为预览元素记录同步状态，用于变化检测
            if preview_element:
                preview_element._last_synced_text = gui_text or ""
                preview_element._last_synced_pos = Point3(pos) if hasattr(main_element, 'getPos') else Point3(0, 0, 0)
                preview_element._last_synced_scale = scale
                print(f"创建预览元素: {gui_type} - {gui_text} (位置: {pos}, 缩放: {scale})")
            
            return preview_element
            
        except Exception as e:
            print(f"创建预览元素失败: {str(e)}")
            import traceback
            traceback.print_exc()
            return None
    
    def destroy(self):
        """销毁预览窗口"""
        try:
            # 停止同步任务
            if self.main_world and hasattr(self.main_world, 'taskMgr'):
                self.main_world.taskMgr.remove('sync_preview_gui')
            
            # 清空GUI元素
            self.clear_preview_elements()
            
            # 移除界面元素
            if hasattr(self, 'welcome_text') and self.welcome_text:
                self.welcome_text.removeNode()
            if hasattr(self, 'tip_text') and self.tip_text:
                self.tip_text.removeNode()
            if hasattr(self, 'sync_text') and self.sync_text:
                self.sync_text.removeNode()
            
            # 关闭窗口
            if self.preview_window and self.main_world:
                self.main_world.closeWindow(self.preview_window)
                self.preview_window = None
            
            print("预览窗口已销毁")
            
        except Exception as e:
            print(f"销毁预览窗口失败: {str(e)}")

# 测试代码
if __name__ == "__main__":
    print("这是GUI预览窗口模块")
    print("需要与主程序集成使用，不能独立运行") 