import pygame
import sys
import math
import random

# --- 初始化 ---
pygame.init()
WIDTH, HEIGHT = 600, 800
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Pygame 保龄球 - Arcade Style")
clock = pygame.time.Clock()

# 颜色
LANE_COLOR = (210, 180, 140)
LANE_LINE = (180, 150, 110)
GUTTER_COLOR = (80, 80, 80)
BALL_COLOR = (30, 30, 150)
PIN_COLOR = (255, 255, 255)
TEXT_COLOR = (50, 50, 50)
STRIKE_COLOR = (255, 215, 0)

FONT = pygame.font.SysFont("consolas", 40)
BIG_FONT = pygame.font.SysFont("consolas", 80)

# 配置
LANE_LEFT = 150
LANE_RIGHT = 450
LANE_TOP = 100
LANE_BOTTOM = 700
BALL_RADIUS = 20
PIN_RADIUS = 12
FRICTION = 0.98
PIN_FRICTION = 0.92
MAX_POWER = 15

# 球瓶初始位置（标准三角形）
PIN_POSITIONS = [
    (300, 200),  # 1号瓶
    (280, 170), (320, 170),  # 2,3
    (260, 140), (300, 140), (340, 140),  # 4,5,6
    (240, 110), (280, 110), (320, 110), (360, 110)  # 7,8,9,10
]

# --- 游戏对象 ---

class Ball:
    def __init__(self):
        self.x = 300
        self.y = 650
        self.vx = 0
        self.vy = 0
        self.spin = 0  # 旋转力：负=左旋，正=右旋
        self.active = False
        self.trail = []

    def reset(self):
        self.x = 300
        self.y = 650
        self.vx = 0
        self.vy = 0
        self.spin = 0
        self.active = False
        self.trail = []

    def throw(self, power, spin):
        self.vy = -power
        self.spin = spin
        self.active = True

    def update(self):
        if not self.active: return

        # 旋转影响水平速度
        self.vx += self.spin * 0.02
        self.x += self.vx
        self.y += self.vy

        # 摩擦力
        self.vx *= FRICTION
        self.vy *= FRICTION
        self.spin *= 0.99

        # 球沟判定
        if self.x - BALL_RADIUS < LANE_LEFT or self.x + BALL_RADIUS > LANE_RIGHT:
            self.active = False  # 进沟，结束

        # 出界
        if self.y < 50 or self.y > HEIGHT:
            self.active = False

        # 拖尾
        self.trail.append((self.x, self.y))
        if len(self.trail) > 20: self.trail.pop(0)

    def draw(self, surface):
        # 拖尾
        for i, (tx, ty) in enumerate(self.trail):
            alpha = int(255 * (i / len(self.trail)))
            r = int(BALL_RADIUS * (i / len(self.trail)))
            pygame.draw.circle(surface, (100, 100, 200), (int(tx), int(ty)), max(2, r))

        if self.active or not self.trail:
            pygame.draw.circle(surface, BALL_COLOR, (int(self.x), int(self.y)), BALL_RADIUS)
            pygame.draw.circle(surface, (255, 255, 255), (int(self.x), int(self.y)), BALL_RADIUS, 3)

class Pin:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.vx = 0
        self.vy = 0
        self.alive = True
        self.fall_timer = 0

    def update(self):
        if not self.alive: return

        self.x += self.vx
        self.y += self.vy
        self.vx *= PIN_FRICTION
        self.vy *= PIN_FRICTION

        # 出界判定
        if self.x < LANE_LEFT - 50 or self.x > LANE_RIGHT + 50 or self.y < 50:
            self.alive = False

        if self.fall_timer > 0: self.fall_timer -= 1

    def hit(self, vx, vy):
        self.vx += vx * 0.8
        self.vy += vy * 0.8
        self.fall_timer = 30

    def draw(self, surface):
        if not self.alive: return
        color = (200, 200, 200) if self.fall_timer > 0 else PIN_COLOR
        pygame.draw.circle(surface, color, (int(self.x), int(self.y)), PIN_RADIUS)
        pygame.draw.circle(surface, (200, 50, 50), (int(self.x), int(self.y)), PIN_RADIUS, 2)

# --- 碰撞检测 ---
def circle_collision(c1_x, c1_y, c1_r, c2_x, c2_y, c2_r):
    return math.hypot(c1_x - c2_x, c1_y - c2_y) < c1_r + c2_r

