<script>
    import { onMount, onDestroy } from 'svelte';
    import { Player } from '../game/Player.js';
    import { get } from 'svelte/store';
    import { lives, colorOpacity, levelColor, onFragmentCollection, resetLevel, completeLevel, gameCompleted } from '../stores/colorStore.js';
    import { showFragmentQuote, showCompleteQuote } from '../stores/quoteStore.js';
    import { push } from 'svelte-spa-router';
    import { Enemy } from '../game/Enemy.js';
    import { TarPuddle } from '../game/TarPuddle.js';
    import { getLevel } from '../game/levelData.js';
    import { Fragment } from '../game/Fragment.js';

    // level to load - passed in from Game.svelte
    export let levelNumber = 1;

    let canvasContainer; // the div p5 will draw into
    let p5Instance;

    const keysDown = { left: false, right: false, jump: false };

    const onKeyDown = (e) => {
        if (e.key === 'Escape') { push('/levelselect'); return; }
        if (e.key === 'ArrowLeft'  || e.key === 'a') { keysDown.left  = true; e.preventDefault(); }
        if (e.key === 'ArrowRight' || e.key === 'd') { keysDown.right = true; e.preventDefault(); }
        // e.repeat blocks the browser auto-repeat from re-queuing a jump while held
        if ((e.key === 'ArrowUp' || e.key === 'w' || e.key === ' ') && !e.repeat) {
            keysDown.jump = true;
            e.preventDefault();
        }
    };

    const onKeyUp = (e) => {
        if (e.key === 'ArrowLeft'  || e.key === 'a') keysDown.left  = false;
        if (e.key === 'ArrowRight' || e.key === 'd') keysDown.right = false;
        // jump is not cleared here — player.update() consumes it so quick taps aren't lost
    };

    // clear all keys when the window loses focus so nothing stays stuck
    const onBlur = () => {
        keysDown.left  = false;
        keysDown.right = false;
        keysDown.jump  = false;
    };

    window.addEventListener('keydown', onKeyDown);
    window.addEventListener('keyup',   onKeyUp);
    window.addEventListener('blur',    onBlur);

    onMount(() => {
        // IMPORTANT: parameter is named "p" not "p5"
        // "p5" is the global constructor from the CDN script
        // naming it "p5" would shadow the global and break new p5(sketch)
        const sketch = (p) => {
            let player;
            let fragments = [];
            let enemies = [];
            let tarPuddles = [];
            let splats = [];      // paint splash effects on collection
            let levelData;        // declared at sketch level so all functions can access it
            let gameState = 'playing'; // 'playing' | 'levelcomplete' | 'gameover'
            let bgImg;
            let playerImgLoaded;
            let splatImg;

            const LAST_LEVEL = 10;
            let collectedCount = 0; // tracks which quote to show next

            function onFragmentCollected(hexColor, x, y) {
                player.collectFragment(hexColor);
                onFragmentCollection(fragments.length, hexColor);
                splats.push({ x, y, alpha: 200, size: 30 });

                const isLast = fragments.every(f => f.collected);

                // skip the toast on the last fragment — the level-complete overlay takes over
                if (!isLast) {
                    showFragmentQuote(levelData.fragmentQuotes?.[collectedCount], hexColor);
                }
                collectedCount++;

                if (isLast) {
                    gameState = 'levelcomplete';
                    completeLevel(levelData.color);
                    const dest = levelNumber === LAST_LEVEL ? '/win' : `/game?level=${levelNumber + 1}`;
                    showCompleteQuote(levelData.completeQuote, levelData.color, dest);
                }
            }

            p.preload = () => {
                const data = getLevel(levelNumber);
                const isComplete = get(gameCompleted);
                const bgPath = isComplete && data?.bgComplete ? data.bgComplete : data?.bg;
                if (bgPath) bgImg = p.loadImage(bgPath);
                const playerPath = isComplete ? '/assets/player_level10.png' : data?.playerImg;
                if (playerPath) playerImgLoaded = p.loadImage(playerPath);
                splatImg = p.loadImage('/assets/splat.png');
            };

            p.setup = () => {
                // create canvas inside the container div
                const canvas = p.createCanvas(800, 450);
                canvas.parent(canvasContainer);

                // gravity — makes sprites fall downward
                // must be set before creating sprites
                p.world.gravity.y = 30;

                // load level data
                levelData = getLevel(levelNumber);

                // reset color store for this level
                resetLevel(levelNumber, levelData.color);

                // create player at the level spawn point
                player = new Player(p, levelData.spawnX, levelData.spawnY);
                if (playerImgLoaded) player.sprite.img = playerImgLoaded;

                // create platforms — rounded-corner graphic is drawn onto an offscreen canvas
                // and used as the sprite image so physics box stays rectangular
                for (const platData of levelData.platforms) {
                    const plat = new p.Sprite(platData.x, platData.y, platData.w, platData.h);
                    plat.collider = 'static';
                    const g = p.createGraphics(platData.w, platData.h);
                    g.clear();
                    g.noStroke();
                    g.fill('#888888');
                    g.rect(0, 0, platData.w, platData.h, 6);
                    plat.img = g;
                }

                // create fragments from level data
                fragments = [];
                for (const fragData of levelData.fragments) {
                    fragments.push(new Fragment(p, fragData.x, fragData.y, fragData.color));
                }

                // create enemies from level data
                enemies = [];
                for (const enemyData of levelData.enemies) {
                    const e = new Enemy(p, enemyData.x, enemyData.y);
                    e.patrolDistance = enemyData.patrol;
                    enemies.push(e);
                }

                // create tar puddles from level data
                tarPuddles = [];
                for (const tarData of levelData.tar) {
                    tarPuddles.push(new TarPuddle(p, tarData.x, tarData.y));
                }
            };

            p.draw = () => {
                if (bgImg) {
                    p.image(bgImg, 0, 0, p.width, p.height);
                } else {
                    p.background(30);
                }

                // ── 1. PHYSICS FIRST ──────────────────────────────────────────
                // p5play needs this every frame to run gravity and collision
                // touching.bottom is only accurate AFTER this runs
                p.allSprites.update();

                // ── 2. COLOR TINT OVERLAY ─────────────────────────────────────
                // skipped after game completion — full-color backgrounds need no tint
                if (!get(gameCompleted)) {
                    const reveal = get(colorOpacity);
                    const hex    = get(levelColor);
                    if (reveal > 0 && hex) {
                        const col = p.color(hex);
                        const r = p.lerp(128, p.red(col),   reveal);
                        const g = p.lerp(128, p.green(col), reveal);
                        const b = p.lerp(128, p.blue(col),  reveal);
                        p.noStroke();
                        p.fill(r, g, b, reveal * 120);
                        p.rect(0, 0, p.width, p.height);
                    }
                }

                // ── 3. PLAYER INPUT ───────────────────────────────────────────
                // update runs AFTER allSprites.update() so touching.bottom is accurate
                player.update(keysDown);
                player.drawGlow();
                player.sprite.visible = player.isVisible(); // handles blink when invincible

                // ── 4. FRAGMENTS ──────────────────────────────────────────────
                for (const frag of fragments) {
                    frag.drawGlow(); // colored glow under the fragment
                    frag.update(player.sprite, onFragmentCollected); // checks overlap with player
                }

                // ── 5. ENEMIES ────────────────────────────────────────────────
                for (const enemy of enemies) {
                    enemy.update(); // patrol movement
                    // p5play overlap check — true when sprites are touching
                    if (player.sprite.overlaps(enemy.sprite)) {
                        const dead = player.loseLife(lives);
                        if (dead && gameState === 'playing') {
                            gameState = 'gameover';
                            // short delay so player sees what happened
                            setTimeout(() => push(`/gameover?level=${levelNumber}`), 500);
                        }
                    }
                }

                // ── 6. TAR PUDDLES ────────────────────────────────────────────
                for (const tar of tarPuddles) {
                    if (player.sprite.overlaps(tar.sprite)) {
                        const dead = player.loseLife(lives);
                        if (dead && gameState === 'playing') {
                            gameState = 'gameover';
                            setTimeout(() => push(`/gameover?level=${levelNumber}`), 500);
                        }
                    }
                }

                // ── 7. SPLAT EFFECTS ──────────────────────────────────────────
                // paint splashes that appear at fragment collection sites
                // they fade out and grow over time
                splats = splats.filter(s => s.alpha > 0); // remove faded splats
                for (const s of splats) {
                    p.push();
                    p.tint(255, s.alpha); // fade out using tint alpha
                    p.image(splatImg, s.x - s.size, s.y - s.size, s.size * 2, s.size * 2);
                    p.pop();
                    s.alpha -= 4;  // fade speed
                    s.size  += 1.2; // grow speed
                }

                // ── 8. FELL OFF SCREEN ────────────────────────────────────────
                // if player falls below the canvas, lose a life and respawn
                if (player.sprite.y > p.height + 100 && gameState === 'playing') {
                    const dead = player.loseLife(lives);
                    if (dead) {
                        gameState = 'gameover';
                        setTimeout(() => push(`/gameover?level=${levelNumber}`), 500);
                    }
                }

                // ── 9. DRAW SPRITES LAST ──────────────────────────────────────
                // p5play renders all sprites after everything else is drawn
                p.allSprites.draw();
            };

        };

        // p5 here is the global constructor from the CDN script
        // NOT the sketch parameter named "p"
        p5Instance = new p5(sketch);
    });

    onDestroy(() => {
        if (p5Instance) p5Instance.remove();
        window.removeEventListener('keydown', onKeyDown);
        window.removeEventListener('keyup',   onKeyUp);
        window.removeEventListener('blur',    onBlur);
    });
</script>

<!-- the div that p5 draws the canvas into -->
<div bind:this={canvasContainer} class="canvas-wrap"></div>

<style>
    .canvas-wrap {
        width: 800px;
        height: 450px;
        margin: 0 auto;
        position: relative;
    }
</style>