'use strict';

/* ────────────────────────────────────────────────────────────────
   Phase 7 Sprint 7.1 — Performance & Optimization
   
   Object pooling, LOD, Culling, Asset streaming
──────────────────────────────────────────────────────────────── */
class ObjectPool {
    constructor(factory, prewarm = 0, maxSize = 10000) {
        this.pool = [];
        this.active = [];
        this.factory = factory;
        this.maxSize = maxSize;
        for (let i = 0; i < prewarm; i++) {
            this.pool.push(factory());
        }
    }
    acquire() {
        let obj = this.pool.pop();
        if (!obj)
            obj = this.factory();
        obj.reset();
        this.active.push(obj);
        return obj;
    }
    release(obj) {
        const idx = this.active.indexOf(obj);
        if (idx >= 0) {
            this.active.splice(idx, 1);
            if (this.pool.length < this.maxSize) {
                this.pool.push(obj);
            }
        }
    }
    releaseAll() {
        while (this.active.length > 0) {
            const obj = this.active.pop();
            if (this.pool.length < this.maxSize)
                this.pool.push(obj);
        }
    }
    activeCount() { return this.active.length; }
    pooledCount() { return this.pool.length; }
    totalCount() { return this.active.length + this.pool.length; }
}
const PRIORITY_ORDER = {
    critical: 0, high: 1, normal: 2, low: 3,
};
class AssetStreamer {
    constructor(maxConcurrent = 4) {
        this.queue = [];
        this.loading = new Map();
        this.loaded = new Map();
        this.maxConcurrent = maxConcurrent;
    }
    enqueue(id, url, priority = 'normal') {
        var _a;
        if (this.loaded.has(id)) {
            return { id, url, priority, loaded: true, progress: 1, data: this.loaded.get(id), error: null };
        }
        const existing = (_a = this.queue.find(r => r.id === id)) !== null && _a !== void 0 ? _a : this.loading.get(id);
        if (existing)
            return existing;
        const req = { id, url, priority, loaded: false, progress: 0, data: null, error: null };
        this.queue.push(req);
        this.queue.sort((a, b) => PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority]);
        this.processQueue();
        return req;
    }
    isLoaded(id) { return this.loaded.has(id); }
    get(id) { var _a; return (_a = this.loaded.get(id)) !== null && _a !== void 0 ? _a : null; }
    processQueue() {
        while (this.loading.size < this.maxConcurrent && this.queue.length > 0) {
            const req = this.queue.shift();
            this.loading.set(req.id, req);
            this.loadAsset(req);
        }
    }
    async loadAsset(req) {
        var _a;
        try {
            const response = await fetch(req.url);
            if (!response.ok) {
                req.error = `HTTP ${response.status}`;
            }
            else {
                const contentType = (_a = response.headers.get('content-type')) !== null && _a !== void 0 ? _a : '';
                if (contentType.includes('json')) {
                    req.data = await response.json();
                }
                else if (contentType.includes('image')) {
                    req.data = await response.blob();
                }
                else {
                    req.data = await response.arrayBuffer();
                }
                req.loaded = true;
                req.progress = 1;
                this.loaded.set(req.id, req.data);
            }
        }
        catch (err) {
            req.error = err instanceof Error ? err.message : 'Unknown error';
        }
        finally {
            this.loading.delete(req.id);
            this.processQueue();
        }
    }
    getPendingCount() { return this.queue.length + this.loading.size; }
    getLoadedCount() { return this.loaded.size; }
    clear() {
        this.queue.length = 0;
        this.loaded.clear();
    }
}
function selectLODLevel(group, distance) {
    for (let i = 0; i < group.levels.length; i++) {
        if (distance <= group.levels[i].distance)
            return i;
    }
    return group.levels.length - 1; // farthest LOD
}
function updateLODGroups(groups, cameraPos, positions) {
    for (const group of groups) {
        const pos = positions.get(group.entityId);
        if (!pos)
            continue;
        const dx = cameraPos.x - pos.x;
        const dy = cameraPos.y - pos.y;
        const dz = cameraPos.z - pos.z;
        const dist = Math.sqrt(dx * dx + dy * dy + dz * dz);
        group.currentLevel = selectLODLevel(group, dist);
    }
}
function aabbInFrustum(aabb, planes) {
    for (const p of planes) {
        // Test the positive vertex
        const px = p.nx > 0 ? aabb.maxX : aabb.minX;
        const py = p.ny > 0 ? aabb.maxY : aabb.minY;
        const pz = p.nz > 0 ? aabb.maxZ : aabb.minZ;
        if (p.nx * px + p.ny * py + p.nz * pz + p.d < 0)
            return false;
    }
    return true;
}
/** Simple 2D frustum check for 2D camera views. */
function rectInView(rx, ry, rw, rh, viewX, viewY, viewW, viewH) {
    return rx + rw > viewX && rx < viewX + viewW &&
        ry + rh > viewY && ry < viewY + viewH;
}
/* ══════════════════════════════════════════════════════════════
   Memory Optimization helpers
   ══════════════════════════════════════════════════════════════ */
function measureMemory() {
    if (typeof performance !== 'undefined' && 'memory' in performance) {
        const mem = performance.memory;
        return { heapUsed: mem.usedJSHeapSize, heapTotal: mem.totalJSHeapSize };
    }
    return null;
}
function formatBytes(bytes) {
    if (bytes < 1024)
        return `${bytes} B`;
    if (bytes < 1024 * 1024)
        return `${(bytes / 1024).toFixed(1)} KB`;
    if (bytes < 1024 * 1024 * 1024)
        return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
    return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
}

exports.AssetStreamer = AssetStreamer;
exports.ObjectPool = ObjectPool;
exports.aabbInFrustum = aabbInFrustum;
exports.formatBytes = formatBytes;
exports.measureMemory = measureMemory;
exports.rectInView = rectInView;
exports.selectLODLevel = selectLODLevel;
exports.updateLODGroups = updateLODGroups;
//# sourceMappingURL=Performance.js.map
