#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
闲闲SDK混淆工具 - 现代化GUI界面
作者：AI Assistant
版本：2.0
左侧配置面板，右侧实时日志监控
"""

import os
import sys
import time
import threading
import subprocess
from pathlib import Path
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
from datetime import datetime
import json

# 添加当前目录到Python路径
current_dir = os.path.dirname(os.path.abspath(__file__))
if current_dir not in sys.path:
    sys.path.insert(0, current_dir)


class ModernObfuscatorGUI:
    def __init__(self, root):
        self.root = root
        self.root.title("闲闲SDK混淆工具 v2.0")
        self.root.geometry("1400x900")
        self.root.minsize(1200, 700)

        # 变量初始化
        self.init_variables()

        # 进程控制
        self.is_running = False
        self.current_process = None
        self.log_monitor_thread = None
        self.log_monitor_running = False

        # pbxproj相关变量
        self.pbxproj_path = tk.StringVar()
        self.sdk_folder_path = tk.StringVar()
        self.group_name = tk.StringVar(value="SDK")

        # 设置现代化样式
        self.setup_modern_style()

        # 创建界面
        self.setup_ui()

        # 设置日志监控
        self.setup_log_monitoring()

    def init_variables(self):
        """初始化所有变量"""
        # 基本配置
        self.input_path = tk.StringVar(
            value="/Users/apple/jumbo/xianxian/XXGPlayKit")
        self.sdk_region = tk.StringVar(value="2")  # 默认海外
        self.new_project_name = tk.StringVar()
        self.startid = tk.StringVar()
        self.sdk_options = tk.StringVar()
        self.functions = tk.StringVar(value="0")  # 默认全部操作

        # 日志相关
        self.log_file_path = tk.StringVar()
        self.log_type = tk.StringVar(value="File混淆日志.txt")
        self.status_var = tk.StringVar(value="就绪")

        # 日志过滤
        self.show_info = tk.BooleanVar(value=True)
        self.show_warnings = tk.BooleanVar(value=True)
        self.show_errors = tk.BooleanVar(value=True)
        self.auto_scroll = tk.BooleanVar(value=True)

    def setup_modern_style(self):
        """设置现代化样式"""
        self.style = ttk.Style()

        # 使用aqua主题
        try:
            self.style.theme_use('aqua')
        except:
            self.style.theme_use('clam')

        # 自定义样式
        self.style.configure("Title.TLabel", font=(
            "SF Pro Display", 18, "bold"))
        self.style.configure("Heading.TLabel", font=(
            "SF Pro Display", 14, "bold"))
        self.style.configure("Body.TLabel", font=("SF Pro Display", 11))
        self.style.configure("Accent.TButton", font=(
            "SF Pro Display", 11, "bold"))

    def setup_ui(self):
        """设置用户界面"""
        # 创建主容器
        main_container = ttk.Frame(self.root, padding="15")
        main_container.pack(fill=tk.BOTH, expand=True)

        # 创建顶部标题区域
        self.create_header(main_container)

        # 创建主要内容区域
        content_frame = ttk.Frame(main_container)
        content_frame.pack(fill=tk.BOTH, expand=True, pady=(15, 0))

        # 创建左右分割面板
        self.main_paned = ttk.PanedWindow(content_frame, orient=tk.HORIZONTAL)
        self.main_paned.pack(fill=tk.BOTH, expand=True)

        # 创建左右面板
        self.create_left_panel()
        self.create_right_panel()

        # 设置分割位置
        self.root.after(100, self.set_paned_position)

        # 创建底部状态栏
        self.create_status_bar(main_container)

    def create_header(self, parent):
        """创建顶部标题区域"""
        header_frame = ttk.Frame(parent)
        header_frame.pack(fill=tk.X, pady=(0, 15))

        # 主标题
        title_label = ttk.Label(header_frame, text="闲闲SDK混淆工具",
                                style="Title.TLabel")
        title_label.pack(side=tk.LEFT)

        # 版本信息
        version_label = ttk.Label(header_frame, text="v2.0",
                                  style="Body.TLabel", foreground="gray")
        version_label.pack(side=tk.LEFT, padx=(10, 0))

        # 右侧快捷操作
        quick_frame = ttk.Frame(header_frame)
        quick_frame.pack(side=tk.RIGHT)

        ttk.Button(quick_frame, text="保存配置", command=self.save_config).pack(
            side=tk.LEFT, padx=(0, 5))
        ttk.Button(quick_frame, text="加载配置",
                   command=self.load_config).pack(side=tk.LEFT)

    def create_left_panel(self):
        """创建左侧配置面板"""
        # 左侧容器
        left_container = ttk.Frame(self.main_paned, width=500, padding="10")

        # 使用Notebook创建选项卡
        self.config_notebook = ttk.Notebook(left_container)
        self.config_notebook.pack(fill=tk.BOTH, expand=True)

        # 基本配置选项卡
        self.create_basic_config_tab()

        # 高级配置选项卡
        self.create_advanced_config_tab()

        # pbxproj工具选项卡
        self.create_pbxproj_tab()

        # 添加到主面板
        self.main_paned.add(left_container, weight=2)

    def create_basic_config_tab(self):
        """创建基本配置选项卡"""
        basic_frame = ttk.Frame(self.config_notebook, padding="15")
        self.config_notebook.add(basic_frame, text="基本配置")

        # 工程配置组
        project_group = ttk.LabelFrame(basic_frame, text="工程配置", padding="15")
        project_group.pack(fill=tk.X, pady=(0, 15))

        # 工程路径
        ttk.Label(project_group, text="工程路径:",
                  style="Body.TLabel").pack(anchor=tk.W)
        path_frame = ttk.Frame(project_group)
        path_frame.pack(fill=tk.X, pady=(5, 15))

        self.path_entry = ttk.Entry(
            path_frame, textvariable=self.input_path, font=("SF Mono", 11))
        self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        ttk.Button(path_frame, text="浏览", command=lambda: self.safe_browse_project_path(
        )).pack(side=tk.RIGHT, padx=(8, 0))

        # 新工程名
        ttk.Label(project_group, text="新工程名:",
                  style="Body.TLabel").pack(anchor=tk.W)
        ttk.Entry(project_group, textvariable=self.new_project_name,
                  font=("SF Mono", 11)).pack(fill=tk.X, pady=(5, 15))

        # 初始化ID
        ttk.Label(project_group, text="初始化ID (可选):",
                  style="Body.TLabel").pack(anchor=tk.W)
        self.startid_entry = ttk.Entry(project_group, textvariable=self.startid,
                                       font=("SF Mono", 11))
        self.startid_entry.pack(fill=tk.X, pady=(5, 5))

        help_label = ttk.Label(project_group, text="32位字符串，留空则自动生成",
                               foreground="gray", font=("SF Pro Display", 9))
        help_label.pack(anchor=tk.W)

        # SDK配置组
        sdk_group = ttk.LabelFrame(basic_frame, text="SDK配置", padding="15")
        sdk_group.pack(fill=tk.X, pady=(0, 15))

        # SDK地区选择
        ttk.Label(sdk_group, text="SDK地区:",
                  style="Body.TLabel").pack(anchor=tk.W)
        region_frame = ttk.Frame(sdk_group)
        region_frame.pack(fill=tk.X, pady=(5, 15))

        ttk.Radiobutton(region_frame, text="🇨🇳 国内版本", variable=self.sdk_region,
                        value="1", command=self.on_region_change).pack(side=tk.LEFT)
        ttk.Radiobutton(region_frame, text="🌍 海外版本", variable=self.sdk_region,
                        value="2", command=self.on_region_change).pack(side=tk.LEFT, padx=(20, 0))

        # SDK选项显示
        self.sdk_options_frame = ttk.Frame(sdk_group)
        self.sdk_options_frame.pack(fill=tk.X)
        self.setup_sdk_options()

        # 操作按钮组
        action_group = ttk.LabelFrame(basic_frame, text="操作", padding="15")
        action_group.pack(fill=tk.X)

        button_frame = ttk.Frame(action_group)
        button_frame.pack(fill=tk.X)

        self.start_button = ttk.Button(button_frame, text="🚀 开始混淆",
                                       style="Accent.TButton", command=self.start_obfuscation)
        self.start_button.pack(side=tk.LEFT, padx=(0, 10))

        self.stop_button = ttk.Button(button_frame, text="⏹ 停止混淆",
                                      command=self.stop_obfuscation, state=tk.DISABLED)
        self.stop_button.pack(side=tk.LEFT)

    def create_advanced_config_tab(self):
        """创建高级配置选项卡"""
        advanced_frame = ttk.Frame(self.config_notebook, padding="15")
        self.config_notebook.add(advanced_frame, text="功能选择")

        # 快速选择
        quick_group = ttk.LabelFrame(advanced_frame, text="快速选择", padding="15")
        quick_group.pack(fill=tk.X, pady=(0, 15))

        quick_buttons = ttk.Frame(quick_group)
        quick_buttons.pack(fill=tk.X)

        ttk.Button(quick_buttons, text="全部操作",
                   command=lambda: self.functions.set("0")).pack(side=tk.LEFT, padx=(0, 8))
        ttk.Button(quick_buttons, text="半全部操作",
                   command=lambda: self.functions.set("t")).pack(side=tk.LEFT, padx=(0, 8))
        ttk.Button(quick_buttons, text="清除选择",
                   command=lambda: self.functions.set("")).pack(side=tk.LEFT)

        # 功能选择
        functions_group = ttk.LabelFrame(
            advanced_frame, text="混淆功能", padding="15")
        functions_group.pack(fill=tk.BOTH, expand=True)

        # 创建滚动区域
        canvas = tk.Canvas(functions_group, height=300)
        scrollbar = ttk.Scrollbar(
            functions_group, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        scrollable_frame.bind("<Configure>",
                              lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.configure(yscrollcommand=scrollbar.set)

        # 功能列表
        self.create_function_checkboxes(scrollable_frame)

        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # 绑定鼠标滚轮
        def _on_mousewheel(event):
            canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        canvas.bind("<MouseWheel>", _on_mousewheel)

    def create_function_checkboxes(self, parent):
        """创建功能选择复选框"""
        functions_data = [
            ("1", "🗑 删除注释", "移除代码中的所有注释"),
            ("2", "📝 删除打印", "移除NSLog和printf语句"),
            ("3", "📁 修改工程名", "重命名Xcode工程"),
            ("4", "📂 修改文件夹名", "重命名项目文件夹"),
            ("5", "📄 修改文件名", "重命名源代码文件"),
            ("6", "🔔 修改通知名", "重命名NSNotification"),
            ("7", "🏷 修改属性名和局部变量名", "重命名property和变量"),
            ("8", "📋 修改枚举", "重命名enum类型"),
            ("9", "🧩 修改block名", "重命名block变量"),
            ("a", "📊 修改常量名", "重命名常量定义"),
            ("b", "🤝 修改delegate名", "重命名代理方法"),
            ("c", "⚙️ 修改方法名", "重命名Objective-C方法"),
            ("d", "🖼 图片处理", "图片资源混淆"),
            ("e", "🆔 修改uuid", "重新生成项目UUID"),
            ("u", "🏗 修改类名", "重命名Objective-C类"),
            ("j", "🔐 资源加密等", "其他资源处理")
        ]

        self.function_vars = {}

        for i, (func_id, func_name, description) in enumerate(functions_data):
            row = i // 2
            col = i % 2

            var = tk.BooleanVar()
            self.function_vars[func_id] = var

            # 创建复选框框架
            cb_frame = ttk.Frame(parent)
            cb_frame.grid(row=row, column=col, sticky="w",
                          padx=(0, 20), pady=5)

            cb = ttk.Checkbutton(cb_frame, text=func_name, variable=var,
                                 command=self.update_functions)
            cb.pack(anchor="w")

            # 添加描述
            desc_label = ttk.Label(cb_frame, text=description,
                                   foreground="gray", font=("SF Pro Display", 9))
            desc_label.pack(anchor="w", padx=(20, 0))

        # 绑定函数字符串变化
        try:
            # Tk 9.0+ 新语法
            self.functions.trace_add('write', self.on_functions_change)
        except AttributeError:
            # Tk 8.x 旧语法
            self.functions.trace('w', self.on_functions_change)

    def create_pbxproj_tab(self):
        """创建pbxproj工具选项卡"""
        pbxproj_frame = ttk.Frame(self.config_notebook, padding="15")
        self.config_notebook.add(pbxproj_frame, text="项目工具")

        # 标题和说明
        title_frame = ttk.Frame(pbxproj_frame)
        title_frame.pack(fill=tk.X, pady=(0, 20))

        ttk.Label(title_frame, text="Xcode项目管理工具",
                  style="Heading.TLabel").pack(anchor=tk.W)

        desc_label = ttk.Label(title_frame,
                               text="添加SDK文件夹到项目或清理第三方文件",
                               foreground="gray", font=("SF Pro Display", 11))
        desc_label.pack(anchor=tk.W, pady=(5, 0))

        # 项目文件配置
        project_group = ttk.LabelFrame(
            pbxproj_frame, text="项目文件", padding="15")
        project_group.pack(fill=tk.X, pady=(0, 15))

        ttk.Label(project_group, text="project.pbxproj 文件路径:",
                  style="Body.TLabel").pack(anchor=tk.W)

        pbxproj_path_frame = ttk.Frame(project_group)
        pbxproj_path_frame.pack(fill=tk.X, pady=(5, 0))

        self.pbxproj_entry = ttk.Entry(pbxproj_path_frame, textvariable=self.pbxproj_path,
                                       font=("SF Mono", 11))
        self.pbxproj_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        browse_pbx_btn = ttk.Button(pbxproj_path_frame, text="浏览",
                                    command=lambda: self.safe_browse_pbxproj_file())
        browse_pbx_btn.pack(side=tk.RIGHT, padx=(8, 0))

        # SDK添加工具
        sdk_group = ttk.LabelFrame(
            pbxproj_frame, text="📦 SDK添加工具", padding="15")
        sdk_group.pack(fill=tk.X, pady=(0, 15))

        ttk.Label(sdk_group, text="将SDK文件夹添加到Xcode项目中",
                  foreground="gray", font=("SF Pro Display", 10)).pack(anchor=tk.W, pady=(0, 10))

        # SDK文件夹路径
        ttk.Label(sdk_group, text="SDK文件夹路径:",
                  style="Body.TLabel").pack(anchor=tk.W)

        sdk_path_frame = ttk.Frame(sdk_group)
        sdk_path_frame.pack(fill=tk.X, pady=(5, 15))

        self.sdk_folder_entry = ttk.Entry(sdk_path_frame, textvariable=self.sdk_folder_path,
                                          font=("SF Mono", 11))
        self.sdk_folder_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        browse_sdk_btn = ttk.Button(sdk_path_frame, text="浏览",
                                    command=lambda: self.safe_browse_sdk_folder())
        browse_sdk_btn.pack(side=tk.RIGHT, padx=(8, 0))

        # 组名配置
        group_frame = ttk.Frame(sdk_group)
        group_frame.pack(fill=tk.X, pady=(0, 15))

        ttk.Label(group_frame, text="组名:",
                  style="Body.TLabel").pack(side=tk.LEFT)
        ttk.Entry(group_frame, textvariable=self.group_name,
                  font=("SF Mono", 11), width=20).pack(side=tk.LEFT, padx=(10, 0))

        # SDK添加按钮
        ttk.Button(sdk_group, text="🚀 添加SDK到项目",
                   style="Accent.TButton",
                   command=self.run_add_sdk).pack(fill=tk.X)

        # 清理工具
        clean_group = ttk.LabelFrame(
            pbxproj_frame, text="🧹 项目清理工具", padding="15")
        clean_group.pack(fill=tk.X, pady=(0, 15))

        ttk.Label(clean_group, text="安全移除项目中所有第三方framework、library、bundle文件",
                  foreground="gray", font=("SF Pro Display", 10)).pack(anchor=tk.W, pady=(0, 15))

        ttk.Button(clean_group, text="⚠️ 清理第三方文件",
                   command=self.run_clean_project).pack(fill=tk.X)

        # 操作说明
        info_group = ttk.LabelFrame(
            pbxproj_frame, text="ℹ️ 操作说明", padding="15")
        info_group.pack(fill=tk.X)

        info_text = """
