import pygame
import random
import sys

# Initialize Pygame
pygame.init()
pygame.font.init()

# Display settings
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 600
FPS = 60

# Colors
SKY_BLUE = (113, 197, 207)
GROUND_COLOR = (222, 216, 149)
GROUND_GRASS = (115, 200, 75)
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
BIRD_YELLOW = (255, 215, 0)
BIRD_ORANGE = (255, 140, 0)
BIRD_EYE = (255, 255, 255)
PUPIL = (0, 0, 0)

# Pipe Colors (Requirement 4: dark green, light brown, or dark gray)
PIPE_COLORS = [
    (34, 139, 34),    # Dark Green
    (181, 101, 29),   # Light Brown
    (80, 80, 80)      # Dark Gray
]

# Game Physics and Parameters
GRAVITY = 0.25
FLAP_STRENGTH = -6.5
PIPE_SPEED = 3
PIPE_GAP = 150
PIPE_FREQUENCY = 1500  # Spawn pipe every 1.5s
GROUND_HEIGHT = 80


class Bird:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.velocity = 0
        self.radius = 16

    def flap(self):
        self.velocity = FLAP_STRENGTH

    def update(self):
        self.velocity += GRAVITY
        self.y += self.velocity

    def draw(self, surface):
        # Body
        pygame.draw.circle(surface, BIRD_YELLOW, (int(self.x), int(self.y)), self.radius)
        pygame.draw.circle(surface, BLACK, (int(self.x), int(self.y)), self.radius, 2)

        # Eye
        eye_x = self.x + 6
        eye_y = self.y - 4
        pygame.draw.circle(surface, BIRD_EYE, (int(eye_x), int(eye_y)), 5)
        pygame.draw.circle(surface, PUPIL, (int(eye_x + 2), int(eye_y)), 2)

        # Beak
        beak_pts = [
            (self.x + 10, self.y - 1),
            (self.x + 20, self.y + 2),
            (self.x + 10, self.y + 6)
        ]
        pygame.draw.polygon(surface, BIRD_ORANGE, beak_pts)
        pygame.draw.polygon(surface, BLACK, beak_pts, 1)

        # Wing
        wing_pts = [
            (self.x - 10, self.y + 2),
            (self.x - 2, self.y + 6),
            (self.x - 2, self.y - 2)
        ]
        pygame.draw.polygon(surface, (230, 190, 0), wing_pts)
        pygame.draw.polygon(surface, BLACK, wing_pts, 1)

    def get_mask_rect(self):
        return pygame.Rect(self.x - self.radius, self.y - self.radius, self.radius * 2, self.radius * 2)


