import { Box3, BufferAttribute } from 'three';
import { MeshBVH } from '../core/MeshBVH.js';

export class GenerateMeshBVHWorker {

	constructor() {

		this.running = false;
		this.worker = new Worker( new URL( './generateAsync.worker.js', import.meta.url ), { type: 'module' } );

	}

	generate( geometry, options = {} ) {

		if ( this.running ) {

			throw new Error( 'GenerateMeshBVHWorker: Already running job.' );

		}

		const { worker } = this;
		this.running = true;

		return new Promise( ( resolve, reject ) => {

			worker.onmessage = e => {

				this.running = false;
				const { data } = e;

				if ( data.error ) {

					reject( new Error( data.error ) );
					worker.onmessage = null;

				} else if ( data.serialized ) {

					const { serialized, position } = data;
					const bvh = MeshBVH.deserialize( serialized, geometry, { setIndex: false } );
					const boundsOptions = Object.assign( {

						setBoundingBox: true,

					}, options );

					// we need to replace the arrays because they're neutered entirely by the
					// webworker transfer.
					geometry.attributes.position.array = position;
					if ( geometry.index ) {

						geometry.index.array = serialized.index;

					} else {

						const newIndex = new BufferAttribute( serialized.index, 1, false );
						geometry.setIndex( newIndex );

					}

					if ( boundsOptions.setBoundingBox ) {

						geometry.boundingBox = bvh.getBoundingBox( new Box3() );

					}

					resolve( bvh );
					worker.onmessage = null;

				} else if ( options.onProgress ) {

					options.onProgress( data.progress );

				}

			};

			const index = geometry.index ? geometry.index.array : null;
			const position = geometry.attributes.position.array;

			if ( position.isInterleavedBufferAttribute || index && index.isInterleavedBufferAttribute ) {

				throw new Error( 'GenerateMeshBVHWorker: InterleavedBufferAttribute are not supported for the geometry attributes.' );

			}

			const transferrables = [ position ];
			if ( index ) {

				transferrables.push( index );

			}

			worker.postMessage( {

				index,
				position,
				options: {
					...options,
					onProgress: null,
					includedProgressCallback: Boolean( options.onProgress ),
					groups: [ ... geometry.groups ],
				},

			}, transferrables.map( arr => arr.buffer ) );

		} );

	}

	dispose() {

		this.worker.terminate();

	}

	terminate() {

		console.warn( 'GenerateMeshBVHWorker: "terminate" is deprecated. Use "dispose" instead.' );
		this.dispose();

	}

}

@@ -278,249 +278,756 @@
  );%0A
-%0A%09%7D%0A%0A%09generate( geometry, options = %7B%7D ) %7B%0A%0A%09%09if ( this.running ) %7B%0A%0A%09%09%09throw new Error( 'GenerateMeshBVHWorker: Already running job.' );%0A%0A%09%09%7D%0A%0A%09%09const %7B worker %7D = this;%0A%09%09this.running = true;%0A%0A%09%09return new Promise( ( resolve, reject ) =%3E %7B
+%09%09this.worker.onerror = e =%3E %7B%0A%0A%09%09%09if ( e.message ) %7B%0A%0A%09%09%09%09throw new Error( %60GenerateMeshBVHWorker: Could not create Web Worker with error %22$%7B e.message %7D%22%60 );%0A%0A%09%09%09%7D else %7B%0A%0A%09%09%09%09throw new Error( 'GenerateMeshBVHWorker: Could not create Web Worker.' );%0A%0A%09%09%09%7D%0A%0A%09%09%7D;%0A%0A%09%7D%0A%0A%09generate( geometry, options = %7B%7D ) %7B%0A%0A%09%09if ( this.running ) %7B%0A%0A%09%09%09throw new Error( 'GenerateMeshBVHWorker: Already running job.' );%0A%0A%09%09%7D%0A%0A%09%09if ( this.worker === null ) %7B%0A%0A%09%09%09throw new Error( 'GenerateMeshBVHWorker: Worker has been disposed.' );%0A%0A%09%09%7D%0A%0A%09%09const %7B worker %7D = this;%0A%09%09this.running = true;%0A%0A%09%09return new Promise( ( resolve, reject ) =%3E %7B%0A%0A%09%09%09worker.onerror = e =%3E %7B%0A%0A%09%09%09%09reject( new Error( %60GenerateMeshBVHWorker: $%7B e.message %7D%60 ) );%0A%09%09%09%09this.running = false;%0A%0A%09%09%09%7D;
 %0A%0A%09%09
@@ -2856,24 +2856,46 @@
 terminate();
+%0A%09%09this.worker = null;
 %0A%0A%09%7D%0A%0A%09termi

Fix #421, Fix #422