# GESP等级：二级 | GESP Python 二级考点
"""
ball.py - Ball类
弹球大作战核心游戏对象
OOP设计：封装了弹球的所有物理属性与行为
"""

import math
import random


class Ball:
    """
    弹球类

    属性:
        x, y: 圆心坐标
        vx, vy: 速度向量
        radius: 半径
        color: RGB颜色元组
        base_speed: 基准速度标量
        active: 是否已发射（False时跟随挡板）
        trail: 轨迹点列表（三级功能特效）

    方法覆盖:
        基础移动/绘制/碰撞检测
        速度/颜色调整（二级功能）
        特效/分身支持（三级功能）
    """

    # 预置颜色方案
    COLORS = {
        "white": (255, 255, 255),
        "red": (255, 50, 50),
        "blue": (50, 100, 255),
        "green": (50, 255, 50),
        "yellow": (255, 255, 50),
        "cyan": (50, 255, 255),
        "magenta": (255, 50, 255),
        "orange": (255, 165, 50),
        "purple": (180, 50, 255),
        "pink": (255, 150, 200),
    }

    # 颜色名称列表（用于循环切换）
    COLOR_NAMES = list(COLORS.keys())

    def __init__(self, x, y, radius=10, color_name="white", speed=5):
        """
        初始化弹球

        参数:
            x, y: 初始位置
            radius: 球半径
            color_name: 颜色名称（从COLORS字典取）
            speed: 基准速率
        """
        self.start_x = x
        self.start_y = y
        self.x = x
        self.y = y
        self.radius = radius
        self.base_speed = speed
        self.color_name = color_name
        self.color = self.COLORS[color_name]
        self.active = False  # False时粘在挡板上

        # 速度初始化（随机水平方向）
        self.vx = speed * random.uniform(-0.8, 0.8)
        self.vy = -speed

        # 确保最小水平速度
        if abs(self.vx) < speed * 0.3:
            self.vx = speed * 0.3 if random.random() > 0.5 else -speed * 0.3

        # === 三级功能扩展属性 ===
        self.trail_enabled = False       # 轨迹特效开关
        self.trail_points = []            # 轨迹点列表
        self.trail_max_length = 15        # 最大轨迹点数
        self.is_clone = False             # 是否为分身球
        self.glow_enabled = False         # 发光特效

    def set_speed(self, speed):
        """调整球速（二级功能），保持当前方向比例"""
        if speed <= 0:
            return
        current_speed = math.hypot(self.vx, self.vy)
        if current_speed > 0:
            ratio = speed / current_speed
            self.vx *= ratio
            self.vy *= ratio
        self.base_speed = speed

    def set_color_by_name(self, color_name):
        """按名称设置颜色（二级功能）"""
        if color_name in self.COLORS:
            self.color_name = color_name
            self.color = self.COLORS[color_name]

    def cycle_color(self, direction=1):
        """循环切换颜色（二级功能）"""
        idx = self.COLOR_NAMES.index(self.color_name)
        idx = (idx + direction) % len(self.COLOR_NAMES)
        self.set_color_by_name(self.COLOR_NAMES[idx])
        return self.color_name

    def launch(self):
        """发射弹球"""
        if not self.active:
            self.active = True

    def move(self):
        """更新位置（每帧调用）"""
        if not self.active:
            return

        self.x += self.vx
        self.y += self.vy

        # 轨迹记录（三级功能）
        if self.trail_enabled:
            self.trail_points.append((int(self.x), int(self.y)))
            if len(self.trail_points) > self.trail_max_length:
                self.trail_points.pop(0)

    def draw(self, screen):
        """
        绘制弹球

        参数:
            screen: pygame.Surface 对象
        """
        # 绘制轨迹（三级功能）
        if self.trail_enabled and len(self.trail_points) > 1:
            for i, point in enumerate(self.trail_points):
                alpha = int(255 * (i + 1) / len(self.trail_points))
                trail_color = tuple(max(0, c * alpha // 255) for c in self.color)
                pygame.draw.circle(
                    screen,
                    trail_color,
                    point,
                    max(1, int(self.radius * 0.5 * (i + 1) / len(self.trail_points)))
                )

        # 发光效果（三级功能）
        if self.glow_enabled:
            glow_surf = pygame.Surface(
                (self.radius * 6, self.radius * 6), pygame.SRCALPHA
            )
            for g in range(3, 0, -1):
                alpha = 30 - g * 8
                r = int(self.radius * (g * 1.2))
                pygame.draw.circle(
                    glow_surf,
                    (*self.color[:3], max(0, alpha)),
                    (self.radius * 3, self.radius * 3),
                    r,
                )
            screen.blit(
                glow_surf,
                (self.x - self.radius * 3, self.y - self.radius * 3),
            )

        # 球体本体
        pygame.draw.circle(
            screen,
            self.color,
            (int(self.x), int(self.y)),
            self.radius,
        )

        # 高光效果
        highlight_x = int(self.x - self.radius * 0.3)
        highlight_y = int(self.y - self.radius * 0.3)
        pygame.draw.circle(
            screen,
            (255, 255, 255, 80),
            (highlight_x, highlight_y),
            int(self.radius * 0.35),
        )

    def bounce_wall(self, screen_width, screen_height):
        """
        墙壁碰撞检测与反弹

        返回: bool 是否发生了碰撞
        """
        bounced = False

        # 左右墙
        if self.x - self.radius <= 0:
            self.x = self.radius
            self.vx = abs(self.vx)
            bounced = True
        elif self.x + self.radius >= screen_width:
            self.x = screen_width - self.radius
            self.vx = -abs(self.vx)
            bounced = True

        # 上墙
        if self.y - self.radius <= 0:
            self.y = self.radius
            self.vy = abs(self.vy)
            bounced = True

        return bounced

    def bounce_paddle(self, paddle_rect):
        """
        挡板碰撞检测与反弹

        根据碰撞位置计算反弹角度：
        - 撞到边缘 → 大角度侧飞
        - 撞到中心 → 垂直上弹

        参数:
            paddle_rect: pygame.Rect 挡板矩形

        返回: bool 是否碰撞
        """
        if not self.active:
            return False

        # 粗略碰撞盒检测
        if (self.vy <= 0 or
            self.y + self.radius < paddle_rect.top or
            self.y + self.radius > paddle_rect.bottom + 15 or
            self.x + self.radius < paddle_rect.left or
            self.x - self.radius > paddle_rect.right):
            return False

        # 计算碰撞位置比例 0~1
        hit_pos = (self.x - paddle_rect.left) / paddle_rect.width
        hit_pos = max(0.0, min(1.0, hit_pos))

        # 映射到 -60° ~ +60° 的发射角
        angle_deg = (hit_pos - 0.5) * 130  # -65° ~ +65°
        angle_rad = math.radians(angle_deg)

        current_speed = math.hypot(self.vx, self.vy)
        new_vx = current_speed * math.sin(angle_rad)
        new_vy = -current_speed * math.cos(angle_rad)

        # 保证最小向上速度（防止水平弹跳）
        if abs(new_vy) < current_speed * 0.25:
            new_vy = -current_speed * 0.25
            # 重新分配水平分量
            remaining = math.sqrt(current_speed**2 - new_vy**2)
            new_vx = remaining * (1 if new_vx >= 0 else -1)

        self.vx = new_vx
        self.vy = new_vy
        self.y = paddle_rect.top - self.radius
        return True

    def bounce_brick(self, brick_rect):
        """
        砖块碰撞检测与反弹

        使用最小重叠法判断碰撞方向

        参数:
            brick_rect: pygame.Rect 砖块矩形

        返回: str or None
            'side' - 左右碰撞
            'top_bottom' - 上下碰撞
            None - 未碰撞
        """
        if not self.active:
            return None

        # 找砖块上离球心最近的点
        closest_x = max(brick_rect.left, min(self.x, brick_rect.right))
        closest_y = max(brick_rect.top, min(self.y, brick_rect.bottom))

        dist_x = self.x - closest_x
        dist_y = self.y - closest_y

        # 距离平方 < 半径平方 → 碰撞
        if dist_x * dist_x + dist_y * dist_y >= self.radius * self.radius:
            return None

        # 计算四个方向的重叠量
        overlap_left = (self.x + self.radius) - brick_rect.left
        overlap_right = brick_rect.right - (self.x - self.radius)
        overlap_top = (self.y + self.radius) - brick_rect.top
        overlap_bottom = brick_rect.bottom - (self.y - self.radius)

        min_overlap = min(overlap_left, overlap_right, overlap_top, overlap_bottom)

        if min_overlap in (overlap_left, overlap_right):
            self.vx = -self.vx
            # 推开球避免卡住
            if overlap_left < overlap_right:
                self.x = brick_rect.left - self.radius
            else:
                self.x = brick_rect.right + self.radius
            return 'side'
        else:
            self.vy = -self.vy
            if overlap_top < overlap_bottom:
                self.y = brick_rect.top - self.radius
            else:
                self.y = brick_rect.bottom + self.radius
            return 'top_bottom'

    def is_off_screen(self, screen_height):
        """检查球是否掉出屏幕底部"""
        return self.y - self.radius > screen_height

    def reset(self, x=None, y=None):
        """
        重置弹球到初始状态

        参数:
            x, y: 重置位置（None则使用start_x, start_y）
        """
        self.x = x if x is not None else self.start_x
        self.y = y if y is not None else self.start_y
        self.active = False
        self.trail_points.clear()
        self.vx = self.base_speed * random.uniform(-0.8, 0.8)
        self.vy = -self.base_speed
        if abs(self.vx) < self.base_speed * 0.3:
            self.vx = self.base_speed * (0.3 if random.random() > 0.5 else -0.3)

    def clone(self, x, y):
        """
        创建分身球（三级功能-多球）

        返回: Ball 新球实例，速度方向随机偏转
        """
        new_ball = Ball(x, y, self.radius, self.color_name, self.base_speed)
        new_ball.active = True
        # 随机偏转 ±30°
        angle_offset = math.radians(random.uniform(-30, 30))
        cos_a, sin_a = math.cos(angle_offset), math.sin(angle_offset)
        new_ball.vx = self.vx * cos_a - self.vy * sin_a
        new_ball.vy = self.vx * sin_a + self.vy * cos_a
        new_ball.is_clone = True
        new_ball.trail_enabled = self.trail_enabled
        new_ball.glow_enabled = self.glow_enabled
        return new_ball


# 为 IDE 类型提示导出
if __name__ == "__main__":
    print("Ball class loaded. Import this module in your game.")
    print(f"Available colors: {', '.join(Ball.COLOR_NAMES)}")