class PipePair:
    def __init__(self, x):
        self.x = x
        self.width = 60
        # Color assigned randomly: dark green, light brown, or dark gray
        self.color = random.choice(PIPE_COLORS)

        min_top = 50
        max_top = SCREEN_HEIGHT - GROUND_HEIGHT - PIPE_GAP - 50
        self.top_height = random.randint(min_top, max_top)
        self.bottom_y = self.top_height + PIPE_GAP
        self.passed = False

    def update(self):
        self.x -= PIPE_SPEED

    def draw(self, surface):
        rim_height = 20
        rim_extra = 6
        darker_color = (
            max(0, self.color[0] - 30),
            max(0, self.color[1] - 30),
            max(0, self.color[2] - 30)
        )

        # Top Pipe
        top_rect = pygame.Rect(self.x, 0, self.width, self.top_height)
        pygame.draw.rect(surface, self.color, top_rect)
        pygame.draw.rect(surface, darker_color, top_rect, 2)

        top_rim = pygame.Rect(self.x - rim_extra // 2, self.top_height - rim_height, self.width + rim_extra, rim_height)
        pygame.draw.rect(surface, self.color, top_rim)
        pygame.draw.rect(surface, darker_color, top_rim, 2)

        # Bottom Pipe
        bottom_height = SCREEN_HEIGHT - GROUND_HEIGHT - self.bottom_y
        bottom_rect = pygame.Rect(self.x, self.bottom_y, self.width, bottom_height)
        pygame.draw.rect(surface, self.color, bottom_rect)
        pygame.draw.rect(surface, darker_color, bottom_rect, 2)

        bottom_rim = pygame.Rect(self.x - rim_extra // 2, self.bottom_y, self.width + rim_extra, rim_height)
        pygame.draw.rect(surface, self.color, bottom_rim)
        pygame.draw.rect(surface, darker_color, bottom_rim, 2)

    def collides_with(self, bird):
        bird_rect = bird.get_mask_rect()
        top_rect = pygame.Rect(self.x, 0, self.width, self.top_height)
        bottom_rect = pygame.Rect(
            self.x, self.bottom_y, self.width, SCREEN_HEIGHT - GROUND_HEIGHT - self.bottom_y
        )
        return bird_rect.colliderect(top_rect) or bird_rect.colliderect(bottom_rect)


def draw_text_with_shadow(surface, text, font, color, shadow_color, x, y, center=True):
    text_surf = font.render(text, True, color)
    shadow_surf = font.render(text, True, shadow_color)

    rect = text_surf.get_rect()
    if center:
        rect.center = (x, y)
    else:
        rect.topleft = (x, y)

    shadow_rect = rect.copy()
    shadow_rect.x += 2
    shadow_rect.y += 2

    surface.blit(shadow_surf, shadow_rect)
    surface.blit(text_surf, rect)


def main():
    screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
    pygame.display.set_caption("Flappy Bird - Pygame")
    clock = pygame.time.Clock()

    font_large = pygame.font.SysFont("Arial", 40, bold=True)
    font_medium = pygame.font.SysFont("Arial", 26, bold=True)
    font_small = pygame.font.SysFont("Arial", 20, bold=True)

    high_score = 0

    while True:
        bird = Bird(80, SCREEN_HEIGHT // 2)
        pipes = []
        last_pipe_time = pygame.time.get_ticks()
        score = 0
        game_active = False
        game_over = False
        ground_x = 0

        running = True
        while running:
            current_time = pygame.time.get_ticks()

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()

                if event.type == pygame.KEYDOWN:
                    # Flap upward when pressing SPACE or UP ARROW
                    if event.key in (pygame.K_SPACE, pygame.K_UP):
                        if not game_active and not game_over:
                            game_active = True
                            bird.flap()
                        elif game_active:
                            bird.flap()
                        elif game_over:
                            running = False  # Reset round

            if game_active:
                bird.update()

                # Spawn pipe pairs periodically
                if current_time - last_pipe_time > PIPE_FREQUENCY:
                    pipes.append(PipePair(SCREEN_WIDTH))
                    last_pipe_time = current_time

                # Move pipes
                for pipe in pipes:
                    pipe.update()

                # Remove off-screen pipes
                pipes = [p for p in pipes if p.x + p.width > -20]

                # Score update when passing pipes without hitting them
                for pipe in pipes:
                    if not pipe.passed and pipe.x + pipe.width < bird.x:
                        pipe.passed = True
                        score += 1

                # Check pipe collisions
                for pipe in pipes:
                    if pipe.collides_with(bird):
                        game_active = False
                        game_over = True

                # Check collision with ground or falling off top of screen
                if bird.y + bird.radius >= SCREEN_HEIGHT - GROUND_HEIGHT or bird.y - bird.radius <= 0:
                    game_active = False
                    game_over = True

                # Animate ground
                ground_x = (ground_x - PIPE_SPEED) % 20

            if game_over:
                if score > high_score:
                    high_score = score

            # Drawing background & graphics
            screen.fill(SKY_BLUE)

            # Draw pipes
            for pipe in pipes:
                pipe.draw(screen)

            # Draw ground
            pygame.draw.rect(screen, GROUND_COLOR, (0, SCREEN_HEIGHT - GROUND_HEIGHT, SCREEN_WIDTH, GROUND_HEIGHT))
            pygame.draw.rect(screen, GROUND_GRASS, (0, SCREEN_HEIGHT - GROUND_HEIGHT, SCREEN_WIDTH, 15))
            for x in range(int(ground_x) - 20, SCREEN_WIDTH + 20, 20):
                pygame.draw.line(screen, (200, 190, 130), (x, SCREEN_HEIGHT - GROUND_HEIGHT + 15), (x - 10, SCREEN_HEIGHT), 3)

            # Draw bird
            bird.draw(screen)

            # UI Text Displays
            if not game_active and not game_over:
                draw_text_with_shadow(screen, "FLAPPY BIRD", font_large, WHITE, BLACK, SCREEN_WIDTH // 2, 180)
                draw_text_with_shadow(screen, "Press SPACE or UP to Fly", font_medium, WHITE, BLACK, SCREEN_WIDTH // 2, 320)
            elif game_active:
                draw_text_with_shadow(screen, str(score), font_large, WHITE, BLACK, SCREEN_WIDTH // 2, 60)
            elif game_over:
                # Game Over Card
                card = pygame.Surface((320, 220))
                card.set_alpha(210)
                card.fill((30, 30, 30))
                screen.blit(card, (SCREEN_WIDTH // 2 - 160, 180))

                draw_text_with_shadow(screen, "GAME OVER", font_large, (255, 80, 80), BLACK, SCREEN_WIDTH // 2, 220)
                draw_text_with_shadow(screen, f"Score: {score}", font_medium, WHITE, BLACK, SCREEN_WIDTH // 2, 275)
                draw_text_with_shadow(screen, f"Best Score: {high_score}", font_medium, (255, 215, 0), BLACK, SCREEN_WIDTH // 2, 315)
                draw_text_with_shadow(screen, "Press SPACE or UP to Restart", font_small, WHITE, BLACK, SCREEN_WIDTH // 2, 365)

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


if __name__ == "__main__":
    main()
