import tkinter as tk
from tkinter import colorchooser, simpledialog
import win32com.client
import pythoncom
from comtypes import COMError
import json
import os

# --- 配置文件管理 ---
CONFIG_FILE = "ppt_timer_config.json"

class ConfigManager:
    def __init__(self):
        self.default_config = {
            "x": 200,
            "y": 200,
            "font_size": 48,
            "font_color": "#FF0000",
            "bg_color": "#000000",
            "alpha": 0.85,
            "duration_sec": 300
        }
        self.config = self.load()

    def load(self):
        if os.path.exists(CONFIG_FILE):
            try:
                with open(CONFIG_FILE, "r", encoding="utf-8") as f:
                    data = json.load(f)
                    # 合并配置,防止旧配置文件缺少新字段
                    return {**self.default_config, **data}
            except Exception as e:
                print(f"Config load error: {e}")
                pass
        return self.default_config.copy()

    def save(self):
        try:
            with open(CONFIG_FILE, "w", encoding="utf-8") as f:
                json.dump(self.config, f, indent=4)
        except Exception as e:
            print(f"Config save error: {e}")

# --- 主程序 ---
class PPTAutoTimer:
    def __init__(self, root):
        self.root = root
        self.root.title("PPT Timer")
        self.cfg = ConfigManager()

        # --- 状态变量 ---
        self.time_left = self.cfg.config["duration_sec"]
        self.is_running = False
        self.is_ppt_playing = False
        self.is_overtime = False
        self.overtime_seconds = 0
        
        # --- COM 对象缓存 ---
        self.ppt_app = None

        # --- UI 初始化 ---
        self.setup_window()
        self.create_widgets()
        
        # 初始调整窗口大小
        self.root.after(100, self.adjust_window_size)

        # --- 启动状态监测 (使用 after 代替 threading) ---
        self.check_ppt_status()

    def setup_window(self):
        self.root.overrideredirect(True)
        self.root.attributes('-topmost', True)
        self.root.attributes('-alpha', self.cfg.config["alpha"])
        self.root.configure(bg=self.cfg.config["bg_color"])

        # 尝试设置初始位置
        try:
            self.root.geometry(f"+{self.cfg.config['x']}+{self.cfg.config['y']}")
        except:
            pass

        self.root.bind("<ButtonPress-1>", self.start_move)
        self.root.bind("<B1-Motion>", self.do_move)
        self.root.bind("<Button-3>", self.show_menu)

    def create_widgets(self):
        self.label = tk.Label(
            self.root,
            text="00:00",
            font=("Microsoft YaHei", self.cfg.config["font_size"], "bold"),
            fg=self.cfg.config["font_color"],
            bg=self.cfg.config["bg_color"],
            bd=0,
            padx=0,
            pady=0
        )
        self.label.pack()

        self.menu = tk.Menu(self.root, tearoff=0)
        self.menu.add_command(label="设置时长 (分:秒)", command=self.set_duration)
        self.menu.add_command(label="设置字体大小", command=self.set_font_size)
        self.menu.add_command(label="设置字体颜色", command=self.pick_font_color)
        self.menu.add_command(label="设置背景颜色", command=self.pick_bg_color)
        self.menu.add_command(label="设置透明度", command=self.set_opacity)
        
        self.menu.add_separator()
        self.menu.add_command(label="退出", command=self.exit_app)

    def exit_app(self):
        # 保存当前窗口位置
        try:
            self.cfg.config['x'] = self.root.winfo_x()
            self.cfg.config['y'] = self.root.winfo_y()
            self.cfg.save()
        except:
            pass
        self.root.destroy()

    def set_opacity(self):
        current_percent = int(self.cfg.config["alpha"] * 100)
        val = simpledialog.askinteger("设置透明度", "请输入不透明度百分比 (10-100):", initialvalue=current_percent, minvalue=10, maxvalue=100, parent=self.root)
        if val is not None:
            alpha_value = val / 100.0
            self.cfg.config["alpha"] = alpha_value
            self.cfg.save()
            self.root.attributes('-alpha', alpha_value)

    # --- 核心逻辑:监测 PPT (优化版:单线程轮询) ---
    def check_ppt_status(self):
        is_playing_now = False
        
        try:
            # 1. 尝试获取或复用 PPT 对象
            if self.ppt_app is None:
                self.ppt_app = win32com.client.GetActiveObject("PowerPoint.Application")
            
            # 2. 检查放映窗口数量
            # 注意:这里需要确保 Count 属性存在,防止极罕见的竞态条件
            if self.ppt_app.SlideShowWindows and self.ppt_app.SlideShowWindows.Count > 0:
                is_playing_now = True
                
        except (COMError, pythoncom.com_error, AttributeError):
            # 如果 PPT 没开,或者 COM 连接断开,重置对象引用
            self.ppt_app = None
            is_playing_now = False
        except Exception:
            # 捕获其他未知错误,防止程序崩溃
            is_playing_now = False

        # --- 状态机逻辑 ---
        if is_playing_now and not self.is_ppt_playing:
            # 状态:从 "没播放" 变为 "正在播放" -> 启动计时
            self.is_ppt_playing = True
            self.start_timer()
        elif not is_playing_now and self.is_ppt_playing:
            # 状态:从 "正在播放" 变为 "没播放" -> 停止/重置
            self.is_ppt_playing = False
            self.reset_timer()

        # 无论状态如何,1秒后再次检查 (1000ms)
        self.root.after(1000, self.check_ppt_status)

    def start_timer(self):
        self.is_running = True
        self.is_overtime = False
        self.overtime_seconds = 0
        self.time_left = self.cfg.config["duration_sec"]
        self.update_display()
        self.run_timer_step()

    def run_timer_step(self):
        if self.is_running:
            # 只有当 PPT 还在播放时才继续计时
            if self.is_ppt_playing:
                if self.time_left > 0:
                    # --- 倒计时阶段 ---
                    self.time_left -= 1
                    self.is_overtime = False
                else:
                    # --- 超时阶段 ---
                    self.is_overtime = True
                    self.overtime_seconds += 1

                self.update_display()
                # 1秒后继续下一步
                self.root.after(1000, self.run_timer_step)
            else:
                # 如果计时器还在运行但 PPT 停止了(通过菜单强制停止等情况)
                pass

    def reset_timer(self):
        self.is_running = False
        self.overtime_seconds = 0
        # 这里的 update_display 是为了让界面立即变回 00:00 或初始状态
        # 如果希望 PPT 退出后保留最后的时间,可以去掉下面这行
        self.update_display()

    def update_display(self):
        if self.is_overtime:
            # --- 超时显示逻辑 ---
            mins, secs = divmod(self.overtime_seconds, 60)
            time_str = f"+{mins:02d}:{secs:02d}"
        else:
            # --- 倒计时显示逻辑 ---
            mins, secs = divmod(self.time_left, 60)
            time_str = f"{mins:02d}:{secs:02d}"

        self.label.config(text=time_str, fg=self.cfg.config["font_color"])
        self.adjust_window_size()

    def adjust_window_size(self):
        try:
            self.label.update_idletasks()
            text_width = self.label.winfo_reqwidth()
            text_height = self.label.winfo_reqheight()

            padding_x = 20
            padding_y = 10

            final_width = text_width + padding_x
            final_height = text_height + padding_y

            # 获取当前坐标以保持位置不变
            x = self.root.winfo_x()
            y = self.root.winfo_y()

            final_width = max(final_width, 50)
            final_height = max(final_height, 30)

            self.root.geometry(f"{final_width}x{final_height}+{x}+{y}")
        except:
            pass

    # --- 交互功能 ---
    def set_duration(self):
        input_str = simpledialog.askstring("设置时长", "请输入倒计时时长 (支持格式 MM:SS 或纯秒数):", initialvalue=str(self.cfg.config["duration_sec"]), parent=self.root)
        if input_str:
            try:
                input_str = input_str.replace(":", ":")
                if ":" in input_str:
                    m, s = map(int, input_str.split(":"))
                    total_seconds = m * 60 + s
                else:
                    total_seconds = int(input_str)

                if total_seconds > 0:
                    self.cfg.config["duration_sec"] = total_seconds
                    self.cfg.save()
                    # 如果当前没有在运行,立即更新显示
                    if not self.is_running:
                        self.time_left = total_seconds
                        self.is_overtime = False
                        self.overtime_seconds = 0
                        self.update_display()
            except ValueError:
                pass

    def set_font_size(self):
        val = simpledialog.askinteger("字体大小", "输入字体大小 (像素):", initialvalue=self.cfg.config["font_size"], minvalue=10, maxvalue=200, parent=self.root)
        if val:
            self.cfg.config["font_size"] = val
            self.label.config(font=("Microsoft YaHei", val, "bold"))
            self.cfg.save()
            self.adjust_window_size()

    def pick_font_color(self):
        color = colorchooser.askcolor(color=self.cfg.config["font_color"], parent=self.root)[1]
        if color:
            self.cfg.config["font_color"] = color
            self.label.config(fg=color)
            self.cfg.save()

    def pick_bg_color(self):
        color = colorchooser.askcolor(color=self.cfg.config["bg_color"], parent=self.root)[1]
        if color:
            self.cfg.config["bg_color"] = color
            self.root.configure(bg=color)
            self.label.config(bg=color)
            self.cfg.save()

    def show_menu(self, event):
        self.menu.tk_popup(event.x_root, event.y_root)

    def start_move(self, event):
        self.x = event.x
        self.y = event.y

    def do_move(self, event):
        deltax = event.x - self.x
        deltay = event.y - self.y
        x = self.root.winfo_x() + deltax
        y = self.root.winfo_y() + deltay
        self.root.geometry(f"+{x}+{y}")

if __name__ == "__main__":
    # 尝试处理 DPI 设置 (兼容 Win7/10/11)
    try:
        from ctypes import windll
        # SetProcessDpiAwareness(1) -> SYSTEM_DPI_AWARE
        windll.shcore.SetProcessDpiAwareness(1)
    except Exception:
        # Win7 可能没有 shcore 或者权限不足,忽略错误
        pass

    root = tk.Tk()
    app = PPTAutoTimer(root)
    root.mainloop()