import pygame
import random
import math

# 初始化pygame
pygame.init()
WIDTH, HEIGHT = 1100, 700
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame 动态烟花特效控制面板")
clock = pygame.time.Clock()

# 颜色常量
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GRAY = (60, 60, 60)
LIGHT_GRAY = (150, 150, 150)
BTN_COLOR = (40, 120, 200)
BTN_HOVER = (70, 160, 240)

# 全局参数（可通过面板修改）
GRAVITY = 0.12
PARTICLE_COUNT = 120
FIREWORK_SPEED = 7
PARTICLE_SIZE = 3

# 存储所有烟花与粒子
fireworks = []
particles = []

# 字体
font = pygame.font.SysFont("simhei", 16)
title_font = pygame.font.SysFont("simhei", 20, bold=True)

# 右侧控制面板区域
panel_rect = pygame.Rect(WIDTH - 220, 0, 220, HEIGHT)
buttons = []
slider_data = {
    "gravity": {"val": GRAVITY, "min": 0, "max": 0.3, "rect": pygame.Rect(WIDTH-210, 100, 180, 8)},
    "count": {"val": PARTICLE_COUNT, "min": 30, "max": 300, "rect": pygame.Rect(WIDTH-210, 160, 180, 8)},
    "speed": {"val": FIREWORK_SPEED, "min": 3, "max": 12, "rect": pygame.Rect(WIDTH-210, 220, 180, 8)},
    "size": {"val": PARTICLE_SIZE, "min": 1, "max": 6, "rect": pygame.Rect(WIDTH-210, 280, 180, 8)}
}
drag_slider = None

# 按钮定义
btn_clear = pygame.Rect(WIDTH-210, 350, 180, 32)
btn_auto = pygame.Rect(WIDTH-210, 400, 180, 32)
auto_fire = False


def random_color():
    """随机亮彩色"""
    return (
        random.randint(100, 255),
        random.randint(100, 255),
        random.randint(100, 255)
    )


class Firework:
    """升空烟花弹"""
    def __init__(self, target_x, target_y):
        self.x = random.randint(0, WIDTH - 220)
        self.y = HEIGHT
        self.target_x = target_x
        self.target_y = target_y
        self.speed = slider_data["speed"]["val"]
        self.color = random_color()
        self.exploded = False

    def update(self):
        dx = self.target_x - self.x
        dy = self.target_y - self.y
        dist = math.hypot(dx, dy)
        if dist < self.speed:
            self.explode()
            return False
        self.x += dx / dist * self.speed
        self.y += dy / dist * self.speed
        return True

    def draw(self):
        pygame.draw.circle(screen, self.color, (int(self.x), int(self.y)), 3)

    def explode(self):
        """炸开生成粒子"""
        cnt = int(slider_data["count"]["val"])
        angle_step = math.pi * 2 / cnt
        for i in range(cnt):
            angle = i * angle_step + random.uniform(-0.08, 0.08)
            spd = random.uniform(2, 5)
            vx = math.cos(angle) * spd
            vy = math.sin(angle) * spd
            particles.append(Particle(self.x, self.y, vx, vy, self.color))


class Particle:
    """爆炸粒子"""
    def __init__(self, x, y, vx, vy, color):
        self.x = x
        self.y = y
        self.vx = vx
        self.vy = vy
        self.color = color
        self.life = 255
        self.size = slider_data["size"]["val"]

    def update(self):
        self.vy += slider_data["gravity"]["val"]
        self.x += self.vx
        self.y += self.vy
        self.life -= 4
        return self.life > 0

    def draw(self):
        r, g, b = self.color
        clr = (r, g, b, self.life)
        surf = pygame.Surface((self.size*2, self.size*2), pygame.SRCALPHA)
        pygame.draw.circle(surf, clr, (self.size, self.size), self.size)
        screen.blit(surf, (self.x - self.size, self.y - self.size))


