import os
import sys
import json
import datetime
import subprocess
import shutil
from PyQt5.QtWidgets import (
    QDialog, QVBoxLayout, QHBoxLayout, QGroupBox, QLineEdit, QPushButton,
    QLabel, QDialogButtonBox, QFileDialog, QMessageBox, QMainWindow
)
from PyQt5.QtCore import Qt

# 导入自定义对话框
from ui.widgets import NewProjectDialog, UniversalMessageDialog

class ProjectManager:

    def __init__(self, world):

        self.world = world
        self.current_project_path = None
        self.project_config = None
        
        print("✓ 项目管理系统初始化完成")
    
    # ==================== 项目生命周期管理 ====================
    
    def createNewProject(self, parent_window):
        """创建新项目"""
        dialog = NewProjectDialog(parent_window)
        if dialog.exec_() != QDialog.Accepted:
            return False
            
        project_path = dialog.projectPath
        project_name = dialog.projectName
        # full_project_path = os.path.join(project_path, project_name)
        full_project_path = os.path.normpath(os.path.join(project_path, project_name))
        print(f"full_project_path: {full_project_path}")
        
        try:
            # 创建项目文件夹结构
            os.makedirs(full_project_path)
            os.makedirs(os.path.join(full_project_path, "models"))  # 模型文件夹
            os.makedirs(os.path.join(full_project_path, "textures"))  # 贴图文件夹
            scenes_path = os.path.join(full_project_path, "scenes")  # 场景文件夹
            os.makedirs(scenes_path)
            
            # 创建项目配置文件
            project_config = {
                "name": project_name,
                "path": full_project_path,
                "created_at": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "last_modified": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                "version": "1.0.0",
                "engine_version": "1.0.0"
            }
            
            config_file = os.path.join(full_project_path, "project.json")
            with open(config_file, "w", encoding="utf-8") as f:
                json.dump(project_config, f, ensure_ascii=False, indent=4)
                
            print(f"项目配置文件已创建: {config_file}")

            # 清空当前场景
            self._clearCurrentScene()

            # 自动保存初始场景
            scene_file = os.path.join(scenes_path, "scene.bam")
            if self.world.scene_manager.saveScene(scene_file, project_path):
                # 更新配置文件中的场景路径
                project_config["scene_file"] = os.path.relpath(scene_file, full_project_path)
                with open(config_file, "w", encoding="utf-8") as f:
                    json.dump(project_config, f, ensure_ascii=False, indent=4)
                print(f"初始场景已保存到: {scene_file}")
            else:
                print("初始场景保存失败")
                
            # 更新项目状态
            self.current_project_path = full_project_path
            self.project_config = project_config
            
            # 更新文件浏览器到新项目路径
            if hasattr(parent_window, 'fileView') and hasattr(parent_window, 'fileModel'):
                parent_window.fileView.setRootIndex(parent_window.fileModel.index(full_project_path))
            
            # 更新窗口标题
            self.updateWindowTitle(parent_window, project_name)
            
            # 保存当前项目路径到主窗口
            parent_window.current_project_path = full_project_path
            
            # 显示成功消息
            UniversalMessageDialog.show_success(
                parent_window,
                "成功",
                f"项目 '{project_name}' 创建成功!",
                show_cancel=False,
                confirm_text="确认"
            )
            return True
            
        except Exception as e:
            UniversalMessageDialog.show_error(
                parent_window,
                "错误",
                f"创建项目失败: {str(e)}",
                show_cancel=False,
                confirm_text="确认"
            )
            return False
    
    def openProject(self, parent_window):
        """打开项目"""
        try:
            # 获取项目路径
            project_path = QFileDialog.getExistingDirectory(
                parent_window,
                "选择项目文件夹",
                "",
                QFileDialog.ShowDirsOnly
            )
            
            if not project_path:
                return False

            # 检查是否是有效的项目文件夹
            config_file = os.path.join(project_path, "project.json")
            if not os.path.exists(config_file):
                UniversalMessageDialog.show_warning(
                    parent_window,
                    "警告",
                    "选择的不是有效的项目文件夹！",
                    show_cancel=False,
                    confirm_text="确认"
                )
                return False

            # 读取项目配置
            with open(config_file, "r", encoding="utf-8") as f:
                project_config = json.load(f)

            # 检查场景文件
            scene_file = os.path.join(project_path, "scenes", "scene.bam")
            if os.path.exists(scene_file):
                # 加载场景
                if self.world.scene_manager.loadScene(scene_file):
                    # 更新项目配置
                    project_config["scene_file"] = os.path.relpath(scene_file, project_path)

            project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            with open(config_file, "w", encoding="utf-8") as f:
                json.dump(project_config, f, ensure_ascii=False, indent=4)

            # 更新项目状态
            self.current_project_path = project_path
            self.project_config = project_config

            # 保存当前项目路径到主窗口
            parent_window.current_project_path = project_path

            # 更新窗口标题
            project_name = os.path.basename(project_path)
            self.updateWindowTitle(parent_window, project_name)

            # 更新文件浏览器
            if hasattr(parent_window, 'fileView') and hasattr(parent_window, 'fileModel'):
                parent_window.fileView.setRootIndex(parent_window.fileModel.index(project_path))

            UniversalMessageDialog.show_success(
                parent_window,
                "成功",
                "项目加载成功！",
                show_cancel=False,
                confirm_text="确认"
            )
            return True
            # 检查场景文件
            # scene_file = os.path.join(project_path, "scenes", "scene.bam")
            # if not os.path.exists(scene_file):
            #     QMessageBox.warning(parent_window, "警告", "没有找到场景文件！")
            #     return False
            #
            # # 加载场景
            # if self.world.scene_manager.loadScene(scene_file):
            #     # 更新项目配置
            #     project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            #     project_config["scene_file"] = os.path.relpath(scene_file, project_path)
            #
            #     with open(config_file, "w", encoding="utf-8") as f:
            #         json.dump(project_config, f, ensure_ascii=False, indent=4)
            #
            #     # 更新项目状态
            #     self.current_project_path = project_path
            #     self.project_config = project_config
            #
            #     # 保存当前项目路径到主窗口
            #     parent_window.current_project_path = project_path
            #
            #     # 更新窗口标题
            #     project_name = os.path.basename(project_path)
            #     self.updateWindowTitle(parent_window, project_name)
            #
            #     # 更新文件浏览器
            #     if hasattr(parent_window, 'fileView') and hasattr(parent_window, 'fileModel'):
            #         parent_window.fileView.setRootIndex(parent_window.fileModel.index(project_path))
            #
            #     QMessageBox.information(parent_window, "成功", "项目加载成功！")
            #     return True
            # else:
            #     QMessageBox.warning(parent_window, "错误", "加载场景失败！")
            #     return False

        except Exception as e:
            UniversalMessageDialog.show_error(
                parent_window,
                "错误",
                f"加载项目时发生错误：{str(e)}",
                show_cancel=False,
                confirm_text="确认"
            )
            return False

    def openProjectForPath(self, project_path, parent_window=None):
        """通过路径打开项目

        Args:
            project_path: 项目路径
            parent_window: 父窗口对象（可选）
        """
        try:
            if not project_path:
                return False
            # 检查是否是有效的项目文件夹
            config_file = os.path.join(project_path, "project.json")
            if not os.path.exists(config_file):
                if parent_window:
                    UniversalMessageDialog.show_warning(
                        parent_window,
                        "警告",
                        f"选择的不是有效的项目文件夹！{project_path}",
                        show_cancel=False,
                        confirm_text="确认"
                    )
                else:
                    print("警告: 选择的不是有效的项目文件夹！")
                return False

            # 读取项目配置
            with open(config_file, "r", encoding="utf-8") as f:
                project_config = json.load(f)

            # 检查场景文件
            scene_file = os.path.join(project_path, "scenes", "scene.bam")
            if os.path.exists(scene_file):
                # 加载场景
                if self.world.scene_manager.loadScene(scene_file):
                    # 更新项目配置
                    project_config["scene_file"] = os.path.relpath(scene_file, project_path)

            project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
            with open(config_file, "w", encoding="utf-8") as f:
                json.dump(project_config, f, ensure_ascii=False, indent=4)

            # 更新项目状态
            self.current_project_path = project_path
            self.project_config = project_config

            # 如果有父窗口，更新相关UI元素
            if parent_window:
                # 保存当前项目路径到主窗口
                parent_window.current_project_path = project_path

                # 更新窗口标题
                project_name = os.path.basename(project_path)
                self.updateWindowTitle(parent_window, project_name)

                # 更新文件浏览器
                if hasattr(parent_window, 'fileView') and hasattr(parent_window, 'fileModel'):
                    parent_window.fileView.setRootIndex(parent_window.fileModel.index(project_path))

                UniversalMessageDialog.show_success(
                    parent_window,
                    "成功",
                    "项目加载成功！",
                    show_cancel=False,
                    confirm_text="确认"
                )

                print(f"项目 '{project_path}' 加载成功！")
                return True
            else:
                if parent_window:
                    UniversalMessageDialog.show_warning(
                        parent_window,
                        "错误",
                        "加载场景失败！",
                        show_cancel=False,
                        confirm_text="确认"
                    )
                else:
                    print("错误: 加载场景失败！")
                return False

        except Exception as e:
            error_msg = f"加载项目时发生错误：{str(e)}"
            if parent_window:
                UniversalMessageDialog.show_error(
                    parent_window,
                    "错误",
                    error_msg,
                    show_cancel=False,
                    confirm_text="确认"
                )
            else:
                print(error_msg)
            return False

    def saveProject(self, parent_window):
        """保存项目"""
        try:
            # 检查是否有当前项目路径
            if not self.current_project_path:
                # 尝试从主窗口获取
                if hasattr(parent_window, 'current_project_path'):
                    self.current_project_path = parent_window.current_project_path
                else:
                    UniversalMessageDialog.show_warning(
                        parent_window,
                        "警告",
                        "请先创建或打开一个项目！",
                        show_cancel=False,
                        confirm_text="确认"
                    )
                    return False
                    
            project_path = self.current_project_path
            scenes_path = os.path.join(project_path, "scenes")
            
            # 固定的场景文件名
            scene_file = os.path.join(scenes_path, "scene.bam")
            
            # 如果存在旧文件，先删除
            if os.path.exists(scene_file):
                try:
                    os.remove(scene_file)
                    print(f"已删除旧场景文件: {scene_file}")
                except Exception as e:
                    print(f"删除旧场景文件失败: {str(e)}")
                    return False
            
            # 保存场景
            if self.world.scene_manager.saveScene(scene_file, project_path):
                # 更新项目配置文件
                config_file = os.path.join(project_path, "project.json")
                if os.path.exists(config_file):
                    with open(config_file, "r", encoding="utf-8") as f:
                        project_config = json.load(f)
                    
                    # 更新最后修改时间
                    project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                    # 记录场景文件路径
                    project_config["scene_file"] = os.path.relpath(scene_file, project_path)
                    
                    with open(config_file, "w", encoding="utf-8") as f:
                        json.dump(project_config, f, ensure_ascii=False, indent=4)
                
                # 更新项目配置
                self.project_config = project_config
                
                # 更新窗口标题
                project_name = os.path.basename(project_path)
                self.updateWindowTitle(parent_window, project_name)
                
                UniversalMessageDialog.show_success(
                    parent_window,
                    "成功",
                    "项目保存成功！",
                    show_cancel=False,
                    confirm_text="确认"
                )
                return True
            else:
                UniversalMessageDialog.show_warning(
                    parent_window,
                    "错误",
                    "保存场景失败！",
                    show_cancel=False,
                    confirm_text="确认"
                )
                return False
                
        except Exception as e:
            UniversalMessageDialog.show_error(
                parent_window,
                "错误",
                f"保存项目时发生错误：{str(e)}",
                show_cancel=False,
                confirm_text="确认"
            )
            return False
    
    # ==================== 项目打包功能 ====================
    
    def buildPackage(self, parent_window):
        """打包项目为可执行文件 - 按照Panda3D官方标准方法"""
        try:
            # 检查是否有当前项目路径
            if not self.current_project_path:
                if hasattr(parent_window, 'current_project_path'):
                    self.current_project_path = parent_window.current_project_path
                else:
                    QMessageBox.warning(parent_window, "警告", "请先创建或打开一个项目！")
                    return False
                    
            project_path = self.current_project_path
            scenes_path = os.path.join(project_path, "scenes")
            scene_file = os.path.join(scenes_path, "scene.bam")
            
            # 检查场景文件是否存在
            if not os.path.exists(scene_file):
                QMessageBox.warning(parent_window, "警告", "请先保存场景！")
                return False

            if hasattr(self.world,'selection') and self.world.selection:
                self.world.selection.clearSelection()
                print("已取消场景中的物体选中状态")

            if self.world.scene_manager.saveScene(scene_file,project_path):
                print("场景保存成功")
                config_file = os.path.join(project_path,"project.json")
                if os.path.exists(config_file):
                    with open(config_file,"r",encoding="utf-8") as f:
                        project_config = json.load(f)

                        project_config["last_modified"] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        project_config["scene_file"] = os.path.relpath(scene_file,project_path)

                        with open(config_file,"w",encoding = "utf-8") as f:
                            json.dump(project_config,f,ensure_ascii = False,indent=4)

                self.project_config = project_config

                project_name = os.path.basename(project_path)
                self.updateWindowTitle(parent_window,project_name)
            else:
                QMessageBox.warning(parent_window,"错误","保存场景失败！")
                return False

            build_dir = QFileDialog.getExistingDirectory(
                parent_window,
                "选择打包输出目录",
                project_path,
                QFileDialog.ShowDirsOnly
            )

            if not build_dir:
                return False
                
            # 创建构建目录
            build_dir = os.path.join(build_dir, "build")

            if not os.path.exists(build_dir):
                os.makedirs(build_dir)
                
            # 创建标准的打包文件
            self._createStandardBuildFiles(build_dir, project_path, scene_file)
            
            # 执行打包命令
            success = self._executeStandardBuild(build_dir, parent_window)
            
            if success:
                # 根据操作系统创建对应的启动脚本文件
                try:
                    import stat
                    # 检查操作系统类型
                    if os.name == 'nt':  # Windows系统
                        run_bat_path = os.path.join(build_dir, "run.bat")
                        with open(run_bat_path, "w") as f:
                            f.write("@echo off\n")
                            f.write("python main.py %*\n")
                    else:  # Unix-like系统 (Linux, macOS等)
                        run_sh_path = os.path.join(build_dir, "run.sh")
                        with open(run_sh_path, "w") as f:
                            f.write("#!/bin/bash\n")
                            f.write("python3.10 main.py \"$@\"\n")
                        
                        # 为Unix-like系统添加执行权限
                        st = os.stat(run_sh_path)
                        os.chmod(run_sh_path, st.st_mode | stat.S_IEXEC)
                except Exception as e:
                    print(f"创建启动脚本文件失败: {str(e)}")
                
                QMessageBox.information(parent_window, "成功",
                                     "打包完成！\n可执行文件在 build 目录中。\n")
                return True
            else:
                return False

        except Exception as e:
            QMessageBox.critical(parent_window, "错误", f"打包过程出错：{str(e)}")
            return False
    
    def _createStandardBuildFiles(self, build_dir, project_path, scene_file):
        """创建标准的Panda3D打包文件"""
        project_name = os.path.basename(project_path)
        
        # 确保构建目录存在
        if not os.path.exists(build_dir):
            os.makedirs(build_dir)
        
        # 复制场景文件到构建目录
        shutil.copy2(scene_file, os.path.join(build_dir, "scene.bam"))

        # 复制Resources文件夹到build目录
        source_resources = os.path.join(project_path, "scenes", "resources")
        self.copy_folder(source_resources, build_dir)

        self._saveGUIElementsToJSON(build_dir, project_path)

        self._copyScriptsToBuild(build_dir, project_path)

        self._copyScriptSystemToBuild(build_dir)

        source_render_pipeline = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"RenderPipelineFile")
        dest_render_pipeline = os.path.join(build_dir,"RenderPipelineFile")

        if os.path.exists(source_render_pipeline):
            if os.path.exists(dest_render_pipeline):
                shutil.rmtree(dest_render_pipeline)

            shutil.copytree(
                source_render_pipeline,
                dest_render_pipeline,
                ignore=shutil.ignore_patterns('__pycache__','*.pyc','.git','.vscode','*.log','samples')
            )
            print("✓ RenderPipelineFile文件夹已复制到build目录")
        else:
            print("⚠️ RenderPipelineFile文件夹未找到")

        # 创建标准的应用程序入口文件
        self._createAppFile(build_dir, project_name)
        
        # 创建标准的setup.py文件
        self._createStandardSetupFile(build_dir, project_name)

        #创建requirements.txt文件
        self._createRequirementsFile(build_dir)

    def _copyScriptsToBuild(self, build_dir, project_path):
        """复制脚本文件到构建目录的scripts文件夹"""
        try:
            # 创建目标scripts目录
            scripts_dest = os.path.join(build_dir, "scripts")

            # 正确的源scripts目录路径
            scripts_src = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "scripts")

            # 如果上面的路径不存在，尝试项目路径下的scripts目录
            if not os.path.exists(scripts_src):
                scripts_src = os.path.join(project_path, "scripts")

            if os.path.exists(scripts_src):
                # 直接复制整个scripts目录
                if os.path.exists(scripts_dest):
                    shutil.rmtree(scripts_dest)

                shutil.copytree(
                    scripts_src,
                    scripts_dest,
                    ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
                )
                print("✓ Scripts目录已复制到build/scripts")
            else:
                # 创建空的scripts目录
                if not os.path.exists(scripts_dest):
                    os.makedirs(scripts_dest)
                print("⚠️ 项目中没有scripts目录")

        except Exception as e:
            print(f"⚠️ 复制脚本文件时出错: {str(e)}")

    def _copyScriptSystemToBuild(self,build_dir):
            core_files = [
                "script_system.py",
                "InfoPanelManager.py",
                "CustomMouseController.py"
            ]

            source_core_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),"core")

            core_dest = os.path.join(build_dir,"core")

            if not os.path.exists(core_dest):
                os.makedirs(core_dest)

            for file_name in core_files:
                source_file = os.path.join(source_core_dir,file_name)

                if os.path.exists(source_file):
                    shutil.copy2(source_file,os.path.join(core_dest,file_name))

    def _saveGUIElementsToJSON(self, build_dir, project_path):
        """保存GUI元素到JSON文件，内容与_collectGUIElementInfo保持一致"""
        try:
            # 创建目标gui目录
            gui_dest = os.path.join(build_dir, "gui")
            if not os.path.exists(gui_dest):
                os.makedirs(gui_dest)

            # 收集所有GUI元素信息
            gui_data = []

            # 获取当前场景中的GUI元素
            if hasattr(self.world, 'gui_elements'):
                for gui_node in self.world.gui_elements:
                    if gui_node and not gui_node.isEmpty():
                        # 使用_collectGUIElementInfo方法收集信息
                        gui_info = self.world.scene_manager._collectGUIElementInfo(gui_node)
                        if gui_info:
                            self._updateResourcePaths(gui_info,project_path,build_dir)
                            gui_data.append(gui_info)
                            print(f"收集GUI元素信息: {gui_info['name']}")

            # 保存GUI信息到JSON文件
            gui_file_path = os.path.join(gui_dest, "gui_elements.json")
            with open(gui_file_path, "w", encoding="utf-8") as f:
                json.dump(gui_data, f, ensure_ascii=False, indent=4)

            print(f"✓ GUI元素数据已保存到 {gui_file_path}，共 {len(gui_data)} 个元素")
            return True

        except Exception as e:
            print(f"⚠️ 保存GUI元素时出错: {str(e)}")
            import traceback
            traceback.print_exc()
            return False

    def _updateResourcePaths(self,gui_info,project_path,build_dir):
        def normalize_resource_path(old_path):
            if not old_path:
                return old_path

            if old_path.startswith(("http://","https://")):
                return old_path

            if os.path.isabs(old_path):
                try:
                    rel_path = os.path.relpath(old_path,project_path)
                    return os.path.join("resources",os.path.basename(rel_path)).replace("\\","/")
                except Exception:
                    return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
            else:
                return os.path.join("resources",os.path.basename(old_path)).replace("\\","/")
        if "image_path" in gui_info and gui_info["image_path"]:
            gui_info["image_path"] = normalize_resource_path(gui_info["image_path"])
        if "video_path" in gui_info and gui_info["video_path"]:
            gui_info["video_path"] = normalize_resource_path(gui_info["video_path"])
        if "bg_image_path" in gui_info and gui_info["bg_image_path"]:
            gui_info["bg_image_path"] = normalize_resource_path(gui_info["bg_image_path"])

    def _createRequirementsFile(self,build_dir):
        requirements_content = """panda3d>=1.10.13"""

        requirements_path = os.path.join(build_dir,"requirements.txt")
        with open(requirements_path,"w",encoding="utf-8") as f:
            f.write(requirements_content)

    def copy_folder(self, source_folder, destination_folder):
        """将一个文件夹从源路径复制到目标路径下的resources文件夹中

        Args:
            source_folder (str): 源文件夹路径
            destination_folder (str): 目标文件夹路径
        """
        try:
            # 创建resources文件夹作为目标
            resources_dest = os.path.join(destination_folder, "resources")

            # 确保目标目录存在
            if not os.path.exists(destination_folder):
                os.makedirs(destination_folder)

            # 如果目标resources文件夹已存在，先删除
            if os.path.exists(resources_dest):
                shutil.rmtree(resources_dest)

            # 检查源文件夹是否存在
            if os.path.exists(source_folder):
                # 复制整个文件夹到resources目录下
                shutil.copytree(
                    source_folder,
                    resources_dest,
                    ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
                )
                print(f"✓ 文件夹已从 {source_folder} 复制到 {resources_dest}")
                return True
            else:
                print(f"⚠️ 源文件夹不存在: {source_folder}")
                # 即使源文件夹不存在，也创建空的resources目录
                if not os.path.exists(resources_dest):
                    os.makedirs(resources_dest)
                return False

        except Exception as e:
            print(f"⚠️ 复制文件夹时出错: {str(e)}")
            return False

    def _copyResourcesToBuild(self, build_dir, project_path):
        """复制GUI资源到构建目录的resources文件夹"""
        try:
            # 创建目标resources目录
            resources_dest = os.path.join(build_dir, "resources")

            # 源Resources目录
            resources_src = os.path.join(project_path, "Resources")

            if os.path.exists(resources_src):
                # 直接复制整个Resources目录
                if os.path.exists(resources_dest):
                    shutil.rmtree(resources_dest)

                shutil.copytree(
                    resources_src,
                    resources_dest,
                    ignore=shutil.ignore_patterns('__pycache__', '*.pyc', '.git', '.vscode', '*.log')
                )
                print("✓ Resources目录已复制到build/resources")

                # 统计复制的文件数量
                file_count = 0
                for root, dirs, files in os.walk(resources_dest):
                    file_count += len(files)
                print(f"✓ 共复制了 {file_count} 个资源文件")
            else:
                # 创建空的resources目录
                if not os.path.exists(resources_dest):
                    os.makedirs(resources_dest)
                print("⚠️ 项目中没有Resources目录")

        except Exception as e:
            print(f"⚠️ 复制资源文件时出错: {str(e)}")

    def _collectResourceFiles(self, project_path):
        """收集项目中GUI使用的资源文件"""
        resource_files = set()

        try:
            # 收集Resources目录中的所有文件（这是最主要的资源来源）
            resources_dir = os.path.join(project_path, "Resources")
            if os.path.exists(resources_dir):
                for root, dirs, files in os.walk(resources_dir):
                    for file in files:
                        file_path = os.path.join(root, file)
                        # 收集所有文件，不仅仅是媒体文件
                        resource_files.add(file_path)

            # 同时收集场景中引用的特定资源
            scene_file = os.path.join(project_path, "scenes", "scene.bam")
            if os.path.exists(scene_file):
                # 从场景文件中提取资源引用
                referenced_files = self._extractResourcesFromScene(scene_file, project_path)
                for file_path in referenced_files:
                    if os.path.isabs(file_path):
                        if os.path.exists(file_path):
                            resource_files.add(file_path)
                    else:
                        # 相对路径
                        full_path = os.path.join(project_path, file_path)
                        if os.path.exists(full_path):
                            resource_files.add(full_path)

        except Exception as e:
            print(f"收集资源文件时出错: {str(e)}")

        return list(resource_files)

    def _extractResourcesFromScene(self, scene_file, project_path):
        """从场景文件中提取资源引用"""
        referenced_files = []

        try:
            # 这里应该实现从BAM文件中提取贴图、视频等资源引用的逻辑
            # 由于直接解析BAM文件比较复杂，我们采用间接方式

            # 检查项目配置文件或其他元数据文件中可能包含的资源引用
            config_file = os.path.join(project_path, "project.json")
            if os.path.exists(config_file):
                try:
                    with open(config_file, "r", encoding="utf-8") as f:
                        project_config = json.load(f)

                    # 如果配置中有资源列表信息，可以在这里处理
                    # 这里暂时保持简单实现
                except Exception as e:
                    print(f"读取项目配置时出错: {str(e)}")

        except Exception as e:
            print(f"从场景提取资源引用时出错: {str(e)}")

        return referenced_files

    def _isMediaFile(self, file_path):
        """判断是否为媒体文件（图片或视频）"""
        media_extensions = {
            # 图片格式
            '.png', '.jpg', '.jpeg', '.bmp', '.gif', '.tga', '.tiff',
            # 视频格式
            '.mp4', '.avi', '.mov', '.wmv', '.mkv', '.webm', '.flv'
        }

        _, ext = os.path.splitext(file_path.lower())
        return ext in media_extensions

    def _createAppFile(self, build_dir, project_name):
        """创建应用程序主文件 - 通过复制模板文件"""
        # 获取模板文件路径（假设模板文件在项目根目录下的templates文件夹中）
        template_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
                                     "templates", "main_template.py")

        # 目标文件路径
        app_path = os.path.join(build_dir, "main.py")

        # 检查模板文件是否存在
        if os.path.exists(template_path):
            # 直接复制模板文件
            shutil.copy2(template_path, app_path)
            print(f"✓ 应用程序主文件已从模板创建: {app_path}")

    def _createStandardSetupFile(self, build_dir, project_name):
        """创建优化的标准setup.py文件"""
        setup_code = f'''#!/usr/bin/env python3
    # -*- coding: utf-8 -*-

    """
    {project_name} 打包配置文件
    使用 Panda3D 标准打包工具
    """

    from setuptools import setup

    # 应用程序配置
    APP_NAME = "{project_name}"
    APP_VERSION = "1.0.0"
    MAIN_SCRIPT = "main.py"

    setup(
        name=APP_NAME,
        version=APP_VERSION,

        # Panda3D 打包选项
        options={{
            'build_apps': {{
                # GUI应用程序
                'gui_apps': {{
                    APP_NAME: MAIN_SCRIPT,
                }},

                # 包含的文件模式
                'include_patterns': [
                    '*.bam',           # 场景文件
                    '*.egg',           # 模型文件
                    '*.jpg', '*.png', '*.tga', '*.dds',  # 纹理文件
                    '*.wav', '*.ogg', '*.mp3',  # 音频文件
                    '*.ttf', '*.otf',  # 字体文件
                    'config/*.yaml',   # 配置文件
                    'shaders/**/*.glsl',  # 着色器文件
                ],

                # 排除的文件模式
                'exclude_patterns': [
                    '*.pyc',
                    '__pycache__/**',
                    '.git/**',
                    '.vscode/**',
                    '*.log',
                    '**/*.tmp',
                    '**/temp/**',
                    'samples/**',      # 示例文件
                    '**/.DS_Store',
                    '**/Thumbs.db',
                ],

                # Panda3D 插件
                'plugins': [
                    'pandagl',      # OpenGL渲染器
                    'pandaegl',     # EGL支持
                    'p3assimp',     # Assimp模型导入
                    'p3ffmpeg',     # FFmpeg视频支持
                    'p3openal_audio',  # OpenAL音频
                ],

                # 包含的Python模块
                'include_modules': {{
                    '*': [
                        # 核心模块
                        'direct.showbase.ShowBase',
                        'direct.task',
                        'direct.actor',
                        'direct.interval',
                        'direct.gui',
                        'direct.stdpy.file',
                        'direct.stdpy.pickle',
                        'panda3d.core',
                        'panda3d.direct',

                        # 渲染管线相关
                        'rpcore',
                        'rpcore.util.movement_controller',
                        'rpcore.native',
                        'rpcore.render_pipeline',
                        'rplibs',
                        'rpplugins',

                        # 标准库
                        'json',
                        'os',
                        'sys',
                        'math',
                        'random',
                        'collections',
                        'copy',
                        'itertools',
                        'importlib',
                    ],
                }},

                # 排除的Python模块（减小体积）
                'exclude_modules': {{
                    '*': [
                        # GUI框架
                        'tkinter',
                        'matplotlib',
                        'numpy',
                        'scipy',
                        'PIL',
                        'wx',
                        'PyQt5',
                        'PySide2',
                        'kivy',

                        # 开发工具
                        'setuptools',
                        'pip',
                        'wheel',
                        'twine',
                        'pytest',
                        'unittest',

                        # 其他不必要的模块
                        'sqlite3',
                        'xmlrpc',
                        'ftplib',
                        'telnetlib',
                    ],
                }},

                # 平台设置
                'platforms': [
                    'win_amd64',    # Windows 64位
                    'linux_x86_64', # Linux 64位
                ],

                # 优化设置
                'strip_docstrings': True,    # 移除文档字符串
                'optimize': 1,               # 字节码优化级别
            }},
        }},

        # 标准setuptools选项
        author="Panda3D Engine Editor User",
        author_email="user@example.com",
        description=f"{{APP_NAME}} - 使用Panda3D创建的3D应用程序",
        long_description="这是一个使用Panda3D引擎编辑器创建的3D应用程序。",

        # 依赖项
        install_requires=[
            'panda3d>=1.10.13',
        ],

        # Python版本要求
        python_requires='>=3.7',

        # 分类信息
        classifiers=[
            'Development Status :: 4 - Beta',
            'Intended Audience :: End Users/Desktop',
            'Programming Language :: Python :: 3',
            'Programming Language :: Python :: 3.7',
            'Programming Language :: Python :: 3.8',
            'Programming Language :: Python :: 3.9',
            'Programming Language :: Python :: 3.10',
            'Topic :: Games/Entertainment',
            'Topic :: Multimedia :: Graphics :: 3D Rendering',
        ],

        license='MIT',
    )
    '''

        setup_path = os.path.join(build_dir, "setup.py")
        with open(setup_path, "w", encoding="utf-8") as f:
            f.write(setup_code)

    def _executeStandardBuild(self, build_dir, parent_window):
        """执行标准的Panda3D打包命令 - 增强版"""
        try:
            print(f"开始打包，工作目录: {build_dir}")

            # 显示打包进度提示
            QMessageBox.information(parent_window, "打包进行中",
                                    "正在打包项目，请稍候...\n"
                                    "此过程可能需要几分钟时间。\n"
                                    "请勿关闭程序。")

            # 使用线程执行打包以避免界面冻结
            import threading
            from PyQt5.QtCore import pyqtSignal, QObject

            class BuildSignals(QObject):
                finished = pyqtSignal(bool, str)
                progress = pyqtSignal(str)

            signals = BuildSignals()

            def build_process():
                try:
                    print("执行标准打包命令: python setup.py bdist_apps")
                    signals.progress.emit("正在初始化打包过程...")

                    process = subprocess.Popen(
                        [sys.executable, "setup.py", "bdist_apps"],
                        cwd=build_dir,
                        stdout=subprocess.PIPE,
                        stderr=subprocess.PIPE,
                        universal_newlines=True,
                        encoding='utf-8'
                    )

                    while True:
                        output = process.stdout.readline()
                        if output == '' and process.poll() is not None:
                            break
                        if output:
                            print(output.strip())
                            if "copying" in output.lower():
                                signals.progress.emit(f"正在处理文件: {output.strip()[-50:]}")
                            elif "building" in output.lower():
                                signals.progress.emit(output.strip())

                    # 获取错误输出
                    stderr = process.stderr.read()
                    if stderr:
                        print("错误输出:", stderr)

                except Exception as e:
                    print(f"执行打包命令时出错: {str(e)}")
                    signals.finished.emit(False, f"打包失败：{str(e)}")

            # 连接信号
            def on_finished(success, message):
                if success:
                    QMessageBox.information(parent_window, "成功", message)
                else:
                    QMessageBox.critical(parent_window, "错误", message)

            def on_progress(message):
                print(f"[打包进度] {message}")

            signals.finished.connect(on_finished)
            signals.progress.connect(on_progress)

            # 启动打包线程
            build_thread = threading.Thread(target=build_process)
            build_thread.daemon = True
            build_thread.start()

            return True  # 线程已启动

        except Exception as e:
            print(f"启动打包过程时出错: {str(e)}")
            QMessageBox.critical(parent_window, "错误", f"启动打包失败：{str(e)}")
            return False

    def _tryBuildAppsThreaded(self, build_dir, signals):
        """尝试使用 build_apps 命令（线程安全版本）"""
        try:
            print("执行备用打包命令: python setup.py build_apps")
            signals.progress.emit("正在执行备用打包方法...")

            process = subprocess.Popen(
                [sys.executable, "setup.py", "build_apps"],
                cwd=build_dir,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                encoding='utf-8'
            )

            # 实时显示输出
            while True:
                output = process.stdout.readline()
                if output == '' and process.poll() is not None:
                    break
                if output:
                    print(output.strip())
                    signals.progress.emit(output.strip())

            stderr = process.stderr.read()
            if stderr:
                print("错误输出:", stderr)

            if process.returncode == 0:
                print("✓ build_apps 成功完成")
                signals.finished.emit(True, "打包成功完成！\n可执行文件在 dist/ 目录中。")
            else:
                error_msg = f"打包失败，返回码：{process.returncode}"
                if stderr:
                    error_msg += f"\n错误信息：{stderr}"
                signals.finished.emit(False, error_msg)

        except Exception as e:
            signals.finished.emit(False, f"执行 build_apps 失败：{str(e)}")
    
    def _tryBuildApps(self, build_dir, parent_window):
        """尝试使用 build_apps 命令"""
        try:
            print("执行备用打包命令: python setup.py build_apps")
            
            process = subprocess.Popen(
                [sys.executable, "setup.py", "build_apps"],
                cwd=build_dir,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                universal_newlines=True,
                encoding='utf-8'
            )
            
            # 实时显示输出
            while True:
                output = process.stdout.readline()
                if output == '' and process.poll() is not None:
                    break
                if output:
                    print(output.strip())
                    
            stderr = process.stderr.read()
            if stderr:
                print("错误输出:", stderr)
                
            if process.returncode == 0:
                print("✓ build_apps 成功完成")
                return True
            else:
                error_msg = f"打包失败，返回码：{process.returncode}"
                if stderr:
                    error_msg += f"\\n错误信息：{stderr}"
                QMessageBox.critical(parent_window, "错误", error_msg)
                return False
                
        except Exception as e:
            QMessageBox.critical(parent_window, "错误", f"执行 build_apps 失败：{str(e)}")
            return False
    
    # ==================== 工具方法 ====================
    
    def updateWindowTitle(self, window, project_name=None):
        """更新窗口标题"""
        base_title = "引擎编辑器"
        if project_name:
            window.setWindowTitle(f"{base_title} - {project_name}")
        else:
            window.setWindowTitle(base_title)
    
    def _clearCurrentScene(self):
        """清空当前场景"""
        # 移除所有模型
        for model in self.world.models:
            model.removeNode()
        self.world.models.clear()
        
        # 清空GUI元素
        for gui_element in self.world.gui_elements:
            gui_element.removeNode()
        self.world.gui_elements.clear()
        
        # 更新场景树
        if hasattr(self.world, 'updateSceneTree'):
            self.world.updateSceneTree()
    
    def getCurrentProjectPath(self):
        """获取当前项目路径"""
        return self.current_project_path
    
    def getCurrentProjectConfig(self):
        """获取当前项目配置"""
        return self.project_config
    
    def isProjectOpen(self):
        """检查是否有项目打开"""
        return self.current_project_path is not None
    
    def getProjectName(self):
        """获取当前项目名称"""
        if self.current_project_path:
            return os.path.basename(self.current_project_path)
        return None
    
    def getProjectInfo(self):
        """获取项目信息"""
        if not self.project_config:
            return None
            
        return {
            "name": self.project_config.get("name", "未知项目"),
            "path": self.project_config.get("path", ""),
            "created_at": self.project_config.get("created_at", ""),
            "last_modified": self.project_config.get("last_modified", ""),
            "version": self.project_config.get("version", "1.0.0"),
            "engine_version": self.project_config.get("engine_version", "1.0.0")
        } 

# ==================== 便利函数 ====================

def updateWindowTitle(window, project_name=None):
    """更新窗口标题 - 独立便利函数"""
    base_title = "引擎编辑器"
    if project_name:
        window.setWindowTitle(f"{base_title} - {project_name}")
    else:
        window.setWindowTitle(base_title) 
