#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""爱心弹窗 —— 抖音「Python 爱心弹窗」同款效果。

72 个小弹窗沿爱心曲线依次弹出、叠成一个爱心，停一秒后全部消失，
再疯狂弹出几百个「提示」窗口铺满屏幕，最后按 ESC / 空格 一键清场。

用法：
    python 爱着老吴.py                 # 完整效果（整蛊模式，会铺满屏幕）
    python 爱着老吴.py --safe          # 温和模式：只有爱心，不铺屏
    python 爱着老吴.py --count 60      # 自定爱心窗口数
    python 爱着老吴.py --no-flood      # 只拼爱心
    python 爱着老吴.py --list          # 只打印消息清单

退出：焦点在任意弹窗上时按 ESC 或空格，会销毁所有窗口并退出。
"""
from __future__ import annotations

import argparse
import math
import random
import sys
import time
import tkinter as tk

# ---- 文案与配色（和参考视频一致） ----------------------------------------
MESSAGES = ["老吴多喝水", "老吴好好爱自己", "老吴好好吃饭", "老吴保持好心情",
            "老吴我想你了", "老吴顺顺利利", "老吴别熬夜", "老吴天凉了多穿衣服",
            "老吴谢谢你，把冬天过得像春天", "老吴有你真好，谢谢", "老吴你让我很安心", "老吴那颗糖甜到心里", "老吴你信我这一次", "老吴有你在我踏实"]

COLORS = ["pink", "lightblue", "lightgreen",
          "lemonchiffon", "hotpink", "skyblue"]

WIN_W, WIN_H = 210, 84
HEART_FONT = ("微软雅黑", 20)
TITLE = "提示"


def heart_points(n: int, w: int, h: int):
    """爱心参数方程：x=16sin³θ, y=13cosθ-5cos2θ-2cos3θ-cos4θ。"""
    pts = []
    for i in range(n):
        th = i / n * 2 * math.pi
        x = 16 * math.sin(th) ** 3
        y = (13 * math.cos(th) - 5 * math.cos(2 * th)
             - 2 * math.cos(3 * th) - math.cos(4 * th))
        sx = int(w / 2 + x * 20 - 50)
        sy = int(h / 2 - y * 20 - 80)
        pts.append((max(0, min(sx, w - WIN_W)), max(0, min(sy, h - WIN_H))))
    return pts


class Popup:
    """一个「提示」小窗口。"""

    def __init__(self, root: tk.Tk, x: int, y: int, text: str,
                 bg: str, topmost: bool = True, registry=None):
        self.win = tk.Toplevel(root)
        self.win.geometry(f"{WIN_W}x{WIN_H}+{x}+{y}")
        self.win.title(TITLE)
        self.win.resizable(False, False)
        if topmost:
            self.win.attributes("-topmost", 1)
        self.win.protocol("WM_DELETE_WINDOW", self.close)

        label = tk.Label(self.win, text=text, bg=bg, fg="#333333",
                         font=HEART_FONT, wraplength=WIN_W - 20)
        label.pack(fill="both", expand=True)

        for seq in ("<space>", "<Escape>"):
            self.win.bind(seq, lambda _e: (registry or self)._close_all())
        label.bind("<space>", lambda _e: (registry or self)._close_all())
        label.bind("<Button-1>", lambda _e: self.win.focus_set())

    def close(self):
        try:
            self.win.destroy()
        except tk.TclError:
            pass

    def alive(self) -> bool:
        try:
            return bool(self.win.winfo_exists())
        except tk.TclError:
            return False


class Show:
    def __init__(self, root: tk.Tk, topmost=True):
        self.root = root
        self.windows: list[Popup] = []
        self.topmost = topmost

    def _close_all(self):
        for w in self.windows:
            w.close()
        self.windows.clear()
        try:
            self.root.quit()
            self.root.destroy()
        except tk.TclError:
            pass
        sys.exit(0)

    def open(self, x, y, text=None, bg=None) -> Popup:
        text = text or random.choice(MESSAGES)
        bg = bg or random.choice(COLORS)
        p = Popup(self.root, x, y, text, bg, self.topmost, registry=self)
        self.windows.append(p)
        self.root.update()
        return p

    def clear(self):
        for w in self.windows:
            w.close()
        self.windows.clear()
        self.root.update()


def run(count=72, flood=True, topmost=True, pace=0.06, flood_pace=0.04,
        hold=1.0, final_hold=10.0):
    root = tk.Tk()
    root.withdraw()
    sw, sh = root.winfo_screenwidth(), root.winfo_screenheight()
    show = Show(root, topmost=topmost)

    # 1) 沿爱心曲线一个个弹出
    for i, (x, y) in enumerate(heart_points(count, sw, sh)):
        show.open(x, y)
        time.sleep(pace)

    # 2) 停一下，全部消失
    time.sleep(hold)
    show.clear()

    if not flood:
        root.after(10, lambda: None)
        root.mainloop()
        return

    # 3) 满屏乱弹
    total = (sw // WIN_W) * (sh // (WIN_H - 20)) + 50
    for _ in range(total):
        show.open(random.randint(0, sw - WIN_W), random.randint(0, sh - WIN_H))
        time.sleep(flood_pace)

    # 4) 定住，等用户按 ESC / 空格
    root.after(int(final_hold * 1000), lambda: None)
    root.mainloop()


def main(argv=None):
    ap = argparse.ArgumentParser(description="爱心弹窗")
    ap.add_argument("--count", type=int, default=72, help="爱心窗口数（默认 72）")
    ap.add_argument("--safe", action="store_true", help="温和模式：不铺满屏幕")
    ap.add_argument("--no-flood", action="store_true", help="只拼爱心，不铺屏")
    ap.add_argument("--no-topmost", action="store_true", help="窗口不置顶")
    ap.add_argument("--list", action="store_true", help="打印文案后退出")
    args = ap.parse_args(argv)

    if args.list:
        print("\n".join(MESSAGES))
        return 0

    flood = not (args.safe or args.no_flood)
    run(count=args.count, flood=flood, topmost=not args.no_topmost)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
