import tkinter as tk
import random
import math

# ---------- 粒子类 ----------
class Particle:
    def __init__(self, x, y, color, vx, vy, size=3, lifetime=100):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.color = color
        self.size = size
        self.lifetime = lifetime          # 总寿命（帧数）
        self.age = 0                     # 当前年龄
        self.alpha = 1.0                 # 不透明度（1.0 ~ 0.0）
        self.trail = []                  # 保存最近几个位置，形成拖尾

    def update(self, gravity=0.05, drag=0.99):
        """更新粒子状态：速度、位置、年龄、透明度"""
        # 物理更新
        self.vx *= drag
        self.vy *= drag
        self.vy += gravity               # 重力
        self.x += self.vx
        self.y += self.vy

        # 年龄增长
        self.age += 1
        # 透明度随年龄线性减小
        self.alpha = max(0.0, 1.0 - self.age / self.lifetime)

        # 记录轨迹（用于拖尾）
        self.trail.append((self.x, self.y))
        if len(self.trail) > 6:          # 保留最近6个点
            self.trail.pop(0)

    def is_alive(self):
        """粒子是否还存活（未超过寿命且透明度>0）"""
        return self.age < self.lifetime and self.alpha > 0.01

    def draw(self, canvas):
        """绘制粒子及其拖尾"""
        if not self.is_alive():
            return

        # 绘制拖尾（逐渐淡出的圆点）
        for i, (tx, ty) in enumerate(self.trail):
            # 越早的点越淡越小
            ratio = (i + 1) / len(self.trail)
            alpha = self.alpha * ratio * 0.6
            size = self.size * ratio * 0.8
            if alpha > 0.01:
                r, g, b = self._hex_to_rgb(self.color)
                # 混合背景色（黑色）产生淡出效果
                fade = 1 - alpha
                nr = int(r * alpha + 0 * fade)
                ng = int(g * alpha + 0 * fade)
                nb = int(b * alpha + 0 * fade)
                fill_color = f"#{nr:02x}{ng:02x}{nb:02x}"
                canvas.create_oval(
                    tx - size, ty - size,
                    tx + size, ty + size,
                    fill=fill_color, outline=""
                )

        # 绘制主粒子（核心）
        r, g, b = self._hex_to_rgb(self.color)
        core_color = f"#{r:02x}{g:02x}{b:02x}"
        canvas.create_oval(
            self.x - self.size, self.y - self.size,
            self.x + self.size, self.y + self.size,
            fill=core_color, outline=""
        )

    @staticmethod
    def _hex_to_rgb(hex_color):
        """将 #RRGGBB 转为 (R, G, B)"""
        hex_color = hex_color.lstrip('#')
        return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))


# ---------- 烟花类 ----------
class Firework:
    def __init__(self, canvas, x, y, color=None, particle_count=80):
        self.canvas = canvas
        self.x = x
        self.y = y
        self.particles = []
        self.color = color or self._random_color()
        self.particle_count = particle_count
        self._explode()

    def _random_color(self):
        """生成随机亮色（使用HSV）"""
        hue = random.random()
        r, g, b = self._hsv_to_rgb(hue, 0.9, 1.0)
        return f"#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}"

    @staticmethod
    def _hsv_to_rgb(h, s, v):
        """HSV转RGB（辅助生成鲜艳颜色）"""
        if s == 0.0:
            return (v, v, v)
        i = int(h * 6.0)
        f = (h * 6.0) - i
        p = v * (1.0 - s)
        q = v * (1.0 - s * f)
        t = v * (1.0 - s * (1.0 - f))
        i = i % 6
        if i == 0: return (v, t, p)
        if i == 1: return (q, v, p)
        if i == 2: return (p, v, t)
        if i == 3: return (p, q, v)
        if i == 4: return (t, p, v)
        if i == 5: return (v, p, q)

    def _explode(self):
        """生成所有粒子，速度方向均匀分布，大小随机"""
        for _ in range(self.particle_count):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(2, 8)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            size = random.uniform(1.5, 4)
            lifetime = random.randint(60, 150)
            # ★ 关键修复：通过 Particle 类调用静态方法 ★
            base_r, base_g, base_b = Particle._hex_to_rgb(self.color)
            dr = random.randint(-30, 30)
            dg = random.randint(-30, 30)
            db = random.randint(-30, 30)
            r = max(0, min(255, base_r + dr))
            g = max(0, min(255, base_g + dg))
            b = max(0, min(255, base_b + db))
            color = f"#{r:02x}{g:02x}{b:02x}"
            particle = Particle(
                self.x, self.y, color,
                vx, vy, size, lifetime
            )
            self.particles.append(particle)

    def update(self):
        """更新所有粒子"""
        for p in self.particles:
            p.update()
        # 移除死亡粒子
        self.particles = [p for p in self.particles if p.is_alive()]

    def draw(self):
        """绘制所有粒子"""
        for p in self.particles:
            p.draw(self.canvas)

    def is_alive(self):
        """烟花是否还有存活粒子"""
        return len(self.particles) > 0


# ---------- 主程序 ----------
class FireworkApp:
    def __init__(self, root):
        self.root = root
        self.root.title("动态烟花")
        self.root.geometry("800x600")
        self.root.resizable(False, False)

        # 创建画布，黑色背景
        self.canvas = tk.Canvas(root, width=800, height=600, bg='black', highlightthickness=0)
        self.canvas.pack()

        # 存放所有烟花对象
        self.fireworks = []

        # 绑定鼠标点击事件
        self.canvas.bind("<Button-1>", self.on_click)

        # 启动动画循环
        self.animate()

        # 初始自动放几朵烟花（可选）
        self.root.after(300, lambda: self._auto_launch(5))

    def on_click(self, event):
        """鼠标点击生成一朵烟花"""
        self._create_firework(event.x, event.y)

    def _create_firework(self, x, y):
        """创建一朵烟花并添加到列表"""
        fw = Firework(self.canvas, x, y)
        self.fireworks.append(fw)

    def _auto_launch(self, count=1):
        """自动在随机位置生成烟花（用于初始演示）"""
        for _ in range(count):
            x = random.randint(100, 700)
            y = random.randint(100, 500)
            self._create_firework(x, y)

    def animate(self):
        """每一帧更新并绘制所有烟花"""
        # 清空画布
        self.canvas.delete("all")

        # 更新并绘制所有烟花
        for fw in self.fireworks:
            fw.update()
            fw.draw()

        # 移除已经死亡的烟花
        self.fireworks = [fw for fw in self.fireworks if fw.is_alive()]

        # 每隔30ms刷新一次（约33帧/秒）
        self.root.after(30, self.animate)


# ---------- 运行程序 ----------
if __name__ == "__main__":
    root = tk.Tk()
    app = FireworkApp(root)
    root.mainloop()