"""
爱心窗口动画（Tkinter 版）

功能概述：
- 创建多个小型独立窗口，背景为粉红/红色渐变，中文短语居中显示。
- 按序淡入显示，每个窗口延迟0.3-0.5秒出现。
- 全部显示后，窗口向中心移动，排列成心形。
- 心形状态保持5秒后同时淡出消失，程序安全退出。

技术说明：
- 使用 Tkinter 的 Toplevel 作为独立窗口，Canvas 绘制渐变背景与文本。
- 使用窗口透明度（-alpha）实现淡入淡出。
- 使用 after 事件进行非阻塞动画与调度。
"""

import random
import math
import sys
import tkinter as tk
from tkinter import font as tkfont
import colorsys


# ------------- 配置项 -------------
WINDOW_COUNT = 18  # 建议 15-20 个
WIN_W, WIN_H = 240, 140  # 每个小窗口尺寸
FADE_STEP = 0.06  # 淡入/淡出透明度步进
FADE_INTERVAL_MS = 30  # 淡入/淡出每步间隔
STAGGER_MIN_MS = 300  # 顺序淡入最小延迟
STAGGER_MAX_MS = 500  # 顺序淡入最大延迟
MOVE_DURATION_MS = 2000  # 移动到心形的总时长
MOVE_INTERVAL_MS = 16  # 移动每步间隔（约 60fps）
HOLD_MS = 5000  # 心形保持时长


# ------------- 文案库（中文）-------------
PHRASES = [
    # 关心类
    "你吃饭了么", "记得多喝水", "早点休息",
    "今天也要开心", "注意保暖", "出门记得带伞",
    "工作别太累", "记得伸展运动", "多吃蔬果",
    "路上注意安全", "到家给我报个平安", "记得吃早餐",
    "睡前别玩太晚", "少熬夜多休息", "心情不好就找我",
    # 思念类
    "对你的思恋永不忘", "想你了", "见不到你的日子好漫长",
    "一想到你就笑", "你在的方向是我的远方", "漫长夜晚都在想你",
    "每一秒都在期待", "想念像潮水涌来", "你的名字我每天都在默念",
    "我在等你消息", "想把你写进每一个梦", "心里住着你",
    "好想快点见到你", "离你近一点再近一点",
    # 表白类
    "你眼里有星星", "心跳为你加速", "你是我的小幸运",
    "喜欢你是我的秘密也是公开", "你是我心里的光", "我的世界因你而亮",
    "妙不可言皆因你", "遇见你是最美的意外", "你比星星更闪耀",
    "我想和你一起看日出", "想牵着你的手到永远", "你是我不变的答案",
    "我就是很喜欢你", "余生请多指教", "我的心向你跑",
]


# ------------- 渐变颜色组（红/粉）-------------
GRADIENT_PALETTES = [
    ("#ff4d6d", "#ff8fab"),  # 红到粉
    ("#ff6b6b", "#ffc2c2"),  # 鲜红到浅粉
    ("#e03131", "#ffa3a3"),  # 暗红到淡粉
]


def hsv_to_hex(h: float, s: float, v: float) -> str:
    """HSV 转 Hex（0..1 范围）。"""
    r, g, b = colorsys.hsv_to_rgb(h, s, v)
    return '#%02x%02x%02x' % (int(r * 255), int(g * 255), int(b * 255))


def random_red_pink_gradient() -> tuple:
    """生成更丰富的红/粉主题渐变颜色对，每个窗口各不相同。"""
    # 红色在 HSV 的 hue 约落在 0 或 1 附近，这里做两段采样以增强多样性
    if random.random() < 0.5:
        base_h = random.uniform(0.0, 0.06)   # 0°~22° 左右（红/橙偏红）
    else:
        base_h = random.uniform(0.94, 1.0)   # 338°~360°（品红偏红）

    # 略微偏移，形成更自然的渐变
    h2 = (base_h + random.uniform(-0.03, 0.03)) % 1.0

    # 饱和度与明度控制：先偏鲜艳，再向浅粉或柔和过渡
    s1 = random.uniform(0.6, 0.95)
    v1 = random.uniform(0.85, 1.0)
    s2 = max(0.0, s1 - random.uniform(0.15, 0.35))
    v2 = min(1.0, v1 + random.uniform(0.0, 0.1))

    c1 = hsv_to_hex(base_h, s1, v1)
    c2 = hsv_to_hex(h2, s2, v2)
    return (c1, c2)


