import pygame
import sys
import math
import os
import time

# Initialize Pygame
pygame.init()

# Screen dimensions
WIDTH, HEIGHT = 800, 600
SCREEN = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Top-Down Car and Ball Simulation")

# Clock for controlling frame rate
CLOCK = pygame.time.Clock()
FPS = 60

# Colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GREEN = (0, 200, 0)
RED = (200, 0, 0)
BLUE = (0, 0, 200)

# Paths to images
CAR_IMAGE_PATH = os.path.join('items', 'car.png')
BALL_IMAGE_PATH = os.path.join('items', 'ball.png')

# Load and scale images
def load_and_scale_image(path, scale_factor=1.0):
    try:
        image = pygame.image.load(path).convert_alpha()
        if scale_factor != 1.0:
            size = image.get_size()
            image = pygame.transform.scale(
                image, (int(size[0] * scale_factor), int(size[1] * scale_factor))
            )
        return image
    except pygame.error as e:
        print(f"Error loading image '{path}': {e}")
        sys.exit()

# Scaling factors
CAR_SCALE = 0.3  # Make the car smaller
BALL_SCALE = 0.1  # Adjusted to 0.1 for better visibility

CAR_IMAGE = load_and_scale_image(CAR_IMAGE_PATH, CAR_SCALE)
BALL_IMAGE = load_and_scale_image(BALL_IMAGE_PATH, BALL_SCALE)

# Constants
GRAVITY = 0  # No gravity for top-down view
FRICTION = 0.99  # Slight friction to slow down over time

# Helper function to rotate an image while keeping its center
def rotate_image(image, angle):
    """Rotates an image while keeping its center."""
    rotated_image = pygame.transform.rotate(image, angle)
    rotated_rect = rotated_image.get_rect(center=image.get_rect().center)
    return rotated_image, rotated_rect

class GameObject:
    def __init__(self, image, x, y):
        self.original_image = image
        self.image = image
        self.rect = self.image.get_rect(center=(x, y))
        self.pos = pygame.math.Vector2(x, y)
        self.vel = pygame.math.Vector2(0, 0)
        self.acc = pygame.math.Vector2(0, 0)
        self.angle = 0  # For rotation
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        # Update velocity and position
        self.vel += self.acc
        self.vel *= FRICTION
        self.pos += self.vel
        self.rect.center = self.pos

        # Reset acceleration
        self.acc = pygame.math.Vector2(0, 0)

    def draw(self, surface):
        surface.blit(self.image, self.rect.topleft)

class Car(GameObject):
    def __init__(self, image, x, y):
        super().__init__(image, x, y)
        self.speed = 0
        self.rotation_speed = 5  # Degrees per frame

    def handle_keys(self):
        keys = pygame.key.get_pressed()
        if keys[pygame.K_UP]:
            # Accelerate forward
            direction = pygame.math.Vector2(
                math.cos(math.radians(self.angle)), -math.sin(math.radians(self.angle))
            )
            self.acc += direction * 0.5
        if keys[pygame.K_DOWN]:
            # Accelerate backward
            direction = pygame.math.Vector2(
                math.cos(math.radians(self.angle)), -math.sin(math.radians(self.angle))
            )
            self.acc -= direction * 0.5
        if keys[pygame.K_LEFT]:
            # Rotate left (counter-clockwise)
            self.angle += self.rotation_speed
            self.angle %= 360  # Ensure the angle stays within 0-359
            self.image, self.rect = rotate_image(self.original_image, self.angle)
            self.rect.center = self.pos
            self.mask = pygame.mask.from_surface(self.image)
        if keys[pygame.K_RIGHT]:
            # Rotate right (clockwise)
            self.angle -= self.rotation_speed
            self.angle %= 360
            self.image, self.rect = rotate_image(self.original_image, self.angle)
            self.rect.center = self.pos
            self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        self.handle_keys()
        super().update()

        # Keep car within screen bounds
        if self.pos.x < self.rect.width / 2:
            self.pos.x = self.rect.width / 2
            self.vel.x = 0
        elif self.pos.x > WIDTH - self.rect.width / 2:
            self.pos.x = WIDTH - self.rect.width / 2
            self.vel.x = 0
        if self.pos.y < self.rect.height / 2:
            self.pos.y = self.rect.height / 2
            self.vel.y = 0
        elif self.pos.y > HEIGHT - self.rect.height / 2:
            self.pos.y = HEIGHT - self.rect.height / 2
            self.vel.y = 0
        self.rect.center = self.pos  # Update rect position

