import pygame
from pygame.locals import *  # Import necessary modules
import sys
import random
import time

# Initialize pygame
pygame.init()

# Define constants
vec = pygame.math.Vector2
HEIGHT = 450
WIDTH = 400
ACC = 0.5  # Acceleration
FRIC = -0.12  # Friction
FPS = 60  # Frames per second

# Set up the game window
FramePerSec = pygame.time.Clock()
displaysurface = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Game")

# Define the Player class
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.surf = pygame.Surface((30, 30))  # Player surface
        self.surf.fill((128, 255, 40))  # Player color
        self.rect = self.surf.get_rect()  # Player rectangle

        # Player position, velocity, and acceleration
        self.pos = vec((10, 360))
        self.vel = vec(0, 0)
        self.acc = vec(0, 0)
        self.jumping = False  # Flag to track if the player is jumping
        self.score = 0  # Player's score

    # Function to handle player movement
    def move(self):
        self.acc = vec(0, 0.5)  # Gravity

        pressed_keys = pygame.key.get_pressed()

        # Adjust acceleration based on key presses
        if pressed_keys[K_LEFT] or pressed_keys[K_a]:
            self.acc.x = -ACC
        if pressed_keys[K_RIGHT] or pressed_keys[K_d]:
            self.acc.x = ACC

        # Apply friction
        self.acc.x += self.vel.x * FRIC
        self.vel += self.acc
        self.pos += self.vel + 0.5 * self.acc

        # Wrap around screen edges
        if self.pos.x > WIDTH:
            self.pos.x = 0
        if self.pos.x < 0:
            self.pos.x = WIDTH

        self.rect.midbottom = self.pos  # Update player position

    # Function for player jump
    def jump(self):
        hits = pygame.sprite.spritecollide(self, platforms, False)
        if hits and not self.jumping:
            self.jumping = True
            self.vel.y = -15

    # Function to cancel jump if the player releases the jump key
    def cancel_jump(self):
        if self.jumping:
            if self.vel.y < -3:
                self.vel.y = -3

    # Function to update player state
    def update(self):
        hits = pygame.sprite.spritecollide(P1, platforms, False)
        if P1.vel.y > 0:
            if hits:
                if self.pos.y < hits[0].rect.bottom:
                    if hits[0].point == True:
                        hits[0].point = False
                        self.score += 1  # Increment score when player lands on a platform
                    self.pos.y = hits[0].rect.top + 1
                    self.vel.y = 0
                    self.jumping = False



# Define the platform class
class platform(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.surf = pygame.Surface((random.randint(50, 100), 12))  # Platform surface
        self.surf.fill((0, 255, 0))  # Platform color
        self.rect = self.surf.get_rect(center=(random.randint(0, WIDTH - 10),
                                               random.randint(0, HEIGHT - 30)))  # Platform position

        # Flag to track if the platform is a scoring platform
        self.point = True
        self.speed = random.randint(-1, 1)  # Platform speed
        self.moving = True  # Flag to track if the platform is moving

    # Function to move the platform
    def move(self):
        hits = self.rect.colliderect(P1.rect) # Check for collision 
        if self.moving == True:  # Check if the object is currently moving
            self.rect.move_ip(self.speed,0) # Move the object horizontally by its speed
            # If there's a collision with another object (named P1), adjust the position of P1
            if hits:
                P1.pos += (self.speed, 0)
            # Wrap around the screen if the object moves beyond the screen boundaries
            if self.speed > 0 and self.rect.left > WIDTH:
                self.rect.right = 0
            if self.speed < 0 and self.rect.right < 0:
                self.rect.left = WIDTH


# Function to check platform collision
def check(platform, groupies):
    if pygame.sprite.spritecollideany(platform, groupies):
        return True
    else:
        for entity in groupies:
            if entity == platform:
                continue
            # Check if platforms are too close vertically or horizontally
            if (abs(platform.rect.top - entity.rect.bottom) < 50) and (
                    abs(platform.rect.bottom - entity.rect.top) < 50):
                return True
        return False

# Function to generate platforms
def plat_gen():
    while len(platforms) < 6:
        width = random.randrange(50, 100)
        p = platform()
        C = True

        while C:
            p = platform()
            p.rect.center = (random.randrange(0, WIDTH - width),
                             random.randrange(-50, 0))
            C = check(p, platforms)
        platforms.add(p)
        all_sprites.add(p)

# Create instances of Player and platform
PT1 = platform()
P1 = Player()

# Customize the main platform
PT1.surf = pygame.Surface((WIDTH, 20))
PT1.surf.fill((255, 0, 0))  # Main platform color
PT1.rect = PT1.surf.get_rect(center=(WIDTH / 2, HEIGHT - 10))
PT1.point = False  # Main platform is not a scoring platform
PT1.moving = False  # Main platform does not move

# Create sprite groups
all_sprites = pygame.sprite.Group()
all_sprites.add(PT1)
all_sprites.add(P1)

platforms = pygame.sprite.Group()
platforms.add(PT1)

# Generate additional platforms
for x in range(random.randint(4, 5)):
    C = True
    pl = platform()
    while C:
        pl = platform()
        C = check(pl, platforms)
    platforms.add(pl)
    all_sprites.add(pl)

# Main game loop
while True:
    P1.update()  # Update player state
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                P1.jump()  # Trigger player jump
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_SPACE:
                P1.cancel_jump()  # Cancel player jump if key is released

    # Game over if player falls below the screen
    if P1.rect.top > HEIGHT:
        for entity in all_sprites:
            entity.kill()
            time.sleep(1)
            displaysurface.fill((255, 0, 0))
            pygame.display.update()
            time.sleep(1)
            pygame.quit()
            sys.exit()

    # Move player and platforms
    if P1.rect.top <= HEIGHT / 3:
    # Move the player's position downwards by the absolute value of its vertical velocity
        P1.pos.y += abs(P1.vel.y)
        # Move each platform's position downwards by the absolute value of the player's vertical velocity
        for plat in platforms:
            plat.rect.y += abs(P1.vel.y)
            # If a platform reaches the bottom of the screen, remove it
            if plat.rect.top >= HEIGHT:
                plat.kill()

    # Generate new platforms
    plat_gen()

    # Fill the display surface with black color
    displaysurface.fill((0, 0, 0))

    # Render the player's score text
    f = pygame.font.SysFont("Verdana", 20)
    g = f.render(str(P1.score), True, (123, 255, 0))
    displaysurface.blit(g, (WIDTH/2, 10))

    # Draw all sprites on the display surface and move them
    for entity in all_sprites:
        displaysurface.blit(entity.surf, entity.rect)
        entity.move()

    # Update the display
    pygame.display.update()

    # Control the frame rate
    FramePerSec.tick(FPS)