import { Box3, BoxGeometry, BufferGeometry, EdgesGeometry, Matrix4, Mesh, Object3D, Quaternion, Vector3, } from 'three';
import { LineMaterial } from 'three/addons/lines/LineMaterial.js';
import { LineSegments2 } from 'three/examples/jsm/lines/LineSegments2.js';
import { LineSegmentsGeometry } from 'three/examples/jsm/lines/LineSegmentsGeometry.js';
const box = new Box3();
const childBox = new Box3();
const inverseRootMatrixWorld = new Matrix4();
const rootMatrixWorld = new Matrix4();
const relativeMatrix = new Matrix4();
const scaleMatrix = new Matrix4();
const center = new Vector3();
const size = new Vector3();
const basis = new Matrix4();
const quaternion = new Quaternion();
const corners = [
    new Vector3(),
    new Vector3(),
    new Vector3(),
    new Vector3(),
    new Vector3(),
    new Vector3(),
    new Vector3(),
    new Vector3(),
];
const expandBoxByTransformedBox = (box, childBox, matrix) => {
    const min = childBox.min;
    const max = childBox.max;
    corners[0].set(min.x, min.y, min.z).applyMatrix4(matrix);
    corners[1].set(min.x, min.y, max.z).applyMatrix4(matrix);
    corners[2].set(min.x, max.y, min.z).applyMatrix4(matrix);
    corners[3].set(min.x, max.y, max.z).applyMatrix4(matrix);
    corners[4].set(max.x, min.y, min.z).applyMatrix4(matrix);
    corners[5].set(max.x, min.y, max.z).applyMatrix4(matrix);
    corners[6].set(max.x, max.y, min.z).applyMatrix4(matrix);
    corners[7].set(max.x, max.y, max.z).applyMatrix4(matrix);
    for (const corner of corners) {
        box.expandByPoint(corner);
    }
};
export class OBBHelper extends LineSegments2 {
    constructor(color = 0x000000, linewidth = 2) {
        const boxGeometry = new BoxGeometry();
        const edges = new EdgesGeometry(boxGeometry);
        const geometry = new LineSegmentsGeometry();
        geometry.setPositions(edges.getAttribute('position').array);
        edges.dispose();
        boxGeometry.dispose();
        const material = new LineMaterial({
            color,
            linewidth,
            depthTest: false,
            depthWrite: false,
            transparent: true,
        });
        super(geometry, material);
        this.matrixAutoUpdate = false;
        this.matrixWorldAutoUpdate = false;
        this.frustumCulled = false;
        this.renderOrder = 999;
    }
    setFromOBB(obb) {
        basis.setFromMatrix3(obb.rotation);
        quaternion.setFromRotationMatrix(basis);
        size.copy(obb.halfSize).multiplyScalar(2);
        this.matrix.compose(obb.center, quaternion, size);
        this.matrixWorld.copy(this.matrix);
        return this;
    }
    setFromObject(root) {
        root.updateWorldMatrix(true, true);
        rootMatrixWorld.copy(root.matrixWorld);
        inverseRootMatrixWorld.copy(rootMatrixWorld).invert();
        box.makeEmpty();
        root.traverse((child) => {
            const mesh = child;
            const geometry = mesh.geometry;
            if (!geometry)
                return;
            if (!geometry.boundingBox) {
                geometry.computeBoundingBox();
            }
            if (!geometry.boundingBox)
                return;
            // Transform this mesh's local bounding box into root-local space
            relativeMatrix.multiplyMatrices(inverseRootMatrixWorld, mesh.matrixWorld);
            childBox.copy(geometry.boundingBox);
            expandBoxByTransformedBox(box, childBox, relativeMatrix);
        });
        if (box.isEmpty()) {
            console.warn('[OBBHelper] No geometry found on object to compute OBB.');
            return this;
        }
        box.getCenter(center);
        box.getSize(size);
        // Place the helper at the center of the bounding box, in root-local space
        this.matrix.makeTranslation(center.x, center.y, center.z);
        // Then inherit the root's full world transform
        this.matrix.premultiply(rootMatrixWorld);
        // Scale the unit box to the OBB extents in root-local axes
        this.matrix.multiply(scaleMatrix.makeScale(size.x, size.y, size.z));
        this.matrixWorld.copy(this.matrix);
        return this;
    }
    dispose() {
        this.geometry.dispose();
        this.material.dispose();
    }
}