🔸 SDK添加：智能添加SDK文件夹到项目，支持framework、bundle、library等文件类型
🔸 项目清理：移除所有第三方文件引用，包括文件引用、构建文件、构建阶段引用
🔸 安全备份：所有操作都会自动创建备份文件，确保数据安全
🔸 智能处理：Framework作为整体添加，不会单独处理内部文件
        """

        info_label = ttk.Label(info_group, text=info_text.strip(),
                               font=("SF Pro Display", 9), foreground="gray")
        info_label.pack(anchor=tk.W)

    def create_right_panel(self):
        """创建右侧日志面板"""
        # 右侧容器
        right_container = ttk.Frame(self.main_paned, width=600, padding="10")

        # 日志控制区域
        log_control = ttk.Frame(right_container)
        log_control.pack(fill=tk.X, pady=(0, 15))

        # 标题
        ttk.Label(log_control, text="实时日志",
                  style="Heading.TLabel").pack(side=tk.LEFT)

        # 日志控制按钮
        control_frame = ttk.Frame(log_control)
        control_frame.pack(side=tk.RIGHT)

        ttk.Button(control_frame, text="清空", command=self.clear_log).pack(
            side=tk.LEFT, padx=(0, 5))
        ttk.Button(control_frame, text="智能检测",
                   command=self.auto_detect_log_file).pack(side=tk.LEFT)

        # 日志文件选择
        file_group = ttk.LabelFrame(right_container, text="日志文件", padding="10")
        file_group.pack(fill=tk.X, pady=(0, 15))

        # 日志类型选择
        type_frame = ttk.Frame(file_group)
        type_frame.pack(fill=tk.X, pady=(0, 10))

        ttk.Label(type_frame, text="日志类型:",
                  style="Body.TLabel").pack(side=tk.LEFT)

        log_types = [
            "File混淆日志.txt",
            "被忽略的关键词日志.txt",
            "文件夹混淆日志.txt",
            "文件名混淆日志.txt"
        ]

        type_combo = ttk.Combobox(type_frame, textvariable=self.log_type,
                                  values=log_types, state="readonly", width=25)
        type_combo.pack(side=tk.LEFT, padx=(10, 0))
        type_combo.bind('<<ComboboxSelected>>', self.on_log_type_change)

        # 文件路径
        path_frame = ttk.Frame(file_group)
        path_frame.pack(fill=tk.X)

        self.log_path_entry = ttk.Entry(path_frame, textvariable=self.log_file_path,
                                        font=("SF Mono", 10))
        self.log_path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)

        ttk.Button(path_frame, text="浏览", command=self.browse_log_file).pack(
            side=tk.RIGHT, padx=(8, 0))

        # 日志显示区域
        log_display = ttk.LabelFrame(
            right_container, text="日志内容", padding="10")
        log_display.pack(fill=tk.BOTH, expand=True)

        # 创建文本显示区域
        text_frame = ttk.Frame(log_display)
        text_frame.pack(fill=tk.BOTH, expand=True)

        self.log_text = tk.Text(text_frame, wrap=tk.WORD, font=("SF Mono", 10),
                                background="white", foreground="black")

        log_scrollbar = ttk.Scrollbar(text_frame, orient=tk.VERTICAL,
                                      command=self.log_text.yview)
        self.log_text.configure(yscrollcommand=log_scrollbar.set)

        self.log_text.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        log_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        # 配置日志颜色标签
        self.log_text.tag_configure("timestamp", foreground="blue")
        self.log_text.tag_configure("info", foreground="black")
        self.log_text.tag_configure("warning", foreground="orange")
        self.log_text.tag_configure("error", foreground="red")
        self.log_text.tag_configure("success", foreground="green")

        # 日志过滤选项
        filter_frame = ttk.Frame(log_display)
        filter_frame.pack(fill=tk.X, pady=(10, 0))

        ttk.Checkbutton(filter_frame, text="信息",
                        variable=self.show_info).pack(side=tk.LEFT)
        ttk.Checkbutton(filter_frame, text="警告", variable=self.show_warnings).pack(
            side=tk.LEFT, padx=(10, 0))
        ttk.Checkbutton(filter_frame, text="错误", variable=self.show_errors).pack(
            side=tk.LEFT, padx=(10, 0))
        ttk.Checkbutton(filter_frame, text="自动滚动",
                        variable=self.auto_scroll).pack(side=tk.RIGHT)

        # 添加到主面板
        self.main_paned.add(right_container, weight=3)

        # 显示欢迎信息
        self.show_welcome_message()

    def create_status_bar(self, parent):
        """创建底部状态栏"""
        status_frame = ttk.Frame(parent, relief=tk.SUNKEN, borderwidth=1)
        status_frame.pack(fill=tk.X, pady=(15, 0))

        # 状态信息
        status_left = ttk.Frame(status_frame)
        status_left.pack(side=tk.LEFT, padx=10, pady=5)

        ttk.Label(status_left, text="状态:",
                  style="Body.TLabel").pack(side=tk.LEFT)
        self.status_label = ttk.Label(status_left, textvariable=self.status_var,
                                      style="Body.TLabel", foreground="blue")
        self.status_label.pack(side=tk.LEFT, padx=(5, 0))

        # 进度条
        self.progress = ttk.Progressbar(
            status_frame, mode='indeterminate', length=200)
        self.progress.pack(side=tk.RIGHT, padx=10, pady=5)

    def set_paned_position(self):
        """设置分割位置"""
        try:
            self.main_paned.sashpos(0, 520)
        except Exception as e:
            self.root.after(100, self.set_paned_position)

    def show_welcome_message(self):
        """显示欢迎信息"""
        welcome_msg = """🎉 欢迎使用 闲闲SDK混淆工具 v2.0

