#!/usr/bin/env python3
import pygame, sys, random, time, math

# ======================================================
# SETTINGS
# ======================================================
WIDTH, HEIGHT = 960, 540
FPS = 60
GRAVITY = 1800
PLAYER_SPEED = 300
JUMP_FORCE = -650
DASH_FORCE = 700
DASH_COOLDOWN = 0.6

pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Ultimate Linux Platformer")
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 24)

# ======================================================
# COLORS
# ======================================================
WHITE=(245,245,245); BLACK=(0,0,0)
BLUE=(70,120,255); GREEN=(70,200,120)
RED=(220,70,70); YELLOW=(240,220,80)
PURPLE=(160,90,200); ORANGE=(255,140,0)
SKY_DAY=(135,206,235); SKY_NIGHT=(15,15,50)

# ======================================================
# UTILS
# ======================================================
def draw_rect(rect, color, cam_x):
    pygame.draw.rect(screen, color, (rect.x-cam_x, rect.y, rect.width, rect.height))

def draw_text(text, x, y, color=BLACK):
    screen.blit(font.render(text, True, color), (x, y))

# ======================================================
# PLAYER
# ======================================================
class Player:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, 40, 55)
        self.vel = pygame.Vector2(0, 0)
        self.on_ground = False
        self.jump_count = 0
        self.health = 5
        self.score = 0
        self.spawn = pygame.Vector2(x, y)
        self.invincible = 0
        self.last_dash = 0

    def update(self, dt, platforms):
        keys = pygame.key.get_pressed()
        # Horizontal movement
        self.vel.x = 0
        if keys[pygame.K_a]: self.vel.x = -PLAYER_SPEED
        if keys[pygame.K_d]: self.vel.x = PLAYER_SPEED

        # Jump
        if keys[pygame.K_SPACE] and self.jump_count < 2:
            self.vel.y = JUMP_FORCE
            self.jump_count += 1

        # Dash
        if keys[pygame.K_LSHIFT] and time.time() - self.last_dash > DASH_COOLDOWN:
            self.vel.x *= 3
            self.last_dash = time.time()

        # Gravity
        self.vel.y += GRAVITY*dt

        # Horizontal collision
        self.rect.x += self.vel.x*dt
        for p in platforms:
            if self.rect.colliderect(p):
                if self.vel.x>0: self.rect.right=p.left
                elif self.vel.x<0: self.rect.left=p.right

        # Vertical collision
        self.rect.y += self.vel.y*dt
        self.on_ground = False
        for p in platforms:
            if self.rect.colliderect(p):
                if self.vel.y>0:
                    self.rect.bottom = p.top
                    self.vel.y = 0
                    self.on_ground=True
                    self.jump_count = 0
                elif self.vel.y<0:
                    self.rect.top=p.bottom
                    self.vel.y=0

        if self.invincible>0: self.invincible-=dt

# ======================================================
# ENEMY
# ======================================================
class Enemy:
    def __init__(self, x, y, type="ground"):
        self.rect = pygame.Rect(x, y, 40, 40)
        self.dir = random.choice([-1,1])
        self.type = type
        self.health = 1 if type=="ground" else 3

    def update(self, dt):
        if self.type=="ground":
            self.rect.x += self.dir*120*dt
            if random.randint(0,200)==0: self.dir*=-1
        elif self.type=="fly":
            self.rect.y += math.sin(time.time()*2)*2

# ======================================================
# MOVING PLATFORM
# ======================================================
class MovingPlatform:
    def __init__(self, x,y,w,h,limit):
        self.rect = pygame.Rect(x,y,w,h)
        self.start_x = x
        self.limit = limit
        self.dir = 1
    def update(self, dt):
        self.rect.x += self.dir*120*dt
        if abs(self.rect.x-self.start_x)>self.limit: self.dir*=-1

# ======================================================
# PARTICLES
# ======================================================
class Particle:
    def __init__(self, x, y, color, dx, dy, lifetime):
        self.x=x; self.y=y
        self.color=color; self.dx=dx; self.dy=dy; self.lifetime=lifetime
    def update(self, dt):
        self.x+=self.dx*dt; self.y+=self.dy*dt; self.lifetime-=dt
    def draw(self, cam_x):
        pygame.draw.circle(screen, self.color, (int(self.x-cam_x), int(self.y)),2)

