"""
🎆 高级动态烟花模拟器 - 完整版
"""

import pygame
import random
import math
import sys
from enum import Enum
from dataclasses import dataclass
from typing import List, Tuple, Optional
from collections import deque

# ============================================================
# 配置与常量
# ============================================================

@dataclass
class Config:
    """全局配置类"""
    WIDTH: int = 1024
    HEIGHT: int = 640
    FPS: int = 60
    BACKGROUND_COLOR: Tuple[int, int, int] = (5, 5, 25)
    MAX_FIREWORKS: int = 12
    AUTO_SPAWN_RATE: float = 0.03
    GRAVITY: float = 0.06
    AIR_RESISTANCE: float = 0.982
    PARTICLE_LIFE_MIN: int = 30
    PARTICLE_LIFE_MAX: int = 90
    TRAIL_LENGTH: int = 12
    STAR_COUNT: int = 200

config = Config()

# 颜色系统
class Colors:
    RED = (255, 50, 50)
    GREEN = (50, 255, 50)
    BLUE = (50, 130, 255)
    YELLOW = (255, 230, 50)
    ORANGE = (255, 160, 20)
    PINK = (255, 105, 180)
    PURPLE = (170, 70, 220)
    CYAN = (50, 240, 250)
    WHITE = (255, 245, 235)
    GOLD = (255, 215, 0)
    SILVER = (190, 195, 210)
    MAGENTA = (238, 58, 232)
    LIME = (57, 237, 77)
    CORAL = (247, 127, 126)
    SKY_BLUE = (135, 206, 253)
    HOT_PINK = (252, 78, 167)
    
    @classmethod
    def get_all_colors(cls) -> List[Tuple[int, int, int]]:
        return [v for k, v in cls.__dict__.items() if isinstance(v, tuple) and k.isupper()]
    
    @classmethod
    def get_random_color(cls) -> Tuple[int, int, int]:
        return random.choice(cls.get_all_colors())
    
    @classmethod
    def gradient_between(cls, color1, color2, t):
        return tuple(int(c1 + (c2 - c1) * t) for c1, c2 in zip(color1, color2))

# 烟花类型枚举
class FireworkType(Enum):
    NORMAL = "普通圆形"
    SPIRAL = "螺旋"
    HEART = "心形"
    RING = "环形"
    WILLOW = "垂柳"
    PALM = "棕榈树"
    CROWN = "皇冠"
    MULTI_STAGE = "多级"
    COMET = "彗星"
    BOUQUET = "花束"

# ============================================================
# 粒子系统
# ============================================================

class Particle:
    def __init__(self, x, y, vx, vy, color, size=3, life=60, gravity=None):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.color = color
        self.size = size
        self.life = life
        self.max_life = life
        self.gravity = gravity if gravity is not None else config.GRAVITY
        self.active = True
        self.trail = []
        self.sparkle_timer = 0
        self.flicker = random.uniform(0.85, 1.0)
        
    def update(self):
        if not self.active:
            return
        self.trail.append((self.x, self.y))
        if len(self.trail) > config.TRAIL_LENGTH:
            self.trail.pop(0)
        self.x += self.vx
        self.y += self.vy
        self.vy += self.gravity
        self.vx *= config.AIR_RESISTANCE
        self.vy *= config.AIR_RESISTANCE
        self.life -= 1
        self.sparkle_timer += 1
        if self.life <= 0:
            self.active = False
    
    def draw(self, screen):
        if not self.active:
            return
        alpha = int(255 * (self.life / self.max_life))
        flicker_effect = self.flicker * (0.9 + 0.1 * math.sin(self.sparkle_timer * 0.3))
        current_size = max(1, int(self.size * (self.life / self.max_life) * flicker_effect))
        
        # 轨迹
        for i, pos in enumerate(self.trail):
            trail_alpha = int(alpha * 0.3 * (i / len(self.trail)))
            trail_size = max(1, int(current_size * 0.5 * (i / len(self.trail))))
            trail_surf = pygame.Surface((trail_size * 2, trail_size * 2), pygame.SRCALPHA)
            pygame.draw.circle(trail_surf, (*self.color, trail_alpha), 
                             (trail_size, trail_size), trail_size)
            screen.blit(trail_surf, (int(pos[0] - trail_size), int(pos[1] - trail_size)))
        
        # 主粒子
        surf = pygame.Surface((current_size * 2, current_size * 2), pygame.SRCALPHA)
        for r in range(current_size * 2, 0, -1):
            glow_alpha = int(alpha * (1 - r / (current_size * 2)) * 0.3)
            if glow_alpha > 0:
                pygame.draw.circle(surf, (*self.color, glow_alpha), 
                                 (current_size, current_size), r)
        pygame.draw.circle(surf, (*self.color, alpha), 
                          (current_size, current_size), current_size)
        screen.blit(surf, (int(self.x - current_size), int(self.y - current_size)))

