import asyncio
import sys
import pygame as pg
from player import Player
import level
import assets
from ui import Button
import random
from camera import Camera
from controls import VirtualJoystick

async def run_game():
    pg.init()
    screen = pg.display.set_mode((640, 360))
    clock = pg.time.Clock()
    pg.display.set_caption('Pixel Patcher')
    current_level = 1
    player = Player()
    player.position = level.load_level(current_level)
    camera = Camera()
    camera.player = player
    player.camera = camera

    assets.load_sound_effects()

    paused = False
    pause_button = Button(32, 32, (screen.width - 25, 25), (255, 255, 255), '')
    pause_button._image = pg.transform.scale(
        pg.image.load(
        './assets/ui/pause.png'
        ),
        (32, 32)
    )

    play_button = Button(150, 40, (screen.width // 2, screen.height // 2), (88, 0, 124), 'PLAY', assets.font)
    INSTRUCTIONS = """Controls:
A and D to move left and right (Move joystick left and right 
on mobile).
W and S to attack up and down (Move joystick up and down on 
mobile).
SPACE to Jump (Press Jump button on mobile).
Press Jump again in the air to double jump.
Press E to attack to the side (press Side Attack button on 
mobile).

The yellow orb will take you to the next level.

NOTE: Glitched tiles are tiles that frequently flicker cyan,
yellow, and magenta. Most glitched tiles will disappear if 
you are above them but within a certain distance, but not
all ..."""

    state = 'menu'
    joystick = VirtualJoystick(70, 280)

    while True:
        dt = clock.tick(60) / 1000.0 # Get deltatime in seconds
        joystick.active_keys = set()

        for event in pg.event.get():
            if event.type == pg.QUIT: 
                sys.exit()
            elif event.type == pg.KEYDOWN:
                if event.key == pg.K_ESCAPE and state == 'game':
                    paused = not paused
            elif event.type == pg.MOUSEBUTTONUP:
                if state == 'game':
                    if paused:
                        paused = False
                    elif pause_button.is_mouse_over():
                        paused = not paused
                elif state == 'menu':
                    if play_button.is_mouse_over():
                        state = 'instructions'
                        continue
                elif state == 'instructions':
                    if play_button.is_mouse_over():
                        state = 'game'
                        continue
            elif event.type == pg.WINDOWFOCUSLOST:
                pg.event.clear()

            if state == 'game':
                joystick.handle_event(event)
        
        screen.fill((138, 0, 196))
        if state == 'menu':
            assets.font.draw_text(
                'Pixel Patcher', screen.width // 2, 25, 40, screen, (255, 255, 255)
            )
            assets.font.draw_text(
                'By Shaurya Sharma', screen.width // 2, 70, 20, screen, (255, 255, 255)
            )

            screen.blit(play_button.image, play_button.rect)
        
        elif state == 'instructions':
            assets.font.draw_text(
                'Instructions', screen.width // 2, 25, 40, screen, (255, 255, 255)
            )
            for line, line_text in enumerate(INSTRUCTIONS.split('\n')):
                assets.font.draw_text(
                    line_text, 10, line * 15 + 60, 10, screen, (255, 255, 255), 'topleft'
                )

            play_button.rect.top = 295
            
            screen.blit(play_button.image, play_button.rect)

        elif state == 'game':
            # Update
            player_update_result = player.update(dt, paused, joystick)
            if player_update_result == 'finish':
                if current_level == 3:
                    state = 'finish'
                else:
                    current_level += 1
                    player.position = level.load_level(current_level)
                    player.velocity = pg.Vector2()
                continue
            elif player_update_result == 'restart':
                player.position = level.load_level(current_level)
                player.velocity = pg.Vector2()
                continue
            level.level.update(dt, player)
            if not paused:
                level.attack_group.update(dt)
                level.enemy_group.update(player, dt)
            camera.update(dt)

            # Render
            for enemy in level.enemy_group:
                distortion = None
                if getattr(enemy, 'distort', False):
                    if int(enemy.frame_index) % 5 == 0:
                        distortion = enemy.image.copy()
                        distortion.fill((255, 8, 232, 0), special_flags=pg.BLEND_RGBA_ADD)
                    elif int(enemy.frame_index) % 4 == 0:
                        distortion = enemy.image.copy()
                        distortion.fill((0, 255, 255, 0), special_flags=pg.BLEND_RGBA_ADD)
                    elif int(enemy.frame_index) % 3 == 0:
                        distortion = enemy.image.copy()
                        distortion.fill((255, 245, 5, 0), special_flags=pg.BLEND_RGBA_ADD)
                screen.blit(enemy.image, enemy.rect.move(-camera.position))
                if distortion is not None and random.randint(0, 1) == 1:
                    screen.blit(distortion, enemy.rect.move(-camera.position))

            level.draw_text(screen, current_level, camera)

            for platform in level.level:
                distortion = None
                if platform.disappearing or platform.breakable:
                    if int(platform.frame) % 5 == 0:
                        distortion = platform.image.copy()
                        distortion.fill((255, 8, 232, 0), special_flags=pg.BLEND_RGBA_ADD)
                    elif int(platform.frame) % 4 == 0:
                        distortion = platform.image.copy()
                        distortion.fill((0, 255, 255, 0), special_flags=pg.BLEND_RGBA_ADD)
                    elif int(platform.frame) % 3 == 0:
                        distortion = platform.image.copy()
                        distortion.fill((255, 245, 5, 0), special_flags=pg.BLEND_RGBA_ADD)

                screen.blit(platform.image, platform.rect.move(-camera.position - pg.Vector2(0, 2)))
                if distortion is not None and random.randint(0, 1) == 1:
                    screen.blit(distortion, platform.rect.move(-camera.position - pg.Vector2(0, 2)))

            for attack in level.attack_group:
                screen.blit(attack.image, attack.rect.move(-camera.position))

            screen.blit(player.image, player.rect.move(-camera.position))

            # UI
            screen.blit(pause_button.image, pause_button.rect)
            if paused:
                assets.font.draw_text(
                    'PAUSED', screen.width // 2, 25, 20, screen, (255, 255, 255)
                )
            joystick.draw(screen)

        elif state == 'finish':
            assets.font.draw_text(
                'Congratulations, you beat the game!\nReload to play again.', screen.width // 2, screen.height // 2, 17, screen, (255, 255, 255)
            )

        pg.display.flip()
        await asyncio.sleep(1 / 60)

try:
    # Web Environment
    asyncio.get_running_loop()
    from pyscript import document
    document.getElementById('loading-overlay').remove()
    asyncio.create_task(run_game())
except RuntimeError:
    # Desktop Environment
    asyncio.run(run_game())