# ======================================================
# WORLD SETUP
# ======================================================
platforms=[
    pygame.Rect(0,480,4000,60),
    pygame.Rect(300,380,200,20),
    pygame.Rect(700,300,200,20),
    pygame.Rect(1200,260,200,20),
    pygame.Rect(1600,320,200,20)
]
moving_platforms=[MovingPlatform(500,420,140,20,220)]
enemies=[
    Enemy(350,340),
    Enemy(750,260),
    Enemy(1250,220),
    Enemy(1800,220,"fly")
]
coins=[
    pygame.Rect(340,350,20,20),
    pygame.Rect(740,270,20,20),
    pygame.Rect(1240,230,20,20),
    pygame.Rect(1640,290,20,20)
]
lava=[pygame.Rect(900,460,200,20)]
checkpoint=pygame.Rect(1400,240,30,40)

player=Player(100,300)
camera_x=0
paused=False; debug=False; won=False
particles=[]

day_time=0  # 0-24 for day/night cycle

# ======================================================
# GAME LOOP
# ======================================================
while True:
    dt = clock.tick(FPS)/1000
    day_time += dt*0.05  # slow day/night

    for e in pygame.event.get():
        if e.type==pygame.QUIT: pygame.quit(); sys.exit()
        if e.type==pygame.KEYDOWN:
            if e.key==pygame.K_p: paused=not paused
            if e.key==pygame.K_F3: debug=not debug

    if paused:
        screen.fill(BLACK)
        draw_text("PAUSED", 440,260,WHITE)
        pygame.display.update(); continue

    all_platforms = platforms + [mp.rect for mp in moving_platforms]
    player.update(dt, all_platforms)

    for mp in moving_platforms: mp.update(dt)
    for e in enemies:
        e.update(dt)
        if player.rect.colliderect(e.rect) and player.invincible<=0:
            player.health-=1; player.invincible=1
            # particle effect
            for _ in range(15):
                particles.append(Particle(player.rect.centerx,player.rect.centery,RED,random.uniform(-100,100),random.uniform(-100,0),0.5))

    for c in coins[:]:
        if player.rect.colliderect(c):
            coins.remove(c)
            player.score+=1
            for _ in range(10):
                particles.append(Particle(c.centerx,c.centery,YELLOW,random.uniform(-50,50),random.uniform(-100,-50),0.5))

    for l in lava:
        if player.rect.colliderect(l):
            player.rect.topleft = player.spawn
            player.health -=1

    if player.rect.colliderect(checkpoint):
        player.spawn = pygame.Vector2(checkpoint.x, checkpoint.y)

    if player.health<=0:
        player=Player(player.spawn.x, player.spawn.y)

    if player.rect.x>2000: won=True

    camera_x = player.rect.x - WIDTH//2

    # ================= DRAW =================
    # Background day/night
    t = (math.sin(day_time)+1)/2
    sky_color = (SKY_NIGHT[0]*(1-t)+SKY_DAY[0]*t,
                 SKY_NIGHT[1]*(1-t)+SKY_DAY[1]*t,
                 SKY_NIGHT[2]*(1-t)+SKY_DAY[2]*t)
    screen.fill(sky_color)

    for p in platforms: draw_rect(p,GREEN,camera_x)
    for mp in moving_platforms: draw_rect(mp.rect,PURPLE,camera_x)
    for e in enemies: draw_rect(e.rect,RED,camera_x)
    for c in coins: pygame.draw.circle(screen,YELLOW,(c.centerx-camera_x,c.centery),10)
    for l in lava: draw_rect(l,RED,camera_x)
    draw_rect(player.rect,BLUE,camera_x)
    draw_rect(checkpoint,BLACK,camera_x)

    # Draw particles
    for particle in particles[:]:
        particle.update(dt)
        if particle.lifetime<=0: particles.remove(particle)
        else: particle.draw(camera_x)

    # UI
    draw_text(f"HP:{player.health}  Coins:{player.score}  FPS:{int(clock.get_fps())}",10,10)
    if debug: pygame.draw.rect(screen,RED,(player.rect.x-camera_x,player.rect.y,player.rect.width,player.rect.height),2)
    if won: draw_text("YOU WIN!",440,260)

    pygame.display.update()
