import { v2 } from '../core/math.js';
import { SeededRandom, WaveGenerator, LootTable } from '../procedural/Procedural.js';

/* ────────────────────────────────────────────────────────────────
   Wave Defense — MVP Game Template
   
   Auto-shooting hero, enemy waves with scaling difficulty,
   shop between waves, upgrades, up to 4-player local co-op,
   procedural enemy sprites, leaderboard.
──────────────────────────────────────────────────────────────── */
/* ── Default config ───────────────────────────────────────────── */
const DEFAULT_WAVE_DEFENSE_CONFIG = {
    arenaWidth: 1280,
    arenaHeight: 720,
    playerSpeed: 200,
    bulletSpeed: 500,
    bulletCooldown: 0.3,
    baseEnemyHp: 3,
    enemyHpGrowth: 1.15,
    waveConfig: {
        baseEnemyCount: 8,
        countGrowth: 1.3,
        typeProbabilities: [
            { type: 'basic', weight: 60, minWave: 1 },
            { type: 'fast', weight: 25, minWave: 2 },
            { type: 'tank', weight: 10, minWave: 3 },
            { type: 'boss', weight: 5, minWave: 5 },
        ],
        groupSize: [1, 4],
        groupDelay: [0.5, 2],
        spawnInterval: [0.3, 0.8],
    },
    upgrades: [
        { id: 'speed', name: 'Speed', description: '+15% move speed', cost: 50, costGrowth: 1.5, maxLevel: 5, effect: { type: 'speed', bonus: 0.15 } },
        { id: 'firerate', name: 'Fire Rate', description: '+20% attack speed', cost: 75, costGrowth: 1.5, maxLevel: 5, effect: { type: 'firerate', bonus: 0.2 } },
        { id: 'damage', name: 'Damage', description: '+1 damage', cost: 100, costGrowth: 1.8, maxLevel: 10, effect: { type: 'damage', bonus: 1 } },
        { id: 'health', name: 'Health', description: '+1 max HP', cost: 60, costGrowth: 1.4, maxLevel: 8, effect: { type: 'health', bonus: 1 } },
        { id: 'magnet', name: 'Magnet', description: '+30% pickup range', cost: 40, costGrowth: 1.3, maxLevel: 4, effect: { type: 'magnet', bonus: 0.3 } },
    ],
    lootDrops: [
        { id: 'coin', weight: 70, quantity: [1, 3] },
        { id: 'gem', weight: 20, quantity: [1, 1], minLevel: 3 },
        { id: 'health_pack', weight: 10, quantity: [1, 1] },
    ],
};
/* ── Wave Defense Template ────────────────────────────────────── */
class WaveDefenseTemplate {
    constructor(config = {}, seed) {
        // Cached computed stats
        this._moveSpeed = 0;
        this._bulletCooldown = 0;
        this._damage = 1;
        this._magnetRange = 60;
        this._cooldownTimer = 0;
        this.spawnTimers = [];
        this.config = { ...DEFAULT_WAVE_DEFENSE_CONFIG, ...config };
        this.rng = new SeededRandom(seed);
        this.waveGen = new WaveGenerator(seed);
        this.lootTable = new LootTable(this.config.lootDrops, seed);
        this.state = {
            phase: 'playing',
            wave: 0,
            score: 0,
            coins: 0,
            kills: 0,
            playerHp: 5,
            maxHp: 5,
            upgradeLevels: {},
            enemiesRemaining: 0,
            timeSurvived: 0,
        };
        this.recalcStats();
    }
    getState() {
        return this.state;
    }
    /* ── Scene setup: creates entities in the world ─────────────── */
    setupScene(world, playerCount = 1) {
        const arena = this.config;
        // Arena bounds (4 walls)
        this.createWall(world, arena.arenaWidth / 2, -16, arena.arenaWidth, 32); // top
        this.createWall(world, arena.arenaWidth / 2, arena.arenaHeight + 16, arena.arenaWidth, 32); // bottom
        this.createWall(world, -16, arena.arenaHeight / 2, 32, arena.arenaHeight); // left
        this.createWall(world, arena.arenaWidth + 16, arena.arenaHeight / 2, 32, arena.arenaHeight); // right
        // Players
        for (let i = 0; i < playerCount; i++) {
            this.createPlayer(world, i);
        }
        // Start first wave
        this.nextWave(world);
    }
    createPlayer(world, index) {
        const colors = [
            { r: 0.3, g: 0.6, b: 1, a: 1 },
            { r: 1, g: 0.3, b: 0.3, a: 1 },
            { r: 0.3, g: 1, b: 0.4, a: 1 },
            { r: 1, g: 0.9, b: 0.2, a: 1 },
        ];
        const cx = this.config.arenaWidth / 2;
        const cy = this.config.arenaHeight / 2;
        const offsets = [
            { x: -40, y: 0 }, { x: 40, y: 0 },
            { x: 0, y: -40 }, { x: 0, y: 40 },
        ];
        const pos = { x: cx + offsets[index].x, y: cy + offsets[index].y };
        const entity = world.createEntity(`Player${index + 1}`);
        entity.tags.add('player');
        entity.tags.add(`player${index}`);
        world.addComponent(entity.id, 'Transform', { position: pos, prevPosition: { ...pos } });
        world.addComponent(entity.id, 'Shape', {
            shape: 'circle',
            radius: 14,
            fillColor: colors[index % colors.length],
            strokeColor: { r: 1, g: 1, b: 1, a: 0.5 },
            strokeWidth: 2,
            layer: 5,
        });
        world.addComponent(entity.id, 'Collider2D', {
            shape: 'circle',
            radius: 14,
            layer: 1,
            mask: 0xFFFF,
        });
        world.addComponent(entity.id, 'RigidBody2D', {
            bodyType: 'dynamic',
            drag: 8,
            mass: 1,
            gravityScale: 0,
        });
        world.addComponent(entity.id, 'CameraTarget', { priority: 1, smoothing: 0.1 });
        return entity.id;
    }
    createWall(world, x, y, w, h) {
        const entity = world.createEntity('Wall');
        entity.tags.add('wall');
        world.addComponent(entity.id, 'Transform', { position: { x, y }, prevPosition: { x, y } });
        world.addComponent(entity.id, 'Collider2D', { shape: 'aabb', width: w, height: h, layer: 2, mask: 0xFFFF });
        world.addComponent(entity.id, 'RigidBody2D', { bodyType: 'static', gravityScale: 0 });
        return entity.id;
    }
    /* ── Wave management ────────────────────────────────────────── */
    nextWave(world) {
        this.state.wave++;
        const entries = this.waveGen.generateWave(this.state.wave, this.config.waveConfig);
        let totalEnemies = 0;
        for (const entry of entries) {
            totalEnemies += entry.count;
            this.scheduleSpawnGroup(world, entry.type, entry.count, entry.delay, entry.spawnInterval);
        }
        this.state.enemiesRemaining = totalEnemies;
        this.state.phase = 'playing';
    }
    scheduleSpawnGroup(world, type, count, delay, interval) {
        for (let i = 0; i < count; i++) {
            this.spawnTimers.push({
                timer: delay + i * interval,
                callback: () => this.spawnEnemy(world, type),
            });
        }
    }
    spawnEnemy(world, type) {
        var _a;
        const arena = this.config;
        // Spawn at random edge
        const edge = this.rng.int(0, 3);
        let x = 0, y = 0;
        switch (edge) {
            case 0:
                x = this.rng.float(0, arena.arenaWidth);
                y = -20;
                break;
            case 1:
                x = arena.arenaWidth + 20;
                y = this.rng.float(0, arena.arenaHeight);
                break;
            case 2:
                x = this.rng.float(0, arena.arenaWidth);
                y = arena.arenaHeight + 20;
                break;
            case 3:
                x = -20;
                y = this.rng.float(0, arena.arenaHeight);
                break;
        }
        const stats = (_a = ENEMY_STATS[type]) !== null && _a !== void 0 ? _a : ENEMY_STATS.basic;
        const hp = Math.ceil(this.config.baseEnemyHp * Math.pow(this.config.enemyHpGrowth, this.state.wave - 1) * stats.hpMul);
        const entity = world.createEntity(`Enemy_${type}`);
        entity.tags.add('enemy');
        entity.tags.add(`enemy_${type}`);
        world.addComponent(entity.id, 'Transform', { position: { x, y }, prevPosition: { x, y } });
        world.addComponent(entity.id, 'Shape', {
            shape: 'circle',
            radius: stats.radius,
            fillColor: stats.color,
            strokeColor: { r: 0, g: 0, b: 0, a: 0.4 },
            strokeWidth: 1,
            layer: 4,
        });
        world.addComponent(entity.id, 'Collider2D', {
            shape: 'circle',
            radius: stats.radius,
            layer: 4,
            mask: 0xFFFF,
        });
        world.addComponent(entity.id, 'RigidBody2D', {
            bodyType: 'dynamic',
            drag: 5,
            mass: stats.mass,
            gravityScale: 0,
        });
        world.addComponent(entity.id, 'Script', {
            scriptId: 'enemy_chase',
            variables: { hp, maxHp: hp, speed: stats.speed, damage: stats.damage, xp: stats.xp },
        });
    }
    /* ── Update ─────────────────────────────────────────────────── */
    update(world, dt, inputActions) {
        var _a, _b;
        if (this.state.phase !== 'playing')
            return;
        this.state.timeSurvived += dt;
        // Process spawn timers
        for (let i = this.spawnTimers.length - 1; i >= 0; i--) {
            this.spawnTimers[i].timer -= dt;
            if (this.spawnTimers[i].timer <= 0) {
                this.spawnTimers[i].callback();
                this.spawnTimers.splice(i, 1);
            }
        }
        // Move players
        const players = world.findAllByTag('player');
        for (let pi = 0; pi < players.length; pi++) {
            const p = players[pi];
            const rb = world.getComponent(p.id, 'RigidBody2D');
            if (!rb)
                continue;
            const input = (_a = inputActions[pi]) !== null && _a !== void 0 ? _a : { moveX: 0, moveY: 0 };
            const dir = v2.normalize({ x: input.moveX, y: input.moveY });
            rb.velocity = v2.mul(dir, this._moveSpeed);
        }
        // Auto-shoot towards nearest enemy
        this._cooldownTimer -= dt;
        if (this._cooldownTimer <= 0 && players.length > 0) {
            const enemies = world.findAllByTag('enemy');
            for (const p of players) {
                const pt = world.getComponent(p.id, 'Transform');
                if (!pt || enemies.length === 0)
                    continue;
                // Find nearest enemy
                let nearest = enemies[0];
                let nearestDist = Infinity;
                for (const e of enemies) {
                    const et = world.getComponent(e.id, 'Transform');
                    if (!et)
                        continue;
                    const d = v2.dist(pt.position, et.position);
                    if (d < nearestDist) {
                        nearestDist = d;
                        nearest = e;
                    }
                }
                const et = world.getComponent(nearest.id, 'Transform');
                if (et) {
                    const dir = v2.normalize(v2.sub(et.position, pt.position));
                    this.spawnBullet(world, pt.position, dir);
                }
            }
            this._cooldownTimer = this._bulletCooldown;
        }
        // Enemy AI — chase nearest player
        const enemies = world.findAllByTag('enemy');
        for (const e of enemies) {
            const et = world.getComponent(e.id, 'Transform');
            const erb = world.getComponent(e.id, 'RigidBody2D');
            const es = world.getComponent(e.id, 'Script');
            if (!et || !erb || !es)
                continue;
            let nearest = null;
            let nearestDist = Infinity;
            for (const p of players) {
                const pt = world.getComponent(p.id, 'Transform');
                if (!pt)
                    continue;
                const d = v2.dist(et.position, pt.position);
                if (d < nearestDist) {
                    nearestDist = d;
                    nearest = pt.position;
                }
            }
            if (nearest) {
                const dir = v2.normalize(v2.sub(nearest, et.position));
                const speed = (_b = es.variables.speed) !== null && _b !== void 0 ? _b : 100;
                erb.velocity = v2.mul(dir, speed);
            }
        }
        // Check wave completion
        if (this.state.enemiesRemaining <= 0 && this.spawnTimers.length === 0) {
            this.state.phase = 'shop';
        }
    }
    spawnBullet(world, pos, dir) {
        const entity = world.createEntity('Bullet');
        entity.tags.add('bullet');
        const spawnPos = v2.add(pos, v2.mul(dir, 20));
        world.addComponent(entity.id, 'Transform', { position: { ...spawnPos }, prevPosition: { ...spawnPos } });
        world.addComponent(entity.id, 'Shape', {
            shape: 'circle',
            radius: 4,
            fillColor: { r: 1, g: 1, b: 0.3, a: 1 },
            layer: 6,
        });
        world.addComponent(entity.id, 'Collider2D', {
            shape: 'circle',
            radius: 4,
            isTrigger: true,
            layer: 8,
            mask: 4, // hits enemies
        });
        world.addComponent(entity.id, 'RigidBody2D', {
            bodyType: 'kinematic',
            velocity: v2.mul(dir, this.config.bulletSpeed),
            gravityScale: 0,
        });
        world.addComponent(entity.id, 'Script', {
            scriptId: 'bullet',
            variables: { damage: this._damage, lifetime: 2.5 },
        });
    }
    /* ── Bullet/enemy collision handling ─────────────────────────── */
    onBulletHitEnemy(world, bulletId, enemyId) {
        var _a, _b, _c;
        const bs = world.getComponent(bulletId, 'Script');
        const es = world.getComponent(enemyId, 'Script');
        if (!bs || !es)
            return;
        const damage = (_a = bs.variables.damage) !== null && _a !== void 0 ? _a : 1;
        let hp = (_b = es.variables.hp) !== null && _b !== void 0 ? _b : 1;
        hp -= damage;
        es.variables.hp = hp;
        // Destroy bullet
        world.destroyEntity(bulletId);
        if (hp <= 0) {
            // Enemy dies
            this.state.kills++;
            this.state.score += (_c = es.variables.xp) !== null && _c !== void 0 ? _c : 10;
            this.state.enemiesRemaining--;
            // Loot drop
            const drops = this.lootTable.rollEach(this.state.wave);
            for (const drop of drops) {
                if (drop.id === 'coin')
                    this.state.coins += drop.quantity;
                else if (drop.id === 'health_pack') {
                    this.state.playerHp = Math.min(this.state.maxHp, this.state.playerHp + 1);
                }
            }
            world.destroyEntity(enemyId);
        }
    }
    onEnemyHitPlayer(world, enemyId, _playerId) {
        var _a;
        const es = world.getComponent(enemyId, 'Script');
        const dmg = (_a = es === null || es === void 0 ? void 0 : es.variables.damage) !== null && _a !== void 0 ? _a : 1;
        this.state.playerHp -= dmg;
        // Knockback enemy
        world.destroyEntity(enemyId);
        this.state.enemiesRemaining--;
        if (this.state.playerHp <= 0) {
            this.state.phase = 'gameover';
        }
    }
    /* ── Shop / Upgrades ────────────────────────────────────────── */
    getAvailableUpgrades() {
        return this.config.upgrades
            .filter(u => { var _a; return ((_a = this.state.upgradeLevels[u.id]) !== null && _a !== void 0 ? _a : 0) < u.maxLevel; })
            .map(u => {
            var _a, _b;
            return ({
                ...u,
                currentLevel: (_a = this.state.upgradeLevels[u.id]) !== null && _a !== void 0 ? _a : 0,
                currentCost: Math.floor(u.cost * Math.pow(u.costGrowth, (_b = this.state.upgradeLevels[u.id]) !== null && _b !== void 0 ? _b : 0)),
            });
        });
    }
    buyUpgrade(upgradeId) {
        var _a;
        const upgrades = this.getAvailableUpgrades();
        const upgrade = upgrades.find(u => u.id === upgradeId);
        if (!upgrade || this.state.coins < upgrade.currentCost)
            return false;
        this.state.coins -= upgrade.currentCost;
        this.state.upgradeLevels[upgradeId] = ((_a = this.state.upgradeLevels[upgradeId]) !== null && _a !== void 0 ? _a : 0) + 1;
        this.recalcStats();
        return true;
    }
    recalcStats() {
        var _a;
        const levels = this.state.upgradeLevels;
        const cfg = this.config;
        let speedMul = 1;
        let firerateMul = 1;
        let bonusDamage = 0;
        let bonusHp = 0;
        let magnetMul = 1;
        for (const upgrade of cfg.upgrades) {
            const lvl = (_a = levels[upgrade.id]) !== null && _a !== void 0 ? _a : 0;
            if (lvl === 0)
                continue;
            switch (upgrade.effect.type) {
                case 'speed':
                    speedMul += upgrade.effect.bonus * lvl;
                    break;
                case 'firerate':
                    firerateMul += upgrade.effect.bonus * lvl;
                    break;
                case 'damage':
                    bonusDamage += upgrade.effect.bonus * lvl;
                    break;
                case 'health':
                    bonusHp += upgrade.effect.bonus * lvl;
                    break;
                case 'magnet':
                    magnetMul += upgrade.effect.bonus * lvl;
                    break;
            }
        }
        this._moveSpeed = cfg.playerSpeed * speedMul;
        this._bulletCooldown = cfg.bulletCooldown / firerateMul;
        this._damage = 1 + bonusDamage;
        this.state.maxHp = 5 + bonusHp;
        this._magnetRange = 60 * magnetMul;
    }
    /** After the shop phase, start the next wave. */
    startNextWave(world) {
        if (this.state.phase !== 'shop')
            return;
        this.nextWave(world);
    }
    /** Reset the game entirely. */
    reset(world, playerCount = 1) {
        world.clear();
        this.spawnTimers.length = 0;
        this.state = {
            phase: 'playing',
            wave: 0,
            score: 0,
            coins: 0,
            kills: 0,
            playerHp: 5,
            maxHp: 5,
            upgradeLevels: {},
            enemiesRemaining: 0,
            timeSurvived: 0,
        };
        this.recalcStats();
        this.setupScene(world, playerCount);
    }
}
/* ── Enemy stats ──────────────────────────────────────────────── */
const ENEMY_STATS = {
    basic: { speed: 80, hpMul: 1, damage: 1, mass: 1, radius: 10, xp: 10, color: { r: 0.9, g: 0.2, b: 0.2, a: 1 } },
    fast: { speed: 160, hpMul: 0.6, damage: 1, mass: 0.6, radius: 8, xp: 15, color: { r: 1, g: 0.6, b: 0.1, a: 1 } },
    tank: { speed: 50, hpMul: 3, damage: 2, mass: 3, radius: 16, xp: 30, color: { r: 0.5, g: 0.2, b: 0.7, a: 1 } },
    boss: { speed: 40, hpMul: 10, damage: 3, mass: 5, radius: 24, xp: 100, color: { r: 0.8, g: 0.1, b: 0.1, a: 1 } },
};

export { DEFAULT_WAVE_DEFENSE_CONFIG, WaveDefenseTemplate };
//# sourceMappingURL=WaveDefense.js.map