def draw_control_panel():
    """绘制右侧控制面板"""
    pygame.draw.rect(screen, GRAY, panel_rect)
    pygame.draw.rect(screen, LIGHT_GRAY, panel_rect, 2)

    # 标题
    title = title_font.render("烟花参数面板", True, WHITE)
    screen.blit(title, (WIDTH - 205, 30))

    # 滑块文本与绘制
    texts = [
        f"重力大小: {slider_data['gravity']['val']:.2f}",
        f"粒子数量: {int(slider_data['count']['val'])}",
        f"升空速度: {slider_data['speed']['val']:.1f}",
        f"粒子大小: {slider_data['size']['val']:.1f}"
    ]
    y_pos = 70
    keys = list(slider_data.keys())
    for idx, key in enumerate(keys):
        data = slider_data[key]
        # 文字
        txt = font.render(texts[idx], True, WHITE)
        screen.blit(txt, (WIDTH - 210, y_pos))
        y_pos += 30
        # 滑道
        r = data["rect"]
        pygame.draw.rect(screen, LIGHT_GRAY, r)
        # 滑块位置
        ratio = (data["val"] - data["min"]) / (data["max"] - data["min"])
        cx = r.x + r.w * ratio
        pygame.draw.circle(screen, BTN_HOVER, (cx, r.y + 4), 6)
        y_pos += 40

    # 按钮
    def draw_btn(rect, text, hover):
        col = BTN_HOVER if hover else BTN_COLOR
        pygame.draw.rect(screen, col, rect, border_radius=6)
        t = font.render(text, True, WHITE)
        tr = t.get_rect(center=rect.center)
        screen.blit(t, tr)

    mouse_pos = pygame.mouse.get_pos()
    draw_btn(btn_clear, "清空所有烟花", btn_clear.collidepoint(mouse_pos))
    draw_btn(btn_auto, "自动燃放【开启】" if auto_fire else "自动燃放【关闭】", btn_auto.collidepoint(mouse_pos))


def update_slider(mx):
    global drag_slider
    if drag_slider is None:
        return
    d = slider_data[drag_slider]
    r = d["rect"]
    # 限制在滑道内
    nx = max(r.x, min(mx, r.x + r.w))
    ratio = (nx - r.x) / r.w
    new_val = d["min"] + ratio * (d["max"] - d["min"])
    d["val"] = new_val
    # 同步全局变量
    if drag_slider == "gravity":
        global GRAVITY
        GRAVITY = new_val
    elif drag_slider == "count":
        global PARTICLE_COUNT
        PARTICLE_COUNT = int(new_val)
    elif drag_slider == "speed":
        global FIREWORK_SPEED
        FIREWORK_SPEED = new_val
    elif drag_slider == "size":
        global PARTICLE_SIZE
        PARTICLE_SIZE = new_val


running = True
while running:
    # 拖影黑色半透明，实现尾迹
    screen.fill((0, 0, 0, 35), special_flags=pygame.BLEND_RGBA_MIN)

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        # 鼠标点击发射烟花（不能点右侧面板）
        if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1:
            mx, my = event.pos
            if not panel_rect.collidepoint(mx, my):
                fireworks.append(Firework(mx, my))
            else:
                # 点击按钮
                if btn_clear.collidepoint(mx, my):
                    fireworks.clear()
                    particles.clear()
                if btn_auto.collidepoint(mx, my):
                    auto_fire = not auto_fire
                # 拖拽滑块
                for k in slider_data:
                    rt = slider_data[k]["rect"]
                    if rt.collidepoint(mx, my):
                        drag_slider = k

        if event.type == pygame.MOUSEMOTION and drag_slider is not None:
            update_slider(event.pos[0])

        if event.type == pygame.MOUSEBUTTONUP:
            drag_slider = None

    # 自动随机发射
    if auto_fire and random.random() < 0.035:
        tx = random.randint(20, WIDTH - 240)
        ty = random.randint(60, HEIGHT//2)
        fireworks.append(Firework(tx, ty))

    # 更新升空烟花
    new_fireworks = []
    for fw in fireworks:
        if fw.update():
            fw.draw()
            new_fireworks.append(fw)
    fireworks = new_fireworks

    # 更新爆炸粒子
    new_parts = []
    for p in particles:
        if p.update():
            p.draw()
            new_parts.append(p)
    particles = new_parts

    # 绘制控制面板
    draw_control_panel()

    pygame.display.flip()
    clock.tick(60)

pygame.quit()