class Ball(GameObject):
    def __init__(self, image, x, y):
        super().__init__(image, x, y)
        self.elasticity = 0.9  # Bounciness factor
        self.mask = pygame.mask.from_surface(self.image)

    def update(self):
        # No gravity in top-down view, but can apply friction
        super().update()

        # Keep ball within screen bounds
        if self.pos.x <= self.rect.width / 2:
            self.pos.x = self.rect.width / 2
            self.vel.x *= -self.elasticity
        elif self.pos.x >= WIDTH - self.rect.width / 2:
            self.pos.x = WIDTH - self.rect.width / 2
            self.vel.x *= -self.elasticity
        if self.pos.y <= self.rect.height / 2:
            self.pos.y = self.rect.height / 2
            self.vel.y *= -self.elasticity
        elif self.pos.y >= HEIGHT - self.rect.height / 2:
            self.pos.y = HEIGHT - self.rect.height / 2
            self.vel.y *= -self.elasticity
        self.rect.center = self.pos  # Update rect position

class Goal:
    def __init__(self, x, y, width, height, color, side):
        """
        side: 'left' or 'right' to determine which team the goal belongs to
        """
        self.rect = pygame.Rect(x, y, width, height)
        self.color = color
        self.side = side  # To assign score correctly

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, self.rect, 2)  # Draw as outline

