import { getEngine, getDevice } from '../app/api.js';
import { BoundingBox } from '../utility/bounding_volume.js';
import { Vector3 } from '@zephyr3d/base';

const geometryCacheMeshStates = new WeakMap();
/**
 * Creates a reusable geometry cache state buffer for a frame layout.
 *
 * @public
 */ function createGeometryCacheState(frame) {
    return {
        positions: new Float32Array(frame.positions.length),
        normals: frame.normals ? new Float32Array(frame.normals.length) : null,
        boundingBox: new BoundingBox()
    };
}
/**
 * Linearly interpolates two geometry-cache bounding boxes.
 *
 * @public
 */ function mixGeometryCacheBoundingBox(a, b, t) {
    return new BoundingBox(new Vector3(a.minPoint.x + (b.minPoint.x - a.minPoint.x) * t, a.minPoint.y + (b.minPoint.y - a.minPoint.y) * t, a.minPoint.z + (b.minPoint.z - a.minPoint.z) * t), new Vector3(a.maxPoint.x + (b.maxPoint.x - a.maxPoint.x) * t, a.maxPoint.y + (b.maxPoint.y - a.maxPoint.y) * t, a.maxPoint.z + (b.maxPoint.z - a.maxPoint.z) * t));
}
/**
 * Ensures the target mesh has GPU buffers compatible with the geometry cache state.
 *
 * @public
 */ function ensureGeometryCacheMeshBinding(meshBindings, mesh, state, errorPrefix) {
    let binding = meshBindings.get(mesh);
    if (!binding) {
        const primitive = mesh.primitive;
        const meshState = geometryCacheMeshStates.get(mesh);
        const originalPrimitive = meshState?.originalPrimitive ?? primitive;
        if (!originalPrimitive) {
            throw new Error(`${errorPrefix}: target mesh has no primitive`);
        }
        validateGeometryCacheState(originalPrimitive, state, errorPrefix);
        const ensuredState = meshState ? ensureGeometryCacheNormalBuffer(meshState, originalPrimitive, state, errorPrefix) : createGeometryCacheMeshState(originalPrimitive, state);
        geometryCacheMeshStates.set(mesh, ensuredState);
        if (mesh.primitive !== ensuredState.cachePrimitive) {
            mesh.primitive = ensuredState.cachePrimitive;
        }
        binding = {
            originalPrimitive,
            primitive: ensuredState.cachePrimitive,
            positionBuffer: ensuredState.positionBuffer,
            normalBuffer: ensuredState.normalBuffer
        };
        meshBindings.set(mesh, binding);
    }
    return binding;
}
/**
 * Restores a mesh primitive after geometry cache playback.
 *
 * @public
 */ async function restoreGeometryCacheMeshBinding(mesh) {
    const meshState = geometryCacheMeshStates.get(mesh);
    if (!meshState) {
        return false;
    }
    const primitiveToRestore = await resolveOriginalPrimitive(meshState);
    if (mesh.primitive === meshState.cachePrimitive) {
        mesh.primitive = primitiveToRestore;
    }
    mesh.setAnimatedBoundingBox(null);
    mesh.scene?.queueUpdateNode(mesh);
    geometryCacheMeshStates.delete(mesh);
    return true;
}
function validateGeometryCacheState(primitive, state, errorPrefix) {
    const expectedPositionLength = primitive.getNumVertices() * 3;
    if (state.positions.length !== expectedPositionLength) {
        throw new Error(`${errorPrefix}: position count mismatch, expected ${expectedPositionLength}, got ${state.positions.length}`);
    }
    if (state.normals) {
        const expectedNormalLength = primitive.getNumVertices() * 3;
        if (state.normals.length !== expectedNormalLength) {
            throw new Error(`${errorPrefix}: normal count mismatch, expected ${expectedNormalLength}, got ${state.normals.length}`);
        }
    }
}
function createGeometryCacheMeshState(originalPrimitive, state) {
    const cachePrimitive = originalPrimitive.clone();
    cachePrimitive.setBoundingVolume(originalPrimitive.getBoundingVolume());
    const primitiveAssetId = getEngine().resourceManager.getAssetId(originalPrimitive);
    if (primitiveAssetId) {
        getEngine().resourceManager.setAssetId(cachePrimitive, primitiveAssetId);
    }
    cachePrimitive.removeVertexBuffer('position');
    const positionBuffer = getDevice().createVertexBuffer('position_f32x3', state.positions, {
        dynamic: true
    });
    cachePrimitive.setVertexBuffer(positionBuffer);
    let normalBuffer = null;
    if (state.normals) {
        cachePrimitive.removeVertexBuffer('normal');
        normalBuffer = getDevice().createVertexBuffer('normal_f32x3', state.normals, {
            dynamic: true
        });
        cachePrimitive.setVertexBuffer(normalBuffer);
    }
    return {
        originalPrimitive,
        originalPrimitiveAssetId: primitiveAssetId ?? '',
        cachePrimitive,
        positionBuffer,
        normalBuffer
    };
}
function ensureGeometryCacheNormalBuffer(meshState, originalPrimitive, state, errorPrefix) {
    if (state.normals && !meshState.normalBuffer) {
        const expectedNormalLength = originalPrimitive.getNumVertices() * 3;
        if (state.normals.length !== expectedNormalLength) {
            throw new Error(`${errorPrefix}: normal count mismatch, expected ${expectedNormalLength}, got ${state.normals.length}`);
        }
        meshState.cachePrimitive.removeVertexBuffer('normal');
        meshState.normalBuffer = getDevice().createVertexBuffer('normal_f32x3', state.normals, {
            dynamic: true
        });
        meshState.cachePrimitive.setVertexBuffer(meshState.normalBuffer);
    }
    return meshState;
}
async function resolveOriginalPrimitive(meshState) {
    if (meshState.originalPrimitiveAssetId) {
        const restoredPrimitive = await getEngine().resourceManager.fetchPrimitive(meshState.originalPrimitiveAssetId);
        if (restoredPrimitive) {
            return restoredPrimitive;
        }
    }
    return meshState.originalPrimitive;
}

export { createGeometryCacheState, ensureGeometryCacheMeshBinding, mixGeometryCacheBoundingBox, restoreGeometryCacheMeshBinding };
//# sourceMappingURL=geometry_cache_utils.js.map