🚀 现代化界面特性：
   • 使用最新 ttk 组件，完美支持 macOS
   • 选项卡式配置，操作更便捷
   • 实时日志监控，支持多种日志类型
   • 智能文件检测，自动定位日志文件

 📝 使用指南：
    1. 在"基本配置"选项卡中确认或修改工程路径（已设置默认值）
    2. 在"功能选择"选项卡中选择需要的混淆功能
    3. 点击"智能检测"自动设置日志文件路径
    4. 点击"开始混淆"启动处理过程
 
 💡 提示：工程路径已预设默认值，所有配置都可以保存和加载
"""
        self.log_text.insert("1.0", welcome_msg)

    def setup_sdk_options(self):
        """设置SDK选项"""
        # 清除现有选项
        for widget in self.sdk_options_frame.winfo_children():
            widget.destroy()

        region = self.sdk_region.get()

        if region == "1":  # 国内
            self.sdk_vars = {
                'ShanYanSDK': tk.BooleanVar(value=True),
                'BDASignalManager': tk.BooleanVar(value=True)
            }
            descriptions = {
                'ShanYanSDK': '🔐 一键登录',
                'BDASignalManager': '📊 巨量归因'
            }
        else:  # 海外
            self.sdk_vars = {
                'AppsFlyer': tk.BooleanVar(value=True),
                'Facebook': tk.BooleanVar(value=True),
                'Firebase': tk.BooleanVar(value=True),
                'Adjust': tk.BooleanVar(value=False),
                'VK': tk.BooleanVar(value=False),
                'AppLovin': tk.BooleanVar(value=False),
                'Poopo': tk.BooleanVar(value=False),
                'ADP': tk.BooleanVar(value=False)
            }
            descriptions = {
                'AppsFlyer': '📈 数据统计',
                'Facebook': '📱 社交登录',
                'Firebase': '🔥 分析服务',
                'Adjust': '📊 归因统计',
                'VK': '🇷🇺 VK登录',
                'AppLovin': '💰 广告平台',
                'Poopo': '🔌 渠道SDK',
                'ADP': 'IAA项目'
            }

        row = 0
        col = 0
        for sdk, var in self.sdk_vars.items():
            text = f"{descriptions.get(sdk, '')} {sdk}"
            cb = ttk.Checkbutton(self.sdk_options_frame, text=text, variable=var,
                                 command=self.update_sdk_options)
            cb.grid(row=row, column=col, sticky=tk.W, padx=(0, 20), pady=3)

            col += 1
            if col > 1:  # 每行2个
                col = 0
                row += 1

        self.update_sdk_options()

    def setup_log_monitoring(self):
        """设置日志监控"""
        try:
            # Tk 9.0+ 新语法
            self.log_file_path.trace_add('write', self.update_log_status)
        except AttributeError:
            # Tk 8.x 旧语法
            self.log_file_path.trace('w', self.update_log_status)

    def browse_project_path(self):
        """浏览工程路径"""
        try:
            path = filedialog.askdirectory(title="选择工程目录")
            if path:
                self.input_path.set(path)
                self.safe_log_message(f"✅ 设置工程路径: {path}", "info")
        except Exception as e:
            print(f"浏览工程路径错误: {e}")

    def browse_pbxproj_file(self):
        """浏览选择pbxproj文件"""
        try:
            filetypes = [
                ("所有文件", "*.*"),
                ("pbxproj文件", "*.pbxproj")
            ]
            path = filedialog.askopenfilename(
                title="选择project.pbxproj文件",
                filetypes=filetypes
            )
            if path:
                self.pbxproj_path.set(path)
                # 使用安全的日志方法
                self.safe_log_message(f"✅ 设置项目文件: {path}", "info")
        except Exception as e:
            print(f"浏览文件错误: {e}")

    def browse_sdk_folder(self):
        """浏览选择SDK文件夹"""
        try:
            path = filedialog.askdirectory(title="选择SDK文件夹")
            if path:
                self.sdk_folder_path.set(path)
                # 使用安全的日志方法
                self.safe_log_message(f"✅ 设置SDK文件夹: {path}", "info")
        except Exception as e:
            print(f"浏览文件夹错误: {e}")

    def safe_log_message(self, message, level="info"):
        """安全的日志消息方法，检查界面是否已初始化"""
        try:
            if hasattr(self, 'log_text') and self.log_text:
                self.log_message(message, level)
            else:
                # 如果界面还没初始化完成，只打印到控制台
                print(f"[{level.upper()}] {message}")
        except Exception as e:
            print(f"日志错误: {e}")
            print(f"[{level.upper()}] {message}")

    def safe_browse_pbxproj_file(self):
        """安全的浏览项目文件方法"""
        try:
            # 确保界面已经完全初始化
            if not hasattr(self, 'pbxproj_path') or not self.pbxproj_path:
                print("错误: pbxproj_path变量未初始化")
                return

            self.browse_pbxproj_file()
        except Exception as e:
            print(f"安全浏览项目文件错误: {e}")
            import traceback
            traceback.print_exc()

    def safe_browse_sdk_folder(self):
        """安全的浏览SDK文件夹方法"""
        try:
            # 确保界面已经完全初始化
            if not hasattr(self, 'sdk_folder_path') or not self.sdk_folder_path:
                print("错误: sdk_folder_path变量未初始化")
                return

            self.browse_sdk_folder()
        except Exception as e:
            print(f"安全浏览SDK文件夹错误: {e}")
            import traceback
            traceback.print_exc()

    def safe_browse_project_path(self):
        """安全的浏览工程路径方法"""
        try:
            # 确保界面已经完全初始化
            if not hasattr(self, 'input_path') or not self.input_path:
                print("错误: input_path变量未初始化")
                return

            self.browse_project_path()
        except Exception as e:
            print(f"安全浏览工程路径错误: {e}")
            import traceback
            traceback.print_exc()

    def run_add_sdk(self):
        """执行SDK添加操作"""
        # 验证参数
        pbxproj_path = self.pbxproj_path.get().strip()
        sdk_folder_path = self.sdk_folder_path.get().strip()
        group_name = self.group_name.get().strip()

        if not pbxproj_path:
            messagebox.showerror("参数错误", "请选择project.pbxproj文件！")
            return

        if not sdk_folder_path:
            messagebox.showerror("参数错误", "请选择SDK文件夹！")
            return

        if not os.path.exists(pbxproj_path):
            messagebox.showerror("参数错误", "项目文件不存在！")
            return

        if not os.path.exists(sdk_folder_path):
            messagebox.showerror("参数错误", "SDK文件夹不存在！")
            return

        if not os.path.isdir(sdk_folder_path):
            messagebox.showerror("参数错误", "SDK路径必须是文件夹！")
            return

        # 执行添加操作
        def run_in_thread():
            try:
                self.log_message("🚀 开始添加SDK到项目...", "info")
                self.log_message(f"📁 项目文件: {pbxproj_path}", "info")
                self.log_message(f"📦 SDK文件夹: {sdk_folder_path}", "info")
                self.log_message(f"📂 组名: {group_name}", "info")

                # 导入并执行
                from ObjectiveC.oc_custom.custom_pbxproj_add import SmartSDKAdder

                adder = SmartSDKAdder(pbxproj_path)
                success = adder.add_sdk_folder(sdk_folder_path, group_name)

                if success:
                    self.log_message("✅ SDK添加成功！", "success")
                    self.log_message("🎉 现在可以在Xcode中看到添加的SDK文件了！", "success")
                    self.log_message("📝 Framework作为整体添加，不会单独处理内部文件", "info")
                    messagebox.showinfo(
                        "操作成功", "SDK添加成功！\n现在可以在Xcode中看到添加的SDK文件了！")
                else:
                    self.log_message("❌ SDK添加失败！", "error")
                    messagebox.showerror("操作失败", "SDK添加失败！请查看日志了解详情。")

            except Exception as e:
                self.log_message(f"❌ 添加SDK时发生错误: {e}", "error")
                import traceback
                self.log_message(f"详细错误: {traceback.format_exc()}", "error")
                messagebox.showerror("操作失败", f"添加SDK时发生错误:\n{e}")

        # 在新线程中执行
        thread = threading.Thread(target=run_in_thread, daemon=True)
        thread.start()

    def run_clean_project(self):
        """执行项目清理操作"""
        # 验证参数
        pbxproj_path = self.pbxproj_path.get().strip()

        if not pbxproj_path:
            messagebox.showerror("参数错误", "请选择project.pbxproj文件！")
            return

        if not os.path.exists(pbxproj_path):
            messagebox.showerror("参数错误", "项目文件不存在！")
            return

        # 确认操作
        result = messagebox.askyesno(
            "确认清理",
            "⚠️ 此操作将移除项目中所有第三方framework、library、bundle文件及相关引用。\n\n"
            "操作会自动创建备份文件，但建议您在操作前手动备份项目。\n\n"
            "确定要继续吗？",
            icon='warning'
        )

        if not result:
            return

        # 执行清理操作
        def run_in_thread():
            try:
                self.log_message("🧹 开始清理第三方文件...", "info")
                self.log_message(f"📁 项目文件: {pbxproj_path}", "info")

                # 导入并执行
                from ObjectiveC.oc_custom.custom_pbxproj_clean import DemoProjectCleanerSafe

                cleaner = DemoProjectCleanerSafe(pbxproj_path)
                success = cleaner.clean_all_third_party_files()

                if success:
                    self.log_message("✅ 项目清理成功！", "success")
                    self.log_message("🎉 所有第三方文件已从项目中移除！", "success")
                    messagebox.showinfo("操作成功", "项目清理成功！\n所有第三方文件已从项目中移除！")
                else:
                    self.log_message("❌ 项目清理失败！", "error")
                    messagebox.showerror("操作失败", "项目清理失败！请查看日志了解详情。")

            except Exception as e:
                self.log_message(f"❌ 清理项目时发生错误: {e}", "error")
                import traceback
                self.log_message(f"详细错误: {traceback.format_exc()}", "error")
                messagebox.showerror("操作失败", f"清理项目时发生错误:\n{e}")

        # 在新线程中执行
        thread = threading.Thread(target=run_in_thread, daemon=True)
        thread.start()

    def browse_log_file(self):
        """浏览日志文件"""
        file_path = filedialog.askopenfilename(
            title="选择日志文件",
            filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
        )
        if file_path:
            self.log_file_path.set(file_path)

    def auto_detect_log_file(self):
        """智能检测日志文件"""
        self.log_message("🔍 正在智能检测日志文件...", "info")

        try:
            # 获取正确的基础路径
            import sys
            if hasattr(sys, '_MEIPASS'):
                # 打包环境，使用用户主目录
                base_path = os.path.join(os.path.expanduser("~"), "混淆完成后的文件")
                self.log_message(f"🔍 打包环境，检测路径: {base_path}", "info")
            else:
                # 开发环境，使用当前目录
                base_path = "混淆完成后的文件"
                self.log_message(f"🔍 开发环境，检测路径: {base_path}", "info")

            if not os.path.exists(base_path):
                self.log_message("❌ 未找到混淆输出目录", "error")
                return

            # 查找最新的混淆目录
            subdirs = [d for d in os.listdir(base_path)
                       if os.path.isdir(os.path.join(base_path, d)) and d.isdigit()]

            if not subdirs:
                self.log_message("❌ 未找到混淆结果目录", "error")
                return

            latest_dir = max(subdirs)
            log_file = os.path.join(base_path, latest_dir, self.log_type.get())

            if os.path.exists(log_file):
                self.log_file_path.set(log_file)
                self.log_message(f"✅ 找到日志文件: {log_file}", "success")
            else:
                self.log_message(f"❌ 日志文件不存在: {log_file}", "error")

        except Exception as e:
            self.log_message(f"❌ 检测失败: {str(e)}", "error")

    def on_log_type_change(self, event=None):
        """日志类型改变时的处理"""
        # 如果当前有路径，尝试更新到新的日志类型
        current_path = self.log_file_path.get()
        if current_path and os.path.dirname(current_path):
            new_path = os.path.join(os.path.dirname(
                current_path), self.log_type.get())
            if os.path.exists(new_path):
                self.log_file_path.set(new_path)
            else:
                # 尝试智能检测
                self.auto_detect_log_file()

    def update_log_status(self, *args):
        """更新日志状态"""
        # 这里可以添加日志文件状态检查逻辑
        pass

    def on_region_change(self):
        """SDK地区改变时的处理"""
        self.setup_sdk_options()

    def update_sdk_options(self):
        """更新SDK选项字符串"""
        if hasattr(self, 'sdk_vars'):
            selected = [sdk for sdk, var in self.sdk_vars.items() if var.get()]
            self.sdk_options.set(','.join(selected))

    def update_functions(self):
        """更新功能选择字符串"""
        if hasattr(self, 'function_vars'):
            selected = [func_id for func_id,
                        var in self.function_vars.items() if var.get()]
            self.functions.set(''.join(selected))

    def on_functions_change(self, *args):
        """功能字符串改变时更新复选框"""
        if hasattr(self, 'function_vars'):
            func_str = self.functions.get()

            # 处理特殊值
            if func_str == "0":  # 全部操作
                for var in self.function_vars.values():
                    var.set(True)
            elif func_str == "t":  # 半全部操作
                half_funcs = ["1", "2", "3", "4", "5", "7", "8", "c", "u"]
                for func_id, var in self.function_vars.items():
                    var.set(func_id in half_funcs)
            elif func_str == "":  # 清除
                for var in self.function_vars.values():
                    var.set(False)
            else:
                # 根据字符串设置
                for func_id, var in self.function_vars.items():
                    var.set(func_id in func_str)

    def start_obfuscation(self):
        """开始混淆"""
        if not self.validate_config():
            return

        self.is_running = True
        self.start_button.config(state=tk.DISABLED)
        self.stop_button.config(state=tk.NORMAL)
        self.progress.start()
        self.status_var.set("正在混淆...")

        # 在新线程中运行混淆
        thread = threading.Thread(target=self.run_obfuscation, daemon=True)
        thread.start()

        # 延迟检测日志文件
        self.root.after(3000, self.delayed_log_detection)

    def validate_config(self):
        """验证配置"""
        if not self.input_path.get():
            messagebox.showerror("配置错误", "请选择工程路径！")
            return False

        if not os.path.exists(self.input_path.get()):
            messagebox.showerror("配置错误", "工程路径不存在！")
            return False

        # if not self.new_project_name.get():
        #     messagebox.showerror("配置错误", "请输入新工程名！")
        #     return False

        return True

    def run_obfuscation(self):
        """运行混淆过程"""
        try:
            # 构建命令 - 在打包后的环境中直接调用混淆功能
            import sys
            import os

            # 检查是否在PyInstaller打包环境中
            if hasattr(sys, '_MEIPASS'):
                # 打包后环境：直接导入和调用混淆功能
                self.run_obfuscation_direct()
                return
            else:
                # 开发环境：使用oc_cli.py作为入口点
                script_path = "oc_cli.py"

                # 构建参数列表
                cmd = [sys.executable, script_path]

            # 添加输入路径
            input_path = self.input_path.get().strip()
            if input_path:  # 只有非空时才添加
                cmd.extend(["--input-path", input_path])

            # 添加SDK地区
            cmd.extend(["--sdk-region", self.sdk_region.get()])

            # 添加新工程名（如果提供）
            new_name = self.new_project_name.get().strip()
            if new_name:  # 只有非空时才添加，空则自动生成
                cmd.extend(["--new-project-name", new_name])

            # 添加startid（如果提供）
            startid = self.startid.get().strip()
            if startid:  # 只有非空时才添加
                cmd.extend(["--startid", startid])

            # 添加SDK选项
            sdk_options = self.sdk_options.get().strip()
            if sdk_options:
                cmd.extend(["--sdk-options", sdk_options])

            # 添加功能选择
            functions = self.functions.get().strip()
            if functions:
                cmd.extend(["--functions", functions])

            self.log_message(f"🚀 开始执行混淆命令", "info")
            self.log_message(f"📝 命令: {' '.join(cmd)}", "info")

            # 设置环境变量，确保Python路径正确
            env = os.environ.copy()
            current_dir = os.getcwd()
            if 'PYTHONPATH' in env:
                env['PYTHONPATH'] = current_dir + \
                    os.pathsep + env['PYTHONPATH']
            else:
                env['PYTHONPATH'] = current_dir

            # 执行命令
            self.current_process = subprocess.Popen(
                cmd,
                stdout=subprocess.PIPE,
                stderr=subprocess.STDOUT,
                text=True,
                bufsize=1,
                universal_newlines=True,
                cwd=current_dir,  # 设置工作目录
                env=env           # 设置环境变量
            )

            # 实时读取输出
            for line in iter(self.current_process.stdout.readline, ''):
                if not self.is_running:
                    break
                line = line.strip()
                if line:
                    self.log_message(line, "info")

            # 等待进程结束
            if self.is_running:
                self.current_process.wait()

                if self.current_process.returncode == 0:
                    self.log_message("🎉 混淆完成！", "success")
                    self.status_var.set("混淆完成")
                    # 尝试自动检测日志文件
                    self.root.after(1000, self.auto_detect_log_file)
                else:
                    self.log_message(
                        f"❌ 混淆失败，退出码: {self.current_process.returncode}", "error")
                    self.status_var.set("混淆失败")

        except Exception as e:
            self.log_message(f"❌ 执行错误: {str(e)}", "error")
            self.status_var.set("执行错误")

        finally:
            self.is_running = False
            self.root.after(0, self.reset_ui)

    def run_obfuscation_direct(self):
        """在打包环境中直接运行混淆功能"""
        try:
            # 导入所需模块
            sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

            # 构建参数
            args = []

            # 添加输入路径
            input_path = self.input_path.get().strip()
            if input_path:
                args.extend(["--input-path", input_path])

            # 添加SDK地区
            args.extend(["--sdk-region", self.sdk_region.get()])

            # 添加新工程名（如果提供）
            new_name = self.new_project_name.get().strip()
            if new_name:
                args.extend(["--new-project-name", new_name])

            # 添加startid（如果提供）
            startid = self.startid.get().strip()
            if startid:
                args.extend(["--startid", startid])

            # 添加SDK选项
            sdk_options = self.sdk_options.get().strip()
            if sdk_options:
                args.extend(["--sdk-options", sdk_options])

            # 添加功能选择
            functions = self.functions.get().strip()
            if functions:
                args.extend(["--functions", functions])

            self.log_message(f"🚀 开始执行混淆 (打包模式)", "info")
            self.log_message(f"📝 参数: {' '.join(args)}", "info")

            # 在新线程中运行混淆，避免阻塞GUI
            def run_in_thread():
                try:
                    # 导入并调用CLI模块
                    self.log_message("🔍 正在导入CLI模块...", "info")

                    # 首先测试基本导入
                    try:
                        import oc_cli
                        self.log_message("✅ oc_cli模块导入成功", "info")
                    except Exception as e:
                        self.log_message(f"❌ oc_cli模块导入失败: {str(e)}", "error")
                        import traceback
                        self.log_message(
                            f"导入错误详情: {traceback.format_exc()}", "error")
                        return

                    # 创建参数解析器并解析参数
                    import argparse
                    parser = argparse.ArgumentParser()
                    parser.add_argument(
                        '--input-path', '-i', default='/Users/apple/jumbo/xianxian/XXGPlayKit')
                    parser.add_argument(
                        '--sdk-region', '-r', choices=['1', '2'], default='2')
                    parser.add_argument('--new-project-name', '-n')
                    parser.add_argument('--startid', '-s')
                    parser.add_argument('--sdk-options', '-o')
                    parser.add_argument('--functions', '-f', default='0')

                    self.log_message("🔍 正在解析参数...", "info")
                    parsed_args = parser.parse_args(args)
                    self.log_message(f"✅ 参数解析成功: {vars(parsed_args)}", "info")

                    # 测试main_with_args函数是否存在
                    if not hasattr(oc_cli, 'main_with_args'):
                        self.log_message(
                            "❌ oc_cli模块中没有main_with_args函数", "error")
                        return

                    # 调用CLI的main函数
                    self.log_message("🔍 正在执行混淆...", "info")

                    # 定义日志回调函数
                    def cli_log_callback(message, level="info"):
                        self.log_message(message, level)

                    result = oc_cli.main_with_args(
                        parsed_args, log_callback=cli_log_callback)

                    if result:
                        self.log_message("🎉 混淆完成！", "success")
                        self.status_var.set("混淆完成")
                        # 尝试自动检测日志文件
                        self.root.after(1000, self.auto_detect_log_file)
                    else:
                        self.log_message("❌ 混淆失败", "error")
                        self.status_var.set("混淆失败")

                except Exception as e:
                    self.log_message(f"❌ 执行错误: {str(e)}", "error")
                    self.status_var.set("执行错误")
                    import traceback
                    self.log_message(
                        f"详细错误: {traceback.format_exc()}", "error")

                finally:
                    self.is_running = False
                    self.root.after(0, self.reset_ui)

            # 启动线程
            import threading
            thread = threading.Thread(target=run_in_thread, daemon=True)
            thread.start()

        except Exception as e:
            self.log_message(f"❌ 启动错误: {str(e)}", "error")
            self.status_var.set("启动错误")
            self.is_running = False
            self.root.after(0, self.reset_ui)

    def delayed_log_detection(self):
        """延迟检测日志文件"""
        if self.is_running:
            self.auto_detect_log_file()

    def stop_obfuscation(self):
        """停止混淆"""
        self.is_running = False
        if self.current_process:
            try:
                self.current_process.terminate()
                self.log_message("⏹ 混淆已停止", "warning")
            except:
                pass
        self.reset_ui()

    def reset_ui(self):
        """重置UI状态"""
        self.start_button.config(state=tk.NORMAL)
        self.stop_button.config(state=tk.DISABLED)
        self.progress.stop()
        if not self.is_running:
            self.status_var.set("就绪")

    def log_message(self, message, level="info"):
        """添加日志消息"""
        if not message.strip():
            return

        # 检查过滤器
        if level == "info" and not self.show_info.get():
            return
        if level == "warning" and not self.show_warnings.get():
            return
        if level == "error" and not self.show_errors.get():
            return

        timestamp = datetime.now().strftime("%H:%M:%S")

        def update_ui():
            self.log_text.config(state=tk.NORMAL)
            self.log_text.insert(tk.END, f"[{timestamp}] ", "timestamp")
            self.log_text.insert(tk.END, f"{message}\n", level)

            # 自动滚动
            if self.auto_scroll.get():
                self.log_text.see(tk.END)

            self.log_text.config(state=tk.DISABLED)

        self.root.after(0, update_ui)

    def clear_log(self):
        """清空日志"""
        self.log_text.config(state=tk.NORMAL)
        self.log_text.delete(1.0, tk.END)
        self.log_text.config(state=tk.DISABLED)
        self.show_welcome_message()

    def save_config(self):
        """保存配置"""
        config = {
            'input_path': self.input_path.get(),
            'sdk_region': self.sdk_region.get(),
            'new_project_name': self.new_project_name.get(),
            'startid': self.startid.get(),
            'sdk_options': self.sdk_options.get(),
            'functions': self.functions.get(),
            'log_file_path': self.log_file_path.get(),
            'log_type': self.log_type.get(),
            'pbxproj_path': self.pbxproj_path.get(),
            'sdk_folder_path': self.sdk_folder_path.get(),
            'group_name': self.group_name.get()
        }

        file_path = filedialog.asksaveasfilename(
            title="保存配置",
            defaultextension=".json",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )

        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    json.dump(config, f, ensure_ascii=False, indent=2)
                self.log_message(f"✅ 配置已保存: {file_path}", "success")
            except Exception as e:
                messagebox.showerror("错误", f"保存配置失败: {str(e)}")

    def load_config(self):
        """加载配置"""
        file_path = filedialog.askopenfilename(
            title="加载配置",
            filetypes=[("JSON文件", "*.json"), ("所有文件", "*.*")]
        )

        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)

                self.input_path.set(config.get('input_path', ''))
                self.sdk_region.set(config.get('sdk_region', '2'))
                self.new_project_name.set(config.get('new_project_name', ''))
                self.startid.set(config.get('startid', ''))
                self.sdk_options.set(config.get('sdk_options', ''))
                self.functions.set(config.get('functions', '0'))
                self.log_file_path.set(config.get('log_file_path', ''))
                self.log_type.set(config.get('log_type', 'File混淆日志.txt'))
                self.pbxproj_path.set(config.get('pbxproj_path', ''))
                self.sdk_folder_path.set(config.get('sdk_folder_path', ''))
                self.group_name.set(config.get('group_name', 'SDK'))

                # 更新SDK选项显示
                self.on_region_change()

                self.log_message(f"✅ 配置已加载: {file_path}", "success")
            except Exception as e:
                messagebox.showerror("错误", f"加载配置失败: {str(e)}")

    def on_closing(self):
        """关闭程序时的处理"""
        self.log_monitor_running = False
        if self.is_running:
            self.stop_obfuscation()
        self.root.destroy()


def main():
    """主函数"""
    root = tk.Tk()

    # 设置样式
    style = ttk.Style()
    try:
        style.theme_use('aqua')
    except:
        try:
            style.theme_use('clam')
        except:
            pass

    app = ModernObfuscatorGUI(root)

    # 绑定关闭事件
    root.protocol("WM_DELETE_WINDOW", app.on_closing)

    # 启动界面
    root.mainloop()


if __name__ == "__main__":
    main()