class Game:
    def __init__(self):
        # Initialize car at the center
        self.car = Car(CAR_IMAGE, WIDTH / 2, HEIGHT / 2)
        # Initialize ball at some position
        self.ball = Ball(BALL_IMAGE, WIDTH / 2 + 150, HEIGHT / 2)
        # Optionally, set initial velocity for the ball
        self.ball.vel = pygame.math.Vector2(-2, 0)

        # Initialize goals
        goal_width, goal_height = 10, 200
        self.left_goal = Goal(
            0, (HEIGHT - goal_height) / 2, goal_width, goal_height, GREEN, side='left'
        )
        self.right_goal = Goal(
            WIDTH - goal_width, (HEIGHT - goal_height) / 2, goal_width, goal_height, RED, side='right'
        )

        # Initialize score
        self.score = {'left': 0, 'right': 0}

        # Font for score display
        self.font = pygame.font.SysFont(None, 36)

        # Replay flag
        self.replaying = False
        self.replay_timer = 0  # Timer for replay duration

    def check_collision(self):
        # Use masks for collision detection
        offset = (
            int(self.ball.rect.left - self.car.rect.left),
            int(self.ball.rect.top - self.car.rect.top),
        )
        collision_point = self.car.mask.overlap(self.ball.mask, offset)
        if collision_point:
            # Collision detected, handle collision response
            # Calculate the normal vector
            dx = self.ball.pos.x - self.car.pos.x
            dy = self.ball.pos.y - self.car.pos.y
            distance = math.hypot(dx, dy)
            if distance == 0:
                distance = 1  # Prevent division by zero

            # Normal vector
            nx = dx / distance
            ny = dy / distance

            # Relative velocity
            rvx = self.ball.vel.x - self.car.vel.x
            rvy = self.ball.vel.y - self.car.vel.y

            # Relative velocity along the normal
            vel_along_normal = rvx * nx + rvy * ny

            # Do not resolve if velocities are separating
            if vel_along_normal > 0:
                return

            # Calculate restitution (bounciness)
            restitution = min(self.ball.elasticity, 0.5)  # Assuming car has less elasticity

            # Impulse scalar
            j = -(1 + restitution) * vel_along_normal
            j /= (1 / 1 + 1 / 1)  # Assuming mass=1 for both

            # Apply impulse
            impulse = pygame.math.Vector2(j * nx, j * ny)
            self.ball.vel += impulse
            self.car.vel -= impulse  # Opposite direction for car

            # Separate the objects to prevent sticking
            overlap = 1  # You may adjust this value
            self.ball.pos += pygame.math.Vector2(nx, ny) * (overlap / 2)
            self.car.pos -= pygame.math.Vector2(nx, ny) * (overlap / 2)
            self.car.rect.center = self.car.pos
            self.ball.rect.center = self.ball.pos

    def check_goal(self):
        # Check if the ball has entered the left goal
        if self.left_goal.rect.collidepoint(self.ball.pos):
            self.score['right'] += 1  # Right team scores
            self.trigger_replay(scoring_team='right')
        # Check if the ball has entered the right goal
        elif self.right_goal.rect.collidepoint(self.ball.pos):
            self.score['left'] += 1  # Left team scores
            self.trigger_replay(scoring_team='left')

    def trigger_replay(self, scoring_team):
        self.replaying = True
        self.replay_timer = pygame.time.get_ticks()
        # Display goal message
        goal_text = f"Goal for {'Right' if scoring_team == 'right' else 'Left'} Team!"
        print(goal_text)  # For console debugging
        # You can expand this to show on the screen instead
        self.show_goal_message(goal_text)

    def show_goal_message(self, text):
        # Render the goal message in the center of the screen
        message_font = pygame.font.SysFont(None, 72)
        message_surface = message_font.render(text, True, BLUE)
        message_rect = message_surface.get_rect(center=(WIDTH / 2, HEIGHT / 2))
        SCREEN.blit(message_surface, message_rect)
        pygame.display.flip()
        # Pause for a moment to show the message
        pygame.time.delay(2000)  # 2 seconds

    def reset_positions(self):
        # Reset car and ball positions
        self.car.pos = pygame.math.Vector2(WIDTH / 2, HEIGHT / 2)
        self.car.vel = pygame.math.Vector2(0, 0)
        self.car.angle = 0
        self.car.image, self.car.rect = rotate_image(self.car.original_image, self.car.angle)
        self.car.rect.center = self.car.pos

        self.ball.pos = pygame.math.Vector2(WIDTH / 2 + 150, HEIGHT / 2)
        self.ball.vel = pygame.math.Vector2(-2, 0)
        self.ball.rect.center = self.ball.pos

    def display_score(self):
        # Render the score on the screen
        score_text = f"Left: {self.score['left']}   Right: {self.score['right']}"
        text_surface = self.font.render(score_text, True, BLACK)
        text_rect = text_surface.get_rect(center=(WIDTH / 2, 30))
        SCREEN.blit(text_surface, text_rect)

    def run(self):
        running = True
        while running:
            CLOCK.tick(FPS)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    running = False

            if not self.replaying:
                # Update game objects
                self.car.update()
                self.ball.update()

                # Check collisions
                self.check_collision()

                # Check for goals
                self.check_goal()
            else:
                # During replay, you might want to disable controls
                current_time = pygame.time.get_ticks()
                if current_time - self.replay_timer > 2000:  # 2 seconds replay
                    self.replaying = False
                    self.reset_positions()

            # Draw everything
            SCREEN.fill(WHITE)

            # Draw goals
            self.left_goal.draw(SCREEN)
            self.right_goal.draw(SCREEN)

            # Draw objects
            self.car.draw(SCREEN)
            self.ball.draw(SCREEN)

            # Draw the score
            self.display_score()

            # Update the display
            pygame.display.flip()

        pygame.quit()
        sys.exit()

if __name__ == "__main__":
    # Ensure the 'items' directory exists with required images
    required_files = [CAR_IMAGE_PATH, BALL_IMAGE_PATH]
    for file in required_files:
        if not os.path.isfile(file):
            print(f"Required file '{file}' not found. Please ensure it exists.")
            sys.exit()

    game = Game()
    game.run()

Fix issue when goals not works, provide full code