import tkinter as tk
import random
import math
from dataclasses import dataclass
from typing import List, Tuple
import colorsys

@dataclass
class Particle:
    """粒子类"""
    x: float
    y: float
    vx: float
    vy: float
    color: str
    size: float
    life: float
    gravity: float
    fade: float
    trail: List[Tuple[float, float]]
    max_trail: int = 5

@dataclass
class Firework:
    """烟花类"""
    x: float
    y: float
    vx: float
    vy: float
    color: str
    size: float
    exploded: bool
    particles: List[Particle]
    target_height: float
    brightness: float

class FireworkSimulator:
    def __init__(self, root):
        self.root = root
        self.root.title(" 动态烟花模拟器")
        self.root.configure(bg='#000')
        
        # 窗口设置
        self.width = 1000
        self.height = 700
        self.root.geometry(f"{self.width}x{self.height}")
        
        # 创建Canvas
        self.canvas = tk.Canvas(root, bg='#000', highlightthickness=0)
        self.canvas.pack(fill=tk.BOTH, expand=True)
        
        # 烟花列表
        self.fireworks: List[Firework] = []
        self.particles: List[Particle] = []
        
        # 星星背景
        self.stars = []
        self.create_stars()
        
        # 控制变量
        self.gravity = 0.2
        self.firework_count = 0
        self.max_fireworks = 8
        
        # 控制面板
        self.create_control_panel()
        
        # 绑定事件
        self.canvas.bind("<Button-1>", self.manual_launch)
        self.canvas.bind("<Motion>", self.mouse_motion)
        self.canvas.bind("<B1-Motion>", self.mouse_drag_launch)
        
        # 鼠标位置
        self.mouse_x = 0
        self.mouse_y = 0
        
        # 启动动画
        self.animate()
    
    def create_control_panel(self):
        """创建控制面板"""
        control_frame = tk.Frame(self.root, bg='#222', bd=2, relief=tk.RAISED)
        control_frame.place(x=10, y=10)
        
    
        


        # 重力控制
        gravity_frame = tk.Frame(control_frame, bg='#222')
        gravity_frame.pack(padx=10, pady=5, fill=tk.X)
        
        tk.Label(gravity_frame, text="重力:", bg='#222', fg='white',
                font=("Microsoft YaHei", 9)).pack(side=tk.LEFT)
        
        self.gravity_scale = tk.Scale(gravity_frame, from_=0.1, to=0.5, 
                                    resolution=0.05, orient=tk.HORIZONTAL,
                                    bg='#222', fg='white', 
                                    highlightbackground='#222',
                                    length=100)
        self.gravity_scale.set(self.gravity)
        self.gravity_scale.pack(side=tk.RIGHT)
        
        # 烟花数量控制
        count_frame = tk.Frame(control_frame, bg='#222')
        count_frame.pack(padx=10, pady=5, fill=tk.X)
        
        tk.Label(count_frame, text="最大数量:", bg='#222', fg='white',
                font=("Microsoft YaHei", 9)).pack(side=tk.LEFT)
        
        self.count_scale = tk.Scale(count_frame, from_=3, to=15, 
                                   orient=tk.HORIZONTAL,
                                   bg='#222', fg='white',
                                   highlightbackground='#222',
                                   length=100)
        self.count_scale.set(self.max_fireworks)
        self.count_scale.pack(side=tk.RIGHT)
        
        # 帮助文本
        help_text = tk.Label(control_frame, 
                           text="操作说明：\n"
                                "• 点击画布发射烟花\n"
                                "• 按住拖拽可连续发射\n"
                                "• 点击按钮控制发射",
                           bg='#222', fg='#aaa',
                           font=("Microsoft YaHei", 8),
                           justify=tk.LEFT)
        help_text.pack(padx=10, pady=5)
        
        # 统计数据
        self.stats_label = tk.Label(control_frame, 
                                  text="烟花: 0 | 粒子: 0",
                                  bg='#222', fg='#4CAF50',
                                  font=("Microsoft YaHei", 9, "bold"))
        self.stats_label.pack(padx=10, pady=5)
    
    def create_stars(self, num_stars=100):
        """创建星空背景"""
        for _ in range(num_stars):
            x = random.randint(0, self.width)
            y = random.randint(0, self.height)
            size = random.uniform(0.5, 2)
            brightness = random.randint(150, 255)
            color = f'#{brightness:02x}{brightness:02x}{brightness:02x}'
            
            star = self.canvas.create_oval(
                x, y, x + size, y + size,
                fill=color, outline=color, width=0
            )
            self.stars.append(star)
    
    def hsv_to_rgb(self, h, s, v):
        """HSV转RGB"""
        r, g, b = colorsys.hsv_to_rgb(h, s, v)
        return f'#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}'
    
    def random_color(self, brightness=1.0):
        """生成随机颜色"""
        hue = random.random()  # 0-1
        saturation = random.uniform(0.7, 1.0)  # 饱和度
        value = random.uniform(0.7, 1.0) * brightness  # 明度
        return self.hsv_to_rgb(hue, saturation, value)
    
    def launch_firework(self, x=None, y=None, target_height=None):
        """发射烟花"""
        if len(self.fireworks) >= self.max_fireworks:
            return
        
        if x is None:
            x = random.randint(100, self.width - 100)
        if y is None:
            y = self.height
            
        if target_height is None:
            target_height = random.randint(50, int(self.height * 0.4))
        
        # 随机速度
        vx = random.uniform(-0.5, 0.5)
        vy = random.uniform(-12, -9)
        
        # 随机颜色
        brightness = random.uniform(0.8, 1.0)
        color = self.random_color(brightness)
        
        # 创建烟花
        firework = Firework(
            x=x, y=y, vx=vx, vy=vy,
            color=color, size=3,
            exploded=False, particles=[],
            target_height=target_height,
            brightness=brightness
        )
        self.fireworks.append(firework)
        self.firework_count += 1
    
    def explode_firework(self, firework):
        """烟花爆炸"""
        explosion_size = random.randint(80, 150)
        num_particles = random.randint(30, 60)
        
        # 创建爆炸粒子
        for _ in range(num_particles):
            # 随机角度
            angle = random.uniform(0, 2 * math.pi)
            
            # 随机速度
            speed = random.uniform(2, 6)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            
            # 粒子属性
            life = random.uniform(60, 120)  # 生命周期
            size = random.uniform(1.5, 3.5)
            fade = random.uniform(0.8, 1.5)  # 衰减速度
            
            # 颜色变化
            h, s, v = colorsys.rgb_to_hsv(
                int(firework.color[1:3], 16)/255,
                int(firework.color[3:5], 16)/255,
                int(firework.color[5:7], 16)/255
            )
            h = (h + random.uniform(-0.1, 0.1)) % 1.0
            v = v * random.uniform(0.8, 1.0)
            particle_color = self.hsv_to_rgb(h, s, v)
            
            particle = Particle(
                x=firework.x, y=firework.y,
                vx=vx, vy=vy,
                color=particle_color, size=size,
                life=life, gravity=self.gravity * 0.3,
                fade=fade, trail=[]
            )
            self.particles.append(particle)
        
        # 添加二次爆炸粒子
        if random.random() < 0.3:  # 30%几率二次爆炸
            self.create_secondary_explosion(firework.x, firework.y, firework.color)
    
    def create_secondary_explosion(self, x, y, base_color):
        """创建二次爆炸效果"""
        num_particles = random.randint(10, 20)
        
        for _ in range(num_particles):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(0.5, 1.5)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            
            # 更小的粒子
            size = random.uniform(0.8, 1.5)
            life = random.uniform(30, 50)
            fade = random.uniform(1.0, 2.0)
            
            # 稍微改变颜色
            r, g, b = int(base_color[1:3], 16), int(base_color[3:5], 16), int(base_color[5:7], 16)
            r = min(255, int(r * random.uniform(1.0, 1.3)))
            g = min(255, int(g * random.uniform(1.0, 1.3)))
            b = min(255, int(b * random.uniform(1.0, 1.3)))
            color = f'#{r:02x}{g:02x}{b:02x}'
            
            particle = Particle(
                x=x, y=y, vx=vx, vy=vy,
                color=color, size=size,
                life=life, gravity=self.gravity * 0.2,
                fade=fade, trail=[]
            )
            self.particles.append(particle)
    
    def update_fireworks(self):
        """更新烟花状态"""
        for firework in self.fireworks[:]:
            # 更新位置
            firework.x += firework.vx
            firework.y += firework.vy
            firework.vy += self.gravity * 0.5
            
            # 检查是否到达爆炸高度
            if firework.y <= firework.target_height and not firework.exploded:
                firework.exploded = True
                self.explode_firework(firework)
            
            # 移除超出边界的烟花
            if (firework.y > self.height or 
                firework.x < 0 or firework.x > self.width or
                firework.vy > 5):  # 速度过大时也移除
                self.fireworks.remove(firework)
                continue
    
    def update_particles(self):
        """更新粒子状态"""
        for particle in self.particles[:]:
            # 更新位置
            particle.x += particle.vx
            particle.y += particle.vy
            particle.vy += particle.gravity
            
            # 记录轨迹
            particle.trail.append((particle.x, particle.y))
            if len(particle.trail) > particle.max_trail:
                particle.trail.pop(0)
            
            # 更新生命周期
            particle.life -= particle.fade
            
            # 随机漂移
            particle.vx += random.uniform(-0.1, 0.1)
            particle.vy += random.uniform(-0.1, 0.1)
            
            # 移除死亡粒子
            if (particle.life <= 0 or 
                particle.y > self.height or
                particle.x < 0 or particle.x > self.width):
                self.particles.remove(particle)
    
    def draw_fireworks(self):
        """绘制烟花和粒子"""
        # 清除Canvas
        self.canvas.delete("firework")
        self.canvas.delete("particle")
        self.canvas.delete("trail")
        
        # 绘制烟花
        for firework in self.fireworks:
            if not firework.exploded:
                # 绘制上升的烟花
                self.canvas.create_oval(
                    firework.x - firework.size,
                    firework.y - firework.size,
                    firework.x + firework.size,
                    firework.y + firework.size,
                    fill=firework.color, outline=firework.color,
                    width=0, tags="firework"
                )
                
                # 绘制烟花的拖尾
                for i in range(3):
                    size = firework.size - i
                    self.canvas.create_oval(
                        firework.x - size,
                        firework.y + 5 + i*2 - size,
                        firework.x + size,
                        firework.y + 5 + i*2 + size,
                        fill=firework.color, outline=firework.color,
                        width=0, tags="firework"
                    )
        
        # 绘制粒子
        for particle in self.particles:
            # 计算透明度
            life_ratio = particle.life / 100
            if life_ratio > 1:
                life_ratio = 1
            if life_ratio < 0:
                life_ratio = 0
            
            # 绘制轨迹
            if len(particle.trail) > 1:
                for i in range(len(particle.trail) - 1):
                    x1, y1 = particle.trail[i]
                    x2, y2 = particle.trail[i+1]
                    
                    # 计算轨迹透明度
                    trail_alpha = life_ratio * (i / len(particle.trail)) * 0.5
                    if trail_alpha > 0:
                        # 简化颜色，不使用透明度
                        self.canvas.create_line(
                            x1, y1, x2, y2,
                            fill=particle.color, width=particle.size * 0.5,
                            smooth=True, tags="trail"
                        )
            
            # 绘制粒子
            self.canvas.create_oval(
                particle.x - particle.size,
                particle.y - particle.size,
                particle.x + particle.size,
                particle.y + particle.size,
                fill=particle.color, outline=particle.color,
                width=0, tags="particle"
            )
            
            # 绘制粒子光晕（简化版本）
            glow_size = particle.size * 1.5
            if life_ratio > 0.3:  # 只在粒子生命周期较长时绘制光晕
                self.canvas.create_oval(
                    particle.x - glow_size,
                    particle.y - glow_size,
                    particle.x + glow_size,
                    particle.y + glow_size,
                    fill=particle.color, outline=particle.color,
                    width=0, tags="particle"
                )
    
    def animate(self):
        """动画循环"""
        # 更新重力
        self.gravity = self.gravity_scale.get()
        self.max_fireworks = self.count_scale.get()
        
        # 随机发射烟花
        if random.random() < 0.02 and len(self.fireworks) < self.max_fireworks:
            self.launch_firework()
        
        # 更新烟花状态
        self.update_fireworks()
        self.update_particles()
        
        # 重新绘制
        self.draw_fireworks()
        
        # 更新统计数据
        self.stats_label.config(
            text=f"烟花: {len(self.fireworks)} | 粒子: {len(self.particles)}"
        )
        
        # 继续动画
        self.root.after(16, self.animate)  # ~60 FPS
    
    def auto_launch(self):
        """自动发射多个烟花"""
        for _ in range(random.randint(3, 5)):
            self.launch_firework()
    
    def launch_multiple(self):
        """连续发射多个烟花"""
        x = random.randint(200, self.width - 200)
        y_offset = random.randint(-50, 50)
        
        for i in range(random.randint(4, 7)):
            self.root.after(i * 150, 
                          lambda x=x, offset=y_offset: 
                          self.launch_firework(x + offset, self.height, 200))
    
    def clear_all(self):
        """清除所有烟花"""
        self.fireworks.clear()
        self.particles.clear()
        self.canvas.delete("firework")
        self.canvas.delete("particle")
        self.canvas.delete("trail")
    
    def manual_launch(self, event):
        """手动发射烟花"""
        if event.x < 200 and event.y < 200:  # 避开控制面板区域
            return
        self.launch_firework(event.x, self.height, event.y)
    
    def mouse_drag_launch(self, event):
        """鼠标拖拽连续发射"""
        if (abs(event.x - self.mouse_x) > 10 or 
            abs(event.y - self.mouse_y) > 10):
            if event.x > 200:  # 避开控制面板区域
                self.launch_firework(event.x, self.height, event.y)
        self.mouse_x, self.mouse_y = event.x, event.y
    
    def mouse_motion(self, event):
        """记录鼠标位置"""
        self.mouse_x, self.mouse_y = event.x, event.y

def main():
    root = tk.Tk()
    
    # 窗口居中
    screen_width = root.winfo_screenwidth()
    screen_height = root.winfo_screenheight()
    x = (screen_width - 1000) // 2
    y = (screen_height - 700) // 2
    root.geometry(f"1000x700+{x}+{y}")
    
    app = FireworkSimulator(root)
    root.mainloop()

if __name__ == "__main__":
    main()