def hex_to_rgb(hex_color: str):
    """将 #rrggbb 转换为 (r, g, b)。"""
    hex_color = hex_color.lstrip('#')
    return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))


def rgb_to_hex(rgb):
    """将 (r, g, b) 转换为 #rrggbb。"""
    return '#%02x%02x%02x' % rgb


def lerp_color(c1: str, c2: str, t: float) -> str:
    """在两颜色之间按比例 t（0..1）插值。"""
    r1, g1, b1 = hex_to_rgb(c1)
    r2, g2, b2 = hex_to_rgb(c2)
    r = int(r1 + (r2 - r1) * t)
    g = int(g1 + (g2 - g1) * t)
    b = int(b1 + (b2 - b1) * t)
    return rgb_to_hex((r, g, b))


def pick_font(root: tk.Tk) -> tuple:
    """选择一个可用的中文友好字体。"""
    families = set(tkfont.families(root))
    for cand in ["PingFang SC", "Heiti SC", "Songti SC", "Microsoft YaHei", "Arial Unicode MS", "Arial"]:
        if cand in families:
            return (cand, 18, "bold")
    return ("Arial", 18, "bold")


class HeartWindow:
    """单个爱心窗口，负责自身的绘制与动画。"""

    def __init__(self, root: tk.Tk, text: str, font_spec: tuple, colors: tuple):
        self.root = root
        self.text = text
        self.font_spec = font_spec
        self.top = tk.Toplevel(root)
        self.top.title("❤")
        self.top.resizable(False, False)
        self.top.protocol("WM_DELETE_WINDOW", self._on_user_close)
        # 初始全透明以配合淡入
        self.top.attributes("-alpha", 0.0)

        self.canvas = tk.Canvas(self.top, width=WIN_W, height=WIN_H, highlightthickness=0)
        self.canvas.pack(fill="both", expand=True)

        self._draw_gradient_background(colors)
        # 居中文本
        self.canvas.create_text(
            WIN_W // 2,
            WIN_H // 2,
            text=self.text,
            fill="white",
            font=self.font_spec,
            anchor="center",
        )

        # 动画状态
        self._fade_target_alpha = 1.0
        self._moving = False
        self._on_fade_in_done = None
        self._on_move_done = None

    def _draw_gradient_background(self, colors: tuple):
        """绘制从上到下的渐变背景。"""
        c1, c2 = colors
        steps = WIN_H
        for i in range(steps):
            t = i / max(steps - 1, 1)
            col = lerp_color(c1, c2, t)
            self.canvas.create_line(0, i, WIN_W, i, fill=col)

    # --- 几何与位置 ---
    def place(self, x: int, y: int):
        self.top.geometry(f"{WIN_W}x{WIN_H}+{int(x)}+{int(y)}")

    def get_pos(self):
        geo = self.top.geometry()  # e.g. '240x140+123+456'
        try:
            parts = geo.split('+')
            coords = parts[1:]
            return int(coords[0]), int(coords[1])
        except Exception:
            return 0, 0

    # --- 淡入/淡出 ---
    def fade_in(self, on_done=None):
        self._on_fade_in_done = on_done
        self._fade_target_alpha = 1.0
        self._step_fade(to_alpha=1.0)

    def fade_out(self, on_done=None):
        self._on_fade_in_done = on_done
        self._fade_target_alpha = 0.0
        self._step_fade(to_alpha=0.0)

    def _step_fade(self, to_alpha: float):
        cur = float(self.top.attributes("-alpha") or 0.0)
        if to_alpha > cur:
            cur = min(cur + FADE_STEP, to_alpha)
        else:
            cur = max(cur - FADE_STEP, to_alpha)
        self.top.attributes("-alpha", cur)
        if abs(cur - to_alpha) < 1e-3:
            if self._on_fade_in_done:
                cb = self._on_fade_in_done
                self._on_fade_in_done = None
                self.root.after(0, cb)
            return
        self.root.after(FADE_INTERVAL_MS, lambda: self._step_fade(to_alpha))

    # --- 平滑移动 ---
    def move_to(self, target_x: int, target_y: int, duration_ms: int = MOVE_DURATION_MS, on_done=None):
        self._on_move_done = on_done
        self._moving = True
        start_x, start_y = self.get_pos()
        steps = max(int(duration_ms / MOVE_INTERVAL_MS), 1)
        dx = (target_x - start_x) / steps
        dy = (target_y - start_y) / steps

        def step(i=0):
            if i >= steps:
                self.place(target_x, target_y)
                self._moving = False
                if self._on_move_done:
                    cb = self._on_move_done
                    self._on_move_done = None
                    self.root.after(0, cb)
                return
            nx = start_x + dx * i
            ny = start_y + dy * i
            self.place(nx, ny)
            self.root.after(MOVE_INTERVAL_MS, lambda: step(i + 1))

        step(0)

    def destroy(self):
        try:
            self.top.destroy()
        except Exception:
            pass

    def _on_user_close(self):
        # 用户关闭任意窗口时，退出全局
        try:
            self.root.quit()
        except Exception:
            pass