class SparkParticle(Particle):
    def __init__(self, x, y, vx, vy, color=None):
        if color is None:
            color = random.choice([Colors.WHITE, Colors.YELLOW, Colors.GOLD])
        super().__init__(x, y, vx, vy, color, size=2, life=random.randint(15, 35), gravity=0.045)
        self.brightness = 1.0
        
    def draw(self, screen):
        if not self.active:
            return
        alpha = int(255 * (self.life / self.max_life) * self.brightness)
        size = max(1, int(self.size * (self.life / self.max_life)))
        surf = pygame.Surface((size * 4, size * 4), pygame.SRCALPHA)
        for r in range(size * 2, 0, -1):
            glow_alpha = int(alpha * (1 - r / (size * 2)) * 0.5)
            if glow_alpha > 0:
                pygame.draw.circle(surf, (*self.color, glow_alpha), 
                                 (size * 2, size * 2), r)
        pygame.draw.circle(surf, (*self.color, alpha), 
                          (size * 2, size * 2), size)
        screen.blit(surf, (int(self.x - size * 2), int(self.y - size * 2)))

# ============================================================
# 烟花核心类
# ============================================================

class Firework:
    def __init__(self, x=None, y=None, target_y=None, color=None):
        self.x = x if x is not None else random.randint(100, config.WIDTH - 100)
        self.y = y if y is not None else config.HEIGHT + 20
        self.target_y = target_y if target_y is not None else random.randint(80, config.HEIGHT // 2 - 50)
        self.speed = random.uniform(4, 9)
        self.horizontal_drift = random.uniform(-0.5, 0.5)
        self.vertical_wobble = random.uniform(-0.3, 0.3)
        self.wobble_timer = 0
        self.color = color if color else random.choice(Colors.get_all_colors())
        self.exploded = False
        self.completed = False
        self.particles = []
        self.trail = deque(maxlen=config.TRAIL_LENGTH)
        self.effect_type = random.choice(list(FireworkType))
        self.explosion_radius = random.uniform(80, 200)
        self.num_particles = random.randint(80, 200)
        
    def update(self):
        if self.completed:
            return
        if not self.exploded:
            self._update_rising()
        else:
            self._update_exploded()
    
    def _update_rising(self):
        self.wobble_timer += 0.1
        self.y -= self.speed
        self.x += self.horizontal_drift + math.sin(self.wobble_timer) * self.vertical_wobble
        self.speed *= 0.990
        self.trail.append((self.x, self.y))
        if self.y <= self.target_y or self.speed < 0.3:
            self.explode()
    
    def _update_exploded(self):
        active_particles = []
        for particle in self.particles:
            particle.update()
            if particle.active:
                active_particles.append(particle)
        self.particles = active_particles
        if not self.particles:
            self.completed = True
    
    def explode(self):
        self.exploded = True
    
    def draw(self, screen):
        if self.completed:
            return
        if not self.exploded:
            self._draw_rising(screen)
        else:
            self._draw_exploded(screen)
    
    def _draw_rising(self, screen):
        pygame.draw.circle(screen, Colors.WHITE, (int(self.x), int(self.y)), 4)
        pygame.draw.circle(screen, Colors.YELLOW, (int(self.x), int(self.y)), 2)
        glow_surf = pygame.Surface((20, 20), pygame.SRCALPHA)
        pygame.draw.circle(glow_surf, (255, 200, 100, 80), (10, 10), 10)
        screen.blit(glow_surf, (int(self.x - 10), int(self.y - 10)))
        for i, pos in enumerate(self.trail):
            alpha = int(150 * (i / len(self.trail)))
            size = int(3 * (i / len(self.trail))) + 1
            trail_surf = pygame.Surface((size * 2, size * 2), pygame.SRCALPHA)
            pygame.draw.circle(trail_surf, (255, 200, 100, alpha), 
                             (size, size), size)
            screen.blit(trail_surf, (int(pos[0] - size), int(pos[1] - size)))
    
    def _draw_exploded(self, screen):
        for particle in self.particles:
            particle.draw(screen)

# ============================================================
# 各种烟花类型的实现
# ============================================================

class NormalFirework(Firework):
    def explode(self):
        super().explode()
        for _ in range(self.num_particles):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(2, 8)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            particle = Particle(
                self.x, self.y, vx, vy, self.color,
                size=random.randint(2, 5),
                life=random.randint(config.PARTICLE_LIFE_MIN, config.PARTICLE_LIFE_MAX)
            )
            self.particles.append(particle)

class SpiralFirework(Firework):
    def explode(self):
        super().explode()
        num_spirals = 3
        particles_per_spiral = 40
        for spiral in range(num_spirals):
            base_angle = (2 * math.pi / num_spirals) * spiral
            for i in range(particles_per_spiral):
                angle = base_angle + (i / particles_per_spiral) * 2 * math.pi * 3
                radius = 3 + (i / particles_per_spiral) * self.explosion_radius
                vx = math.cos(angle) * radius * 0.075
                vy = math.sin(angle) * radius * 0.075
                cv = random.randint(-30, 30)
                ac = tuple(min(255, max(0, c + cv)) for c in self.color)
                particle = Particle(self.x, self.y, vx, vy, ac,
                    size=random.randint(2, 4), life=random.randint(50, 80))
                self.particles.append(particle)

class HeartFirework(Firework):
    def explode(self):
        super().explode()
        heart_points = []
        for t in range(0, 630, 2):
            theta = t / 100
            x = 16 * math.sin(theta) ** 3
            y = 13 * math.cos(theta) - 5 * math.cos(2*theta) - 2 * math.cos(3*theta) - math.cos(4*theta)
            heart_points.append((x, y))
        scale = random.uniform(4, 7)
        for px, py in heart_points:
            vx = px * scale * 0.14 + random.uniform(-0.3, 0.3)
            vy = -py * scale * 0.16 + random.uniform(-0.3, 0.3)
            particle = Particle(self.x, self.y, vx, vy, self.color,
                size=random.randint(2, 4), life=random.randint(40, 65))
            self.particles.append(particle)

class RingFirework(Firework):
    def explode(self):
        super().explode()
        num_rings = random.randint(2, 4)
        for ring in range(num_rings):
            radius = (ring + 1) * (self.explosion_radius / num_rings)
            np = int(30 + radius * 0.5)
            for i in range(np):
                angle = (2 * math.pi / np) * i
                speed = radius * 0.060
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                t = ring / num_rings
                color = Colors.gradient_between(self.color, Colors.WHITE, t)
                particle = Particle(self.x, self.y, vx, vy, color,
                    size=random.randint(2, 4), life=random.randint(55, 95))
                self.particles.append(particle)

class WillowFirework(Firework):
    def explode(self):
        super().explode()
        for _ in range(self.num_particles):
            angle = random.uniform(math.pi * 0.3, math.pi * 0.7)
            speed = random.uniform(1, 4)
            vx = math.cos(angle) * speed
            vy = -math.sin(angle) * speed
            particle = Particle(self.x, self.y, vx, vy, self.color,
                size=random.randint(2, 3), life=random.randint(80, 120), gravity=0.015)
            self.particles.append(particle)

class PalmFirework(Firework):
    def explode(self):
        super().explode()
        nb = random.randint(8, 14)
        for branch in range(nb):
            ba = (2 * math.pi / nb) * branch
            for i in range(random.randint(8, 18)):
                angle = ba + random.uniform(-0.3, 0.3)
                speed = random.uniform(4, 9) * (1 - i * 0.030)
                vx = math.cos(angle) * speed
                vy = math.sin(angle) * speed
                particle = Particle(self.x, self.y, vx, vy, self.color,
                    size=random.randint(2, 4), life=random.randint(40, 70))
                self.particles.append(particle)

class CrownFirework(Firework):
    def explode(self):
        super().explode()
        ns = random.randint(24, 48)
        for spike in range(ns):
            angle = (2 * math.pi / ns) * spike
            speed = random.uniform(6, 11)
            vx = math.cos(angle) * speed * 0.7
            vy = math.sin(angle) * speed * 0.7
            mp = Particle(self.x, self.y, vx, vy, self.color,
                size=random.randint(3, 5), life=random.randint(50, 76))
            self.particles.append(mp)
            for _ in range(3):
                sa = angle + random.uniform(-0.2, 0.2)
                ss = speed * random.uniform(0.4, 0.7)
                svx = math.cos(sa) * ss
                svy = math.sin(sa) * ss
                sp = SparkParticle(self.x, self.y, svx, svy)
                self.particles.append(sp)

class MultiStageFirework(Firework):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.stages = random.randint(2, 4)
        self.stage_delays = [random.randint(20, 40) for _ in range(self.stages)]
        self.stage_timers = [0 for _ in range(self.stages)]
        self.stage_positions = [(self.x, self.y) for _ in range(self.stages)]
        self.stage_colors = [random.choice(Colors.get_all_colors()) for _ in range(self.stages)]
        
    def explode(self):
        super().explode()
        self._create_stage_explosion(0)
        
    def _create_stage_explosion(self, si):
        sx, sy = self.stage_positions[si]
        color = self.stage_colors[si]
        for _ in range(self.num_particles // self.stages):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(2, 7)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            particle = Particle(sx, sy, vx, vy, color,
                size=random.randint(2, 4), life=random.randint(40, 71))
            self.particles.append(particle)
    
    def update(self):
        if self.completed:
            return
        if not self.exploded:
            self._update_rising()
        else:
            for i in range(self.stages - 1):
                if self.stage_timers[i] < self.stage_delays[i]:
                    self.stage_timers[i] += 1
                    if self.stage_timers[i] >= self.stage_delays[i]:
                        ox = random.uniform(-30, 30)
                        oy = random.uniform(-30, 30)
                        self.stage_positions[i + 1] = (
                            self.stage_positions[i][0] + ox,
                            self.stage_positions[i][1] + oy
                        )
                        self._create_stage_explosion(i + 1)
            self._update_exploded()

class CometFirework(Firework):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.comet_angle = random.uniform(math.pi * 0.25, math.pi * 0.75)
        self.comet_speed = random.uniform(12, 18)
        self.comet_particles = []
        
    def _update_rising(self):
        self.wobble_timer += 0.1
        self.x += math.cos(self.comet_angle) * self.comet_speed
        self.y -= math.sin(self.comet_angle) * self.comet_speed
        self.comet_speed *= 0.993
        if random.random() < 0.3:
            sp = SparkParticle(self.x, self.y,
                random.uniform(-1, 1), random.uniform(-1, 1),
                Colors.gradient_between(self.color, Colors.WHITE, random.random()))
            self.comet_particles.append(sp)
        for p in self.comet_particles[:]:
            p.update()
            if not p.active:
                self.comet_particles.remove(p)
        if (self.x < -50 or self.x > config.WIDTH + 50 or 
            self.y < -50 or self.y > config.HEIGHT + 50):
            self.explode()
    
    def _draw_rising(self, screen):
        for p in self.comet_particles:
            p.draw(screen)
        pygame.draw.circle(screen, Colors.WHITE, (int(self.x), int(self.y)), 6)
        gs = pygame.Surface((30, 30), pygame.SRCALPHA)
        pygame.draw.circle(gs, (*self.color, 100), (15, 15), 15)
        screen.blit(gs, (int(self.x - 15), int(self.y - 15)))
    
    def explode(self):
        super().explode()
        for _ in range(self.num_particles):
            angle = random.uniform(0, 2 * math.pi)
            speed = random.uniform(3, 9)
            vx = math.cos(angle) * speed
            vy = math.sin(angle) * speed
            particle = Particle(self.x, self.y, vx, vy, self.color,
                size=random.randint(2, 5), life=random.randint(35, 66))
            self.particles.append(particle)

class BouquetFirework(Firework):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.sub_count = random.randint(3, 7)
        self.sub_fireworks = []
        
    def explode(self):
        super().explode()
        for i in range(self.sub_count):
            angle = (2 * math.pi / self.sub_count) * i
            dist = random.uniform(30, 80)
            sx = self.x + math.cos(angle) * dist
            sy = self.y + math.sin(angle) * dist
            cv = random.randint(-40, 40)
            sc = tuple(min(255, max(0, c + cv)) for c in self.color)
            sf = NormalFirework(sx, sy, self.target_y, sc)
            sf.num_particles = random.randint(20, 41)
            sf.explosion_radius = random.uniform(40, 81)
            sf.explode()
            self.sub_fireworks.append(sf)
    
    def _update_exploded(self):
        for fw in self.sub_fireworks[:]:
            fw._update_exploded()
            if fw.completed:
                self.sub_fireworks.remove(fw)
        super()._update_exploded()
    
    def _draw_exploded(self, screen):
        for fw in self.sub_fireworks:
            fw._draw_exploded(screen)
        super()._draw_exploded(screen)

# ============================================================
# 环境系统
# ============================================================

class Star:
    def __init__(self):
        self.x = random.randint(0, config.WIDTH)
        self.y = random.randint(0, config.HEIGHT // 2)
        self.size = random.uniform(0.5, 2.5)
        self.brightness = random.uniform(0.3, 1.0)
        self.twinkle_speed = random.uniform(0.008, 0.020)
        self.twinkle_offset = random.uniform(0, 2 * math.pi)
        
    def update(self):
        self.brightness = 0.5 + 0.5 * math.sin(
            pygame.time.get_ticks() * self.twinkle_speed + self.twinkle_offset
        )
    
    def draw(self, screen):
        alpha = int(255 * self.brightness)
        size = max(1, int(self.size * self.brightness))
        surf = pygame.Surface((size * 2, size * 2), pygame.SRCALPHA)
        pygame.draw.circle(surf, (255, 255, 255, alpha), (size, size), size)
        screen.blit(surf, (int(self.x - size), int(self.y - size)))

class Moon:
    def __init__(self):
        self.x = config.WIDTH - 120
        self.y = 80
        self.phase = 0
        self.glow_intensity = 0.3
        
    def update(self):
        self.phase = (self.phase + 0.001) % (2 * math.pi)
        self.glow_intensity = 0.2 + 0.15 * math.sin(self.phase)
    
    def draw(self, screen):
        mr = 35
        for r in range(60, 0, -5):
            alpha = int(50 * self.glow_intensity * (1 - r / 61))
            surf = pygame.Surface((r * 2, r * 2), pygame.SRCALPHA)
            pygame.draw.circle(surf, (255, 244, 222, alpha), (r, r), r)
            screen.blit(surf, (int(self.x - r), int(self.y - r)))
        pygame.draw.circle(screen, (254, 246, 232), (int(self.x), int(self.y)), mr)
        pygame.draw.circle(screen, (239, 231, 213), (int(self.x), int(self.y)), mr, 1)

class Background:
    def __init__(self):
        self.stars = [Star() for _ in range(config.STAR_COUNT)]
        self.moon = Moon()
        self.ground_height = 20
        
    def update(self):
        for star in self.stars:
            star.update()
        self.moon.update()
    
    def draw(self, screen):
        sg = pygame.Surface((config.WIDTH, config.HEIGHT))
        for y in range(config.HEIGHT):
            t = y / config.HEIGHT
            color = (int(5 + 10*t), int(5 + 15*t), int(25 + 21*t))
            pygame.draw.line(sg, color, (0, y), (config.WIDTH, y))
        screen.blit(sg, (0, 0))
        for star in self.stars:
            star.draw(screen)
        self.moon.draw(screen)
        gr = pygame.Rect(0, config.HEIGHT - self.ground_height, config.WIDTH, self.ground_height)
        pygame.draw.rect(screen, (10, 15, 22), gr)
        bcolors = [(15, 19, 27), (18, 22, 29), (12, 16, 24)]
        buildings = [
            (50, 55), (104, 109), (172, 141), (234, 173), (314, 121),
            (386, 152), (446, 181), (524, 113), (594, 146), (666, 166),
            (734, 131), (814, 157), (884, 177), (964, 107), (1034, 142)
        ]
        for bx, bh in buildings:
            if bx < config.WIDTH:
                rect = pygame.Rect(bx, config.HEIGHT - self.ground_height - bh, 40, bh)
                color = random.choice(bcolors)
                pygame.draw.rect(screen, color, rect)
                for wy in range(config.HEIGHT - self.ground_height - bh + 5, 
                               config.HEIGHT - self.ground_height - 5, 15):
                    for wx in range(bx + 5, bx + 33, 10):
                        if random.random() < 0.3:
                            pygame.draw.rect(screen, (58, 63, 73), (wx, wy, 6, 8))

# ============================================================
# 特效系统
# ============================================================

class TextEffect:
    def __init__(self, text, x, y, color=None, duration=120):
        self.text = text
        self.x = x
        self.y = y
        self.color = color if color else Colors.WHITE
        self.duration = duration
        self.age = 0
        self.active = True
        self.font = pygame.font.Font(None, 42)
        
    def update(self):
        self.age += 1
        self.y -= 0.5
        if self.age >= self.duration:
            self.active = False
    
    def draw(self, screen):
        if not self.active:
            return
        alpha = int(255 * (1 - self.age / self.duration))
        text_surf = self.font.render(self.text, True, self.color)
        text_surf.set_alpha(alpha)
        text_rect = text_surf.get_rect(center=(self.x, self.y))
        screen.blit(text_surf, text_rect)

class FlashEffect:
    def __init__(self, x, y, intensity=1.0, duration=15):
        self.x = x
        self.y = y
        self.intensity = intensity
        self.duration = duration
        self.age = 0
        self.active = True
        
    def update(self):
        self.age += 1
        if self.age >= self.duration:
            self.active = False
    
    def draw(self, screen):
        if not self.active:
            return
        alpha = int(200 * self.intensity * (1 - self.age / self.duration))
        for r in range(100, 0, -10):
            surf = pygame.Surface((r * 2, r * 2), pygame.SRCALPHA)
            pygame.draw.circle(surf, (255, 255, 228, alpha), (r, r), r)
            screen.blit(surf, (int(self.x - r), int(self.y - r)))

# ============================================================
# 粒子系统管理器
# ============================================================

class ParticleSystem:
    def __init__(self):
        self.fireworks = []
        self.text_effects = []
        self.flash_effects = []
        self.ambient_particles = []
        self.background = Background()
        self.total_fireworks = 0
        self.start_time = pygame.time.get_ticks()
        
    def spawn_firework(self, x=None, y=None, firework_type=None):
        if len(self.fireworks) >= config.MAX_FIREWORKS:
            return
        if firework_type is None:
            firework_type = random.choice(list(FireworkType))
        
        classes = {
            FireworkType.NORMAL: NormalFirework,
            FireworkType.SPIRAL: SpiralFirework,
            FireworkType.HEART: HeartFirework,
            FireworkType.RING: RingFirework,
            FireworkType.WILLOW: WillowFirework,
            FireworkType.PALM: PalmFirework,
            FireworkType.CROWN: CrownFirework,
            FireworkType.MULTI_STAGE: MultiStageFirework,
            FireworkType.COMET: CometFirework,
            FireworkType.BOUQUET: BouquetFirework,
        }
        fw = classes[firework_type](x, y)
        self.fireworks.append(fw)
        self.total_fireworks += 1
        if random.random() < 0.15:
            texts = ["✨", "🎆", "🌟", "💫", "⭐"]
            te = TextEffect(random.choice(texts), fw.x, fw.y - 50, fw.color, random.randint(60, 119))
            self.text_effects.append(te)
    
    def update(self):
        self.background.update()
        for fw in self.fireworks[:]:
            fw.update()
            if fw.completed:
                self.fireworks.remove(fw)
        for te in self.text_effects[:]:
            te.update()
            if not te.active:
                self.text_effects.remove(te)
        for fe in self.flash_effects[:]:
            fe.update()
            if not fe.active:
                self.flash_effects.remove(fe)
        for ap in self.ambient_particles[:]:
            ap.update()
            if not ap.active:
                self.ambient_particles.remove(ap)
        if random.random() < config.AUTO_SPAWN_RATE:
            self.spawn_firework()
        if random.random() < 0.05:
            self.ambient_particles.append(
                SparkParticle(random.randint(0, config.WIDTH), config.HEIGHT + 10,
                    random.uniform(-0.5, 0.5), random.uniform(-3, -1), Colors.WHITE))
    
    def draw(self, screen):
        self.background.draw(screen)
        for ap in self.ambient_particles:
            ap.draw(screen)
        for fw in self.fireworks:
            fw.draw(screen)
        for te in self.text_effects:
            te.draw(screen)
        for fe in self.flash_effects:
            fe.draw(screen)
        self._draw_ui(screen)
    
    def _draw_ui(self, screen):
        font = pygame.font.Font(None, 24)
        small_font = pygame.font.Font(None, 20)
        elapsed = (pygame.time.get_ticks() - self.start_time) // 1000
        m, s = elapsed // 60, elapsed % 60
        lines = [
            f"🎆 烟花: {len(self.fireworks)}",
            f"📊 总计: {self.total_fireworks}",
            f"⏱️ {m:02d}:{s:02d}",
            f"🔄 FPS: {int(clock.get_fps())}"
        ]
        for i, line in enumerate(lines):
            ts = small_font.render(line, True, (182, 189, 197))
            screen.blit(ts, (12, 12 + i * 22))
        
        hints = [
            "🖱️ 左键: 发射",
            "🔄 滚轮: 切换",
            "⌨️ 1-0: 选型",
            "🗑️ C: 清除",
            "❌ ESC: 退出"
        ]
        for i, hint in enumerate(reversed(hints)):
            ts = small_font.render(hint, True, (139, 147, 155))
            tr = ts.get_rect()
            screen.blit(ts, (config.WIDTH - tr.width - 12, config.HEIGHT - 25 - i * 20))
        
        ct = selected_type
        type_surf = font.render(f"当前: {ct.value}", True, (201, 209, 217))
        screen.blit(type_surf, (config.WIDTH // 2 - type_surf.get_width() // 2, 12))
    
    def clear_all(self):
        self.fireworks.clear()
        self.text_effects.clear()
        self.flash_effects.clear()

# ============================================================
# 主程序
# ============================================================

def handle_input(event, ps):
    global selected_type
    if event.type == pygame.KEYDOWN:
        if event.key == pygame.K_ESCAPE:
            return False
        elif event.key == pygame.K_c:
            ps.clear_all()
            print("🧹 已清除所有烟花")
        key_map = {
            pygame.K_1: FireworkType.NORMAL,
            pygame.K_2: FireworkType.SPIRAL,
            pygame.K_3: FireworkType.HEART,
            pygame.K_4: FireworkType.RING,
            pygame.K_5: FireworkType.WILLOW,
            pygame.K_6: FireworkType.PALM,
            pygame.K_7: FireworkType.CROWN,
            pygame.K_8: FireworkType.MULTI_STAGE,
            pygame.K_9: FireworkType.COMET,
            pygame.K_0: FireworkType.BOUQUET,
        }
        if event.key in key_map:
            selected_type = key_map[event.key]
            print(f"🎯 切换到: {selected_type.value}")
    elif event.type == pygame.MOUSEBUTTONDOWN:
        if event.button == 1:
            x, y = event.pos
            ps.spawn_firework(x, y, selected_type)
            ps.flash_effects.append(FlashEffect(x, y, 0.5, 10))
        elif event.button == 4:
            types = list(FireworkType)
            idx = types.index(selected_type)
            selected_type = types[(idx + 1) % len(types)]
            print(f"🎯 切换到: {selected_type.value}")
        elif event.button == 5:
            types = list(FireworkType)
            idx = types.index(selected_type)
            selected_type = types[(idx - 1) % len(types)]
            print(f"🎯 切换到: {selected_type.value}")
    return True

def show_splash_screen(screen):
    font_title = pygame.font.Font(None, 60)
    font_sub = pygame.font.Font(None, 32)
    font_info = pygame.font.Font(None, 26)
    
    title = font_title.render("🎆 高级烟花模拟器", True, (255, 227, 178))
    subtitle = font_sub.render("Premium Firework Simulator v2.0", True, (183, 193, 211))
    info = font_info.render("点击任意键开始...", True, (134, 142, 159))
    
    tr = title.get_rect(center=(config.WIDTH//2, config.HEIGHT//2 - 50))
    sr = subtitle.get_rect(center=(config.WIDTH//2, config.HEIGHT//2 + 5))
    ir = info.get_rect(center=(config.WIDTH//2, config.HEIGHT//2 + 55))
    
    clock = pygame.time.Clock()
    start_time = pygame.time.get_ticks()
    waiting = True
    
    while waiting:
        current = pygame.time.get_ticks() - start_time
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                return False
            if (event.type == pygame.KEYDOWN or event.type == pygame.MOUSEBUTTONDOWN) and current > 650:
                waiting = False
        
        bg = Background()
        bg.draw(screen)
        
        breathe = 0.86 + 0.09 * math.sin(current * 0.002)
        # 手动缩放标题
        orig_w, orig_h = title.get_size()
        new_w, new_h = int(orig_w * breathe), int(orig_h * breathe)
        scaled_title = pygame.transform.scale(title, (new_w, new_h))
        str2 = scaled_title.get_rect(center=(config.WIDTH//2, config.HEIGHT//2 - 47))
        screen.blit(scaled_title, str2)
        
        screen.blit(subtitle, sr)
        
        if current > 850:
            blink = int(255 * (0.53 + 0.49 * math.sin(current * 0.003)))
            info.set_alpha(blink)
            screen.blit(info, ir)
        
        # 装饰烟花粒子
        if current % 67 == 0:
            dx = random.randint(180, config.WIDTH - 180)
            dy = random.randint(90, config.HEIGHT // 3)
            tf = NormalFirework(dx, dy)
            tf.num_particles = 25
            tf.explode()
            for p in tf.particles:
                p.life = 28
                p.max_life = 28
                p.draw(screen)
        
        pygame.display.flip()
        clock.tick(60)
    return True

def main():
    global clock, selected_type
    pygame.init()
    screen = pygame.display.set_mode((config.WIDTH, config.HEIGHT))
    pygame.display.set_caption("🎆 高级动态烟花模拟器 v2.0")
    
    try:
        icon = pygame.Surface((32, 32))
        icon.fill((0, 0, 15))
        pygame.draw.circle(icon, (255, 219, 39), (16, 16), 12)
        pygame.draw.circle(icon, (249, 89, 38), (16, 16), 8)
        pygame.draw.circle(icon, (226, 49, 51), (16, 16), 4)
        pygame.display.set_icon(icon)
    except:
        pass
    
    clock = pygame.time.Clock()
    selected_type = FireworkType.NORMAL
    
    if not show_splash_screen(screen):
        pygame.quit()
        return
    
    ps = ParticleSystem()
    
    print("\n" + "=" * 52)
    print("  🎆 欢迎使用高级动态烟花模拟器!")
    print("=" * 52)
    print("  🔥 正在准备烟花表演...")
    
    for i in range(5):
        x = random.randint(140, config.WIDTH - 140)
        y = random.randint(110, config.HEIGHT // 2 - 25)
        pygame.time.wait(320)
        ft = random.choice([FireworkType.NORMAL, FireworkType.RING, 
                           FireworkType.HEART, FireworkType.SPIRAL, FireworkType.CROWN])
        ps.spawn_firework(x, y, ft)
        ps.flash_effects.append(FlashEffect(x, y, 0.4, 8))
    
    print("  ✅ 准备就绪！尽情享受吧！")
    print("=" * 52)
    print("  🖱️ 左键发射 | 🔄 滚轮切换 | ⌨️ 1-0选型")
    print("  🗑️ C清除 | ❌ ESC退出")
    print("=" * 52 + "\n")
    
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            else:
                running = handle_input(event, ps)
                if not running:
                    break
        
        ps.update()
        screen.fill(config.BACKGROUND_COLOR)
        ps.draw(screen)
        pygame.display.flip()
        clock.tick(config.FPS)
    
    print("\n👋 感谢使用！再见！")
    pygame.quit()
    sys.exit()

# ============================================================
# 程序入口
# ============================================================

if __name__ == "__main__":
    clock = None
    selected_type = FireworkType.NORMAL
    try:
        main()
    except KeyboardInterrupt:
        print("\n\n⚠️ 程序被中断")
        pygame.quit()
        sys.exit(0)
    except Exception as e:
        print(f"\n❌ 错误: {e}")
        import traceback
        traceback.print_exc()
        pygame.quit()
        sys.exit(1)