"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.FlappyBirdGame = void 0;
const DEFAULT = { mode: 'default', value: 0 };
const YELLOW = { mode: 'palette', value: 220 };
const GREEN = { mode: 'palette', value: 2 };
const BROWN = { mode: 'palette', value: 130 };
const SKY_BG = { mode: 'palette', value: 25 };
const WHITE = { mode: 'palette', value: 15 };
const BIRD_COL = 5;
const GRAVITY = 0.4;
const FLAP_VELOCITY = -3;
const TERMINAL_VELOCITY = 4;
const TICK_INTERVAL_MS = 80;
const PIPE_SPACING = 22; // columns between pipe spawns
const GAP_SIZE = 7;
const PIPE_WIDTH = 2;
class FlappyBirdGame {
    name = 'Flappy Bird';
    width = 0;
    height = 0;
    birdY = 0;
    birdVelocity = 0;
    pipes = [];
    score = 0;
    _paused = false;
    _gameOver = false;
    started = false;
    tickAccumulator = 0;
    distanceSinceLastPipe = 0;
    init(width, height) {
        this.width = Math.max(10, width);
        this.height = Math.max(8, height);
        this.reset();
    }
    tick(deltaMs) {
        if (this._paused || this._gameOver || !this.started)
            return;
        this.tickAccumulator += deltaMs;
        const interval = Math.max(60, TICK_INTERVAL_MS - this.score);
        while (this.tickAccumulator >= interval) {
            this.tickAccumulator -= interval;
            this.step();
            if (this._gameOver)
                break;
        }
    }
    handleInput(key) {
        switch (key) {
            case 'space':
            case 'up':
                if (this._gameOver) {
                    this.reset();
                }
                else if (!this.started) {
                    this.started = true;
                    this.flap();
                }
                else if (!this._paused) {
                    this.flap();
                }
                break;
            case 'pause':
                this._paused ? this.resume() : this.pause();
                break;
            case 'reset':
                if (this._gameOver)
                    this.reset();
                break;
        }
    }
    getState() {
        const playableHeight = this.height - 1; // bottom row is ground
        const grid = [];
        // Fill sky
        for (let row = 0; row < this.height; row++) {
            grid[row] = [];
            for (let col = 0; col < this.width; col++) {
                grid[row][col] = { char: ' ', fg: DEFAULT, bg: SKY_BG };
            }
        }
        // Ground (bottom row)
        for (let col = 0; col < this.width; col++) {
            grid[this.height - 1][col] = { char: '▓', fg: BROWN, bg: DEFAULT };
        }
        // Pipes
        for (const pipe of this.pipes) {
            for (let pw = 0; pw < PIPE_WIDTH; pw++) {
                const col = Math.round(pipe.x) + pw;
                if (col < 0 || col >= this.width)
                    continue;
                for (let row = 0; row < playableHeight; row++) {
                    if (row < pipe.gapTop || row > pipe.gapBottom) {
                        grid[row][col] = { char: '█', fg: GREEN, bg: DEFAULT };
                    }
                }
            }
        }
        // Bird
        const birdRow = Math.round(this.birdY);
        if (birdRow >= 0 && birdRow < playableHeight && BIRD_COL < this.width) {
            grid[birdRow][BIRD_COL] = { char: '►', fg: YELLOW, bg: SKY_BG };
        }
        // Score display at top center
        const scoreStr = String(this.score);
        const startCol = Math.floor((this.width - scoreStr.length) / 2);
        for (let i = 0; i < scoreStr.length; i++) {
            const col = startCol + i;
            if (col >= 0 && col < this.width) {
                grid[0][col] = { char: scoreStr[i], fg: WHITE, bg: SKY_BG };
            }
        }
        let statusMessage;
        if (this._gameOver) {
            statusMessage = 'Game Over! Press SPACE';
        }
        else if (this._paused) {
            statusMessage = 'PAUSED - Ctrl+Space to resume';
        }
        else if (!this.started) {
            statusMessage = 'Press SPACE to start';
        }
        return {
            grid,
            score: this.score,
            status: this._gameOver ? 'gameover' : this._paused ? 'paused' : 'playing',
            statusMessage,
        };
    }
    resize(width, height) {
        const oldHeight = this.height;
        this.width = Math.max(10, width);
        this.height = Math.max(8, height);
        if (oldHeight > 0 && this.height !== oldHeight) {
            const ratio = (this.height - 1) / (oldHeight - 1);
            this.birdY *= ratio;
            for (const pipe of this.pipes) {
                pipe.gapTop = Math.round(pipe.gapTop * ratio);
                pipe.gapBottom = pipe.gapTop + pipe.gapSize - 1;
            }
        }
    }
    isPaused() { return this._paused; }
    pause() { this._paused = true; }
    resume() { if (!this._gameOver)
        this._paused = false; }
    isGameOver() { return this._gameOver; }
    reset() {
        this._gameOver = false;
        this._paused = false;
        this.started = false;
        this.score = 0;
        this.tickAccumulator = 0;
        this.birdY = Math.floor((this.height - 1) / 2);
        this.birdVelocity = 0;
        this.pipes = [];
        this.distanceSinceLastPipe = 0; // grace period: first pipe spawns after full PIPE_SPACING distance
    }
    flap() {
        this.birdVelocity = FLAP_VELOCITY;
    }
    step() {
        const playableHeight = this.height - 1;
        // Apply gravity
        this.birdVelocity += GRAVITY;
        if (this.birdVelocity > TERMINAL_VELOCITY) {
            this.birdVelocity = TERMINAL_VELOCITY;
        }
        this.birdY += this.birdVelocity;
        // Ceiling/floor collision
        if (this.birdY < 0) {
            this.birdY = 0;
            this.birdVelocity = 0;
        }
        if (this.birdY >= playableHeight) {
            this.birdY = playableHeight - 1;
            this._gameOver = true;
            return;
        }
        // Scroll pipes left
        for (const pipe of this.pipes) {
            pipe.x -= 1;
        }
        // Remove pipes that scrolled off screen
        this.pipes = this.pipes.filter(p => p.x >= -PIPE_WIDTH);
        // Spawn new pipes
        this.distanceSinceLastPipe += 1;
        if (this.distanceSinceLastPipe >= PIPE_SPACING) {
            this.distanceSinceLastPipe = 0;
            this.spawnPipe();
        }
        // Scoring: bird passes a pipe
        const birdRow = Math.round(this.birdY);
        for (const pipe of this.pipes) {
            if (!pipe.scored && Math.round(pipe.x) + PIPE_WIDTH - 1 < BIRD_COL) {
                pipe.scored = true;
                this.score += 1;
            }
        }
        // Collision with pipes
        for (const pipe of this.pipes) {
            for (let pw = 0; pw < PIPE_WIDTH; pw++) {
                const pipeCol = Math.round(pipe.x) + pw;
                if (pipeCol !== BIRD_COL)
                    continue;
                if (birdRow < pipe.gapTop || birdRow > pipe.gapBottom) {
                    this._gameOver = true;
                    return;
                }
            }
        }
    }
    spawnPipe() {
        const playableHeight = this.height - 1;
        const gapSize = Math.min(Math.max(4, GAP_SIZE - Math.floor(this.score / 8)), playableHeight - 3);
        if (gapSize < 2)
            return;
        const minGapTop = 1;
        const maxGapTop = playableHeight - gapSize - 1;
        if (maxGapTop <= minGapTop)
            return;
        const gapTop = minGapTop + Math.floor(Math.random() * (maxGapTop - minGapTop + 1));
        this.pipes.push({
            x: this.width - 1,
            gapTop,
            gapBottom: gapTop + gapSize - 1,
            gapSize,
            scored: false,
        });
    }
}
exports.FlappyBirdGame = FlappyBirdGame;
//# sourceMappingURL=flappy-bird.js.map