import pygame
import random
import sys
import math

pygame.init()

SCREEN_WIDTH = 480
SCREEN_HEIGHT = 720
FPS = 60

WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (100, 100, 100)
DARK_GRAY = (60, 60, 60)
RED = (220, 50, 50)
BLUE = (50, 100, 220)
GREEN = (50, 200, 50)
YELLOW = (255, 220, 0)
ORANGE = (255, 140, 0)
ROAD_COLOR = (70, 70, 80)
GRASS_COLOR = (40, 120, 40)
LINE_COLOR = (220, 220, 220)
STRIPE_COLOR = (200, 180, 50)

ROAD_LEFT = 80
ROAD_RIGHT = 400
ROAD_WIDTH = ROAD_RIGHT - ROAD_LEFT
LANE_WIDTH = ROAD_WIDTH // 3
LANE_COUNT = 3


class PlayerCar:
    def __init__(self):
        self.width = 42
        self.height = 72
        self.x = SCREEN_WIDTH // 2 - self.width // 2
        self.y = SCREEN_HEIGHT - self.height - 30
        self.speed = 5
        self.lives = 3
        self.invincible = 0 
        self.tilt = 0

    def move(self, keys):
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            self.x -= self.speed
            self.tilt = max(self.tilt - 1, -8)
        elif keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            self.x += self.speed
            self.tilt = min(self.tilt + 1, 8)
        else:
            if self.tilt > 0:
                self.tilt -= 1
            elif self.tilt < 0:
                self.tilt += 1

        self.x = max(ROAD_LEFT + 5, min(self.x, ROAD_RIGHT - self.width - 5))

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

    def draw(self, surface):
        if self.invincible > 0 and (self.invincible // 3) % 2 == 0:
            return

        cx = self.x + self.width // 2
        cy = self.y + self.height // 2

        body_rect = pygame.Rect(self.x + 4, self.y + 10, self.width - 8, self.height - 15)
        pygame.draw.rect(surface, BLUE, body_rect, border_radius=8)

        top_rect = pygame.Rect(self.x + 8, self.y + 20, self.width - 16, 25)
        pygame.draw.rect(surface, (30, 80, 200), top_rect, border_radius=5)

        glass_rect = pygame.Rect(self.x + 10, self.y + 14, self.width - 20, 14)
        pygame.draw.rect(surface, (150, 200, 255), glass_rect, border_radius=3)

        wheel_positions = [
            (self.x + 2, self.y + 14),
            (self.x + 2, self.y + self.height - 18),
            (self.x + self.width - 7, self.y + 14),
            (self.x + self.width - 7, self.y + self.height - 18),
        ]
        for wx, wy in wheel_positions:
            pygame.draw.rect(surface, DARK_GRAY, (wx, wy, 5, 10), border_radius=2)

        pygame.draw.rect(surface, YELLOW, (self.x + 8, self.y + 8, 6, 4), border_radius=2)
        pygame.draw.rect(surface, YELLOW, (self.x + self.width - 14, self.y + 8, 6, 4), border_radius=2)

        pygame.draw.rect(surface, RED, (self.x + 8, self.y + self.height - 8, 6, 4), border_radius=2)
        pygame.draw.rect(surface, RED, (self.x + self.width - 14, self.y + self.height - 8, 6, 4), border_radius=2)

    def get_rect(self):
        return pygame.Rect(self.x + 6, self.y + 8, self.width - 12, self.height - 12)


class Obstacle:

    COLORS = [
        (220, 50, 50),  
        (50, 180, 50),   
        (200, 100, 200), 
        (255, 165, 0),   
        (0, 180, 180),   
        (180, 180, 50),  
    ]

    def __init__(self, speed, y=None):
        self.width = 40
        self.height = 68
        lane = random.randint(0, LANE_COUNT - 1)
        self.x = ROAD_LEFT + lane * LANE_WIDTH + (LANE_WIDTH - self.width) // 2
        self.y = y if y is not None else -self.height - random.randint(0, 100)
        self.speed = speed + random.uniform(-1, 1)
        self.color = random.choice(self.COLORS)
        self.lane_change_timer = random.randint(60, 180)

    def update(self):
        self.y += self.speed
        self.lane_change_timer -= 1

    def draw(self, surface):
        body_rect = pygame.Rect(self.x + 3, self.y + 8, self.width - 6, self.height - 14)
        pygame.draw.rect(surface, self.color, body_rect, border_radius=7)

        top_color = tuple(max(0, c - 40) for c in self.color)
        top_rect = pygame.Rect(self.x + 7, self.y + 22, self.width - 14, 20)
        pygame.draw.rect(surface, top_color, top_rect, border_radius=4)

        glass_rect = pygame.Rect(self.x + 9, self.y + self.height - 24, self.width - 18, 12)
        pygame.draw.rect(surface, (150, 200, 255), glass_rect, border_radius=3)

        wheel_positions = [
            (self.x + 1, self.y + 12),
            (self.x + 1, self.y + self.height - 16),
            (self.x + self.width - 6, self.y + 12),
            (self.x + self.width - 6, self.y + self.height - 16),
        ]
        for wx, wy in wheel_positions:
            pygame.draw.rect(surface, DARK_GRAY, (wx, wy, 5, 10), border_radius=2)

        pygame.draw.rect(surface, (255, 50, 50), (self.x + 7, self.y + self.height - 8, 6, 4), border_radius=2)
        pygame.draw.rect(surface, (255, 50, 50), (self.x + self.width - 13, self.y + self.height - 8, 6, 4), border_radius=2)

    def get_rect(self):
        return pygame.Rect(self.x + 5, self.y + 8, self.width - 10, self.height - 12)

    def is_off_screen(self):
        return self.y > SCREEN_HEIGHT + 50


class RoadBarrier:
    def __init__(self, speed):
        self.width = 22
        self.height = 28
        lane = random.randint(0, LANE_COUNT - 1)
        self.x = ROAD_LEFT + lane * LANE_WIDTH + (LANE_WIDTH - self.width) // 2
        self.y = -self.height - random.randint(0, 60)
        self.speed = speed

    def update(self):
        self.y += self.speed

    def draw(self, surface):
        points = [
            (self.x + self.width // 2, self.y),
            (self.x + self.width - 2, self.y + self.height),
            (self.x + 2, self.y + self.height),
        ]
        pygame.draw.polygon(surface, ORANGE, points)
        stripe_y = self.y + self.height * 0.4
        stripe_points = [
            (self.x + self.width // 2 - 3, stripe_y),
            (self.x + self.width // 2 + 3, stripe_y),
            (self.x + self.width // 2 + 5, stripe_y + 5),
            (self.x + self.width // 2 - 5, stripe_y + 5),
        ]
        pygame.draw.polygon(surface, WHITE, stripe_points)

    def get_rect(self):
        return pygame.Rect(self.x + 3, self.y + 5, self.width - 6, self.height - 5)

    def is_off_screen(self):
        return self.y > SCREEN_HEIGHT + 50


class Particle:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        angle = random.uniform(0, 2 * math.pi)
        speed = random.uniform(2, 7)
        self.vx = math.cos(angle) * speed
        self.vy = math.sin(angle) * speed
        self.life = random.randint(15, 35)
        self.color = random.choice([RED, ORANGE, YELLOW, WHITE])
        self.size = random.randint(2, 5)

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.vy += 0.1 
        self.life -= 1
        self.size = max(1, self.size - 0.1)

    def draw(self, surface):
        if self.life > 0:
            alpha = min(255, self.life * 10)
            pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), int(self.size))

    def is_dead(self):
        return self.life <= 0


class Game:

    def __init__(self):
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("无尽驾驶 - Endless Racing")
        self.clock = pygame.time.Clock()
        self.font_large = pygame.font.SysFont("simhei", 48, bold=True)
        self.font_medium = pygame.font.SysFont("simhei", 28)
        self.font_small = pygame.font.SysFont("simhei", 20)
        self.font_tiny = pygame.font.SysFont("simhei", 16)
        self.reset()

    def reset(self):
        self.player = PlayerCar()
        self.obstacles = []
        self.particles = []
        self.score = 0
        self.high_score = getattr(self, 'high_score', 0)
        self.game_speed = 4.0
        self.road_offset = 0
        self.spawn_timer = 0
        self.spawn_interval = 90
        self.barrier_timer = 0
        self.state = "menu"
        self.distance = 0
        self.speed_boost_timer = 0
        self.tree_offsets = [(random.randint(0, 65), random.randint(0, SCREEN_HEIGHT)) for _ in range(12)]
        self.tree_offsets += [(random.randint(ROAD_RIGHT + 5, SCREEN_WIDTH - 15), random.randint(0, SCREEN_HEIGHT)) for _ in range(12)]

    def spawn_obstacles(self):
        self.spawn_timer += 1
        self.barrier_timer += 1

        if self.spawn_timer >= self.spawn_interval:
            self.spawn_timer = 0
            count = 1 if random.random() < 0.7 else 2
            occupied_lanes = set()
            for _ in range(count):
                obs = Obstacle(self.game_speed)
                lane = (obs.x - ROAD_LEFT) // LANE_WIDTH
                if lane not in occupied_lanes:
                    occupied_lanes.add(lane)
                    self.obstacles.append(obs)
            if len(occupied_lanes) >= LANE_COUNT:
                if self.obstacles:
                    self.obstacles.pop()

        if self.barrier_timer >= self.spawn_interval * 2 + random.randint(0, 60):
            self.barrier_timer = 0
            if random.random() < 0.4:
                self.obstacles.append(RoadBarrier(self.game_speed))

    def update(self):
        if self.state != "playing":
            return

        keys = pygame.key.get_pressed()
        self.player.move(keys)

        self.distance += self.game_speed
        self.score = int(self.distance / 10)

        self.game_speed = 4.0 + self.distance / 3000
        self.game_speed = min(self.game_speed, 14)

        self.road_offset = (self.road_offset + self.game_speed) % 40

        for i in range(len(self.tree_offsets)):
            x, y = self.tree_offsets[i]
            y += self.game_speed * 0.8
            if y > SCREEN_HEIGHT + 20:
                y = -20
                if x < ROAD_LEFT:
                    x = random.randint(5, 65)
                else:
                    x = random.randint(ROAD_RIGHT + 5, SCREEN_WIDTH - 15)
            self.tree_offsets[i] = (x, y)

        self.spawn_obstacles()

        for obs in self.obstacles:
            obs.update()

        self.obstacles = [obs for obs in self.obstacles if not obs.is_off_screen()]

        for p in self.particles:
            p.update()
        self.particles = [p for p in self.particles if not p.is_dead()]

        player_rect = self.player.get_rect()
        for obs in self.obstacles:
            if player_rect.colliderect(obs.get_rect()):
                if self.player.invincible <= 0:
                    self.player.lives -= 1
                    self.player.invincible = 90
                    cx = (player_rect.centerx + obs.get_rect().centerx) // 2
                    cy = (player_rect.centery + obs.get_rect().centery) // 2
                    for _ in range(20):
                        self.particles.append(Particle(cx, cy))
                    self.obstacles.remove(obs)
                    if self.player.lives <= 0:
                        self.state = "gameover"
                        self.high_score = max(self.high_score, self.score)
                    break

        self.spawn_interval = max(30, int(90 - self.game_speed * 5))

    def draw_road(self):
        self.screen.fill(GRASS_COLOR)

        pygame.draw.rect(self.screen, STRIPE_COLOR, (ROAD_LEFT - 10, 0, 10, SCREEN_HEIGHT))
        pygame.draw.rect(self.screen, STRIPE_COLOR, (ROAD_RIGHT, 0, 10, SCREEN_HEIGHT))

        pygame.draw.rect(self.screen, ROAD_COLOR, (ROAD_LEFT, 0, ROAD_WIDTH, SCREEN_HEIGHT))

        for i in range(1, LANE_COUNT):
            x = ROAD_LEFT + i * LANE_WIDTH
            for y_offset in range(-40, SCREEN_HEIGHT + 40, 40):
                y = y_offset + self.road_offset
                pygame.draw.rect(self.screen, LINE_COLOR, (x - 1, y, 3, 20))

        pygame.draw.rect(self.screen, WHITE, (ROAD_LEFT, 0, 3, SCREEN_HEIGHT))
        pygame.draw.rect(self.screen, WHITE, (ROAD_RIGHT - 3, 0, 3, SCREEN_HEIGHT))

        for tx, ty in self.tree_offsets:
            pygame.draw.rect(self.screen, (100, 70, 30), (tx + 4, ty + 12, 6, 10))
            pygame.draw.circle(self.screen, (30, 100, 30), (tx + 7, ty + 8), 10)
            pygame.draw.circle(self.screen, (40, 130, 40), (tx + 7, ty + 5), 7)

    def draw_hud(self):
        score_text = self.font_small.render(f"分数: {self.score}", True, WHITE)
        self.screen.blit(score_text, (10, 10))
        
        speed_display = int(self.game_speed * 20)
        speed_text = self.font_small.render(f"速度: {speed_display} km/h", True, WHITE)
        self.screen.blit(speed_text, (10, 35))

        for i in range(self.player.lives):
            x = SCREEN_WIDTH - 35 - i * 30
            pygame.draw.polygon(self.screen, RED, [
                (x + 8, y + 5) for y in [0, -5, -12, -5]
            ] if False else self._heart_points(x, 10))

        if self.high_score > 0:
            hs_text = self.font_tiny.render(f"最高分: {self.high_score}", True, YELLOW)
            self.screen.blit(hs_text, (10, 60))

    def _heart_points(self, x, y):
        points = []
        for t in range(100):
            angle = t / 100 * 2 * math.pi
            hx = 8 * (math.sin(angle) ** 3)
            hy = -(6.5 * math.cos(angle) - 2.5 * math.cos(2 * angle) - 1 * math.cos(3 * angle) - 0.5 * math.cos(4 * angle))
            points.append((x + hx, y + hy * 0.8))
        return points

    def draw_hud_simple(self):
        score_text = self.font_small.render(f"分数: {self.score}", True, WHITE)
        self.screen.blit(score_text, (10, 10))

        speed_display = int(self.game_speed * 20)
        speed_text = self.font_small.render(f"{speed_display} km/h", True, WHITE)
        self.screen.blit(speed_text, (10, 35))

        for i in range(self.player.lives):
            x = SCREEN_WIDTH - 30 - i * 25
            pygame.draw.rect(self.screen, RED, (x, 10, 18, 18), border_radius=3)
            pygame.draw.rect(self.screen, WHITE, (x + 7, 13, 4, 12))
            pygame.draw.rect(self.screen, WHITE, (x + 5, 15, 8, 8))

        if self.high_score > 0:
            hs_text = self.font_tiny.render(f"最高分: {self.high_score}", True, YELLOW)
            self.screen.blit(hs_text, (10, 60))

    def draw_menu(self):
        self.draw_road()

        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        overlay.set_alpha(150)
        overlay.fill(BLACK)
        self.screen.blit(overlay, (0, 0))

        title = self.font_large.render("无尽驾驶", True, YELLOW)
        title_rect = title.get_rect(center=(SCREEN_WIDTH // 2, 200))
        self.screen.blit(title, title_rect)

        sub = self.font_medium.render("Endless Racing", True, WHITE)
        sub_rect = sub.get_rect(center=(SCREEN_WIDTH // 2, 260))
        self.screen.blit(sub, sub_rect)

        pygame.draw.line(self.screen, YELLOW, (SCREEN_WIDTH // 2 - 100, 290), (SCREEN_WIDTH // 2 + 100, 290), 2)

        instructions = [
            "← → / A D  左右移动",
            "躲避障碍车辆和路障",
            "坚持越久，分数越高！",
            "",
            "按 ENTER 开始游戏",
        ]
        for i, text in enumerate(instructions):
            color = YELLOW if i == len(instructions) - 1 else WHITE
            txt = self.font_small.render(text, True, color)
            rect = txt.get_rect(center=(SCREEN_WIDTH // 2, 340 + i * 35))
            self.screen.blit(txt, rect)

    def draw_gameover(self):
        overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT))
        overlay.set_alpha(160)
        overlay.fill(BLACK)
        self.screen.blit(overlay, (0, 0))

        go_text = self.font_large.render("游戏结束", True, RED)
        go_rect = go_text.get_rect(center=(SCREEN_WIDTH // 2, 220))
        self.screen.blit(go_text, go_rect)

        score_text = self.font_medium.render(f"得分: {self.score}", True, WHITE)
        score_rect = score_text.get_rect(center=(SCREEN_WIDTH // 2, 300))
        self.screen.blit(score_text, score_rect)

        hs_text = self.font_medium.render(f"最高分: {self.high_score}", True, YELLOW)
        hs_rect = hs_text.get_rect(center=(SCREEN_WIDTH // 2, 350))
        self.screen.blit(hs_text, hs_rect)

        speed_display = int(self.game_speed * 20)
        sp_text = self.font_small.render(f"最终速度: {speed_display} km/h", True, GRAY)
        sp_rect = sp_text.get_rect(center=(SCREEN_WIDTH // 2, 400))
        self.screen.blit(sp_text, sp_rect)

        hint = self.font_small.render("按 ENTER 重新开始", True, YELLOW)
        hint_rect = hint.get_rect(center=(SCREEN_WIDTH // 2, 470))

        if (pygame.time.get_ticks() // 500) % 2 == 0:
            self.screen.blit(hint, hint_rect)

    def run(self):
        running = True
        while running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_ESCAPE:
                        if self.state == "playing":
                            self.state = "menu"
                        else:
                            running = False
                    if event.key == pygame.K_RETURN or event.key == pygame.K_SPACE:
                        if self.state == "menu":
                            hs = self.high_score
                            self.reset()
                            self.high_score = hs
                            self.state = "playing"
                        elif self.state == "gameover":
                            hs = self.high_score
                            self.reset()
                            self.high_score = hs
                            self.state = "playing"

            self.update()

            self.draw_road()

            if self.state == "playing":
                for obs in self.obstacles:
                    obs.draw(self.screen)
                for p in self.particles:
                    p.draw(self.screen)
                self.player.draw(self.screen)
                self.draw_hud_simple()
            elif self.state == "menu":
                self.draw_menu()
            elif self.state == "gameover":
                for obs in self.obstacles:
                    obs.draw(self.screen)
                self.player.draw(self.screen)
                self.draw_hud_simple()
                self.draw_gameover()

            pygame.display.flip()
            self.clock.tick(FPS)

        pygame.quit()
        sys.exit()


if __name__ == "__main__":
    game = Game()
    game.run()