def resolve_ball_pin(ball, pin):
    if not pin.alive: return
    if not circle_collision(ball.x, ball.y, BALL_RADIUS, pin.x, pin.y, PIN_RADIUS): return

    dx = pin.x - ball.x
    dy = pin.y - ball.y
    dist = math.hypot(dx, dy)
    if dist == 0: return

    nx = dx / dist
    ny = dy / dist

    # 分离
    overlap = (BALL_RADIUS + PIN_RADIUS) - dist
    pin.x += nx * overlap
    pin.y += ny * overlap

    # 传递速度
    pin.hit(ball.vx, ball.vy)

def resolve_pin_pin(p1, p2):
    if not p1.alive or not p2.alive: return
    if not circle_collision(p1.x, p1.y, PIN_RADIUS, p2.x, p2.y, PIN_RADIUS): return

    dx = p2.x - p1.x
    dy = p2.y - p1.y
    dist = math.hypot(dx, dy)
    if dist == 0: return

    nx = dx / dist
    ny = dy / dist

    # 分离
    overlap = (PIN_RADIUS * 2) - dist
    p1.x -= nx * overlap / 2
    p1.y -= ny * overlap / 2
    p2.x += nx * overlap / 2
    p2.y += ny * overlap / 2

    # 交换速度（简化）
    p1.vx, p2.vx = p2.vx * 0.8, p1.vx * 0.8
    p1.vy, p2.vy = p2.vy * 0.8, p1.vy * 0.8

# --- 主程序 ---

def main():
    ball = Ball()
    pins = [Pin(x, y) for x, y in PIN_POSITIONS]

    # 游戏状态
    state = "AIM"  # AIM, POWER, SPIN, ROLL, RESULT
    aim_x = 300
    power = 0
    power_dir = 1
    spin = 0
    spin_dir = 1

    frame = 1
    roll = 0  # 当前局第几次投球
    score = 0
    frame_scores = []
    strike_flash = 0

    running = True
    while running:
        clock.tick(60)
        for event in pygame.event.get():
            if event.type == pygame.QUIT: pygame.quit(); sys.exit()
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_ESCAPE: pygame.quit(); sys.exit()
                if event.key == pygame.K_SPACE:
                    if state == "AIM":
                        state = "POWER"
                        power = 0
                        power_dir = 1
                    elif state == "POWER":
                        state = "SPIN"
                        spin = 0
                        spin_dir = 1
                    elif state == "SPIN":
                        ball.x = aim_x
                        ball.throw(power, spin)
                        state = "ROLL"
                    elif state == "RESULT":
                        # 下一投或下一局
                        if roll == 0 and all(not p.alive for p in pins):
                            # Strike
                            frame_scores.append(10)
                            score += 10
                            strike_flash = 60
                            roll = 1
                            # 重置球瓶
                            pins = [Pin(x, y) for x, y in PIN_POSITIONS]
                        elif roll == 1 and all(not p.alive for p in pins):
                            # Spare
                            frame_scores.append(10)
                            score += 10
                            roll = 0
                            frame += 1
                            pins = [Pin(x, y) for x, y in PIN_POSITIONS]
                        elif roll == 0:
                            # 第一次没全中
                            roll = 1
                        else:
                            # 第二次结束
                            knocked = sum(1 for p in pins if not p.alive)
                            frame_scores.append(knocked)
                            score += knocked
                            roll = 0
                            frame += 1
                            pins = [Pin(x, y) for x, y in PIN_POSITIONS]

                        if frame > 10:
                            state = "GAMEOVER"
                        else:
                            ball.reset()
                            state = "AIM"

                if event.key == pygame.K_r and state == "GAMEOVER":
                    frame = 1
                    roll = 0
                    score = 0
                    frame_scores = []
                    pins = [Pin(x, y) for x, y in PIN_POSITIONS]
                    ball.reset()
                    state = "AIM"

        # 更新逻辑
        if state == "AIM":
            keys = pygame.key.get_pressed()
            if keys[pygame.K_LEFT]: aim_x = max(LANE_LEFT + BALL_RADIUS, aim_x - 3)
            if keys[pygame.K_RIGHT]: aim_x = min(LANE_RIGHT - BALL_RADIUS, aim_x + 3)

        elif state == "POWER":
            power += power_dir * 0.3
            if power >= MAX_POWER: power_dir = -1
            if power <= 0: power_dir = 1

        elif state == "SPIN":
            spin += spin_dir * 0.2
            if spin >= 3: spin_dir = -1
            if spin <= -3: spin_dir = 1

        elif state == "ROLL":
            ball.update()
            for p in pins: p.update()

            # 球撞瓶
            for p in pins: resolve_ball_pin(ball, p)

            # 瓶撞瓶
            for i in range(len(pins)):
                for j in range(i + 1, len(pins)):
                    resolve_pin_pin(pins[i], pins[j])

            # 球停或出界 -> 结果
            if not ball.active or (abs(ball.vx) < 0.1 and abs(ball.vy) < 0.1):
                state = "RESULT"

        if strike_flash > 0: strike_flash -= 1

        # --- 绘图 ---
        screen.fill((100, 100, 100))

        # 球沟
        pygame.draw.rect(screen, GUTTER_COLOR, (0, LANE_TOP, LANE_LEFT, LANE_BOTTOM - LANE_TOP))
        pygame.draw.rect(screen, GUTTER_COLOR, (LANE_RIGHT, LANE_TOP, WIDTH - LANE_RIGHT, LANE_BOTTOM - LANE_TOP))

        # 球道
        pygame.draw.rect(screen, LANE_COLOR, (LANE_LEFT, LANE_TOP, LANE_RIGHT - LANE_LEFT, LANE_BOTTOM - LANE_TOP))

        # 球道线
        for x in range(LANE_LEFT, LANE_RIGHT, 30):
            pygame.draw.line(screen, LANE_LINE, (x, LANE_TOP), (x, LANE_BOTTOM), 1)

        # 瞄准点
        if state == "AIM":
            pygame.draw.circle(screen, (255, 100, 100), (int(aim_x), 650), BALL_RADIUS, 3)
            pygame.draw.line(screen, (255, 100, 100), (int(aim_x), 650), (int(aim_x), 200), 2)

        # 力度条
        if state == "POWER":
            bar_h = int((power / MAX_POWER) * 200)
            pygame.draw.rect(screen, (100, 100, 100), (50, 500, 30, 200))
            pygame.draw.rect(screen, (255, 50, 50), (50, 700 - bar_h, 30, bar_h))
            txt = FONT.render("POWER", True, TEXT_COLOR)
            screen.blit(txt, (30, 470))

        # 旋转条
        if state == "SPIN":
            bar_w = int((abs(spin) / 3) * 200)
            pygame.draw.rect(screen, (100, 100, 100), (200, 750, 200, 20))
            color = (50, 50, 255) if spin < 0 else (255, 50, 50)
            if spin < 0:
                pygame.draw.rect(screen, color, (300 - bar_w, 750, bar_w, 20))
            else:
                pygame.draw.rect(screen, color, (300, 750, bar_w, 20))
            txt = FONT.render("SPIN", True, TEXT_COLOR)
            screen.blit(txt, (270, 720))

        # 球 & 瓶
        ball.draw(screen)
        for p in pins: p.draw(screen)

        # UI
        score_txt = FONT.render(f"Frame: {min(frame, 10)} | Score: {score}", True, TEXT_COLOR)
        screen.blit(score_txt, (20, 20))

        hint = FONT.render("SPACE: Confirm | R: Restart", True, (150, 150, 150))
        screen.blit(hint, (150, HEIGHT - 40))

        # 全中闪光
        if strike_flash > 0:
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((255, 215, 0, strike_flash * 4))
            screen.blit(overlay, (0, 0))
            txt = BIG_FONT.render("STRIKE!", True, STRIKE_COLOR)
            screen.blit(txt, txt.get_rect(center=(WIDTH // 2, HEIGHT // 2)))

        if state == "GAMEOVER":
            overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 180))
            screen.blit(overlay, (0, 0))
            txt = BIG_FONT.render(f"FINAL: {score}", True, (255, 255, 255))
            sub = FONT.render("Press R to Restart", True, (200, 200, 200))
            screen.blit(txt, txt.get_rect(center=(WIDTH // 2, HEIGHT // 2 - 40)))
            screen.blit(sub, sub.get_rect(center=(WIDTH // 2, HEIGHT // 2 + 40)))

        pygame.display.flip()

    pygame.quit()

if __name__ == "__main__":
    main()