def heart_points(n: int, center_x: int, center_y: int, scale: float) -> list:
    """根据经典心形方程生成 n 个点，返回屏幕坐标列表。"""
    pts = []
    # 经典心形：
    # x = 16 sin^3(t)
    # y = 13 cos(t) - 5 cos(2t) - 2 cos(3t) - cos(4t)
    # 注意屏幕 y 轴向下，因此 y 取负号后再平移
    for i in range(n):
        t = (2 * math.pi) * (i / n)
        x = 16 * (math.sin(t) ** 3)
        y = 13 * math.cos(t) - 5 * math.cos(2 * t) - 2 * math.cos(3 * t) - math.cos(4 * t)
        sx = center_x + scale * x - WIN_W // 2
        sy = center_y - scale * y - WIN_H // 2
        pts.append((int(sx), int(sy)))
    return pts


def main():
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口，仅使用 Toplevel
    root.title("Love Windows ❤")
    root.protocol("WM_DELETE_WINDOW", root.quit)

    screen_w = root.winfo_screenwidth()
    screen_h = root.winfo_screenheight()

    # 心形缩放：适配不同屏幕，留边距
    scale = min(screen_w, screen_h) / 35.0  # 经验值：越大心形越大
    center_x = screen_w // 2
    center_y = screen_h // 2

    font_spec = pick_font(root)

    # 创建窗口集合，初始化到随机位置
    windows: list[HeartWindow] = []
    for _ in range(WINDOW_COUNT):
        text = random.choice(PHRASES)
        # 使用随机红/粉主题渐变，让每张小卡片颜色更丰富
        colors = random_red_pink_gradient()
        w = HeartWindow(root, text, font_spec, colors)
        rx = random.randint(0, max(screen_w - WIN_W, 0))
        ry = random.randint(0, max(screen_h - WIN_H, 0))
        w.place(rx, ry)
        windows.append(w)

    # 顺序淡入调度
    visible_count = {"cnt": 0}

    def on_one_visible():
        visible_count["cnt"] += 1
        if visible_count["cnt"] >= len(windows):
            # 全部显示后开始向心形移动
            start_move_to_heart()

    delay_acc = 0
    for w in windows:
        stagger = random.randint(STAGGER_MIN_MS, STAGGER_MAX_MS)
        delay_acc += stagger
        root.after(delay_acc, lambda win=w: win.fade_in(on_done=on_one_visible))

    # 移动到心形
    arrived_count = {"cnt": 0}

    def on_one_arrived():
        arrived_count["cnt"] += 1
        if arrived_count["cnt"] >= len(windows):
            # 全部到位，保持后淡出
            root.after(HOLD_MS, start_fade_out_all)

    def start_move_to_heart():
        targets = heart_points(len(windows), center_x, center_y, scale)
        for i, w in enumerate(windows):
            tx, ty = targets[i]
            w.move_to(tx, ty, duration_ms=MOVE_DURATION_MS, on_done=on_one_arrived)

    # 同时淡出并退出
    faded_count = {"cnt": 0}

    def on_one_faded():
        faded_count["cnt"] += 1
        if faded_count["cnt"] >= len(windows):
            # 全部淡出后销毁并退出
            for w in windows:
                w.destroy()
            try:
                root.quit()
            except Exception:
                pass

    def start_fade_out_all():
        for w in windows:
            w.fade_out(on_done=on_one_faded)

    # 进入主循环
    try:
        root.mainloop()
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    # 确保中文输出正常
    if sys.platform == "darwin":
        # macOS 常见中文字体已在 pick_font 中处理
        pass
    main()