( function () {

	/**
 * @author Deepkolos / https://github.com/deepkolos
 */
	class WorkerPool {

		constructor( pool = 4 ) {

			this.pool = pool;
			this.queue = [];
			this.workers = [];
			this.workersResolve = [];
			this.workerStatus = 0;

		}

		_initWorker( workerId ) {

			if ( ! this.workers[ workerId ] ) {

				const worker = this.workerCreator();
				worker.addEventListener( 'message', this._onMessage.bind( this, workerId ) );
				this.workers[ workerId ] = worker;

			}

		}

		_getIdleWorker() {

			for ( let i = 0; i < this.pool; i ++ ) if ( ! ( this.workerStatus & 1 << i ) ) return i;

			return - 1;

		}

		_onMessage( workerId, msg ) {

			const resolve = this.workersResolve[ workerId ];
			resolve && resolve( msg );

			if ( this.queue.length ) {

				const {
					resolve,
					msg,
					transfer
				} = this.queue.shift();
				this.workersResolve[ workerId ] = resolve;
				this.workers[ workerId ].postMessage( msg, transfer );

			} else {

				this.workerStatus ^= 1 << workerId;

			}

		}

		setWorkerCreator( workerCreator ) {

			this.workerCreator = workerCreator;

		}

		setWorkerLimit( pool ) {

			this.pool = pool;

		}

		postMessage( msg, transfer ) {

			return new Promise( resolve => {

				const workerId = this._getIdleWorker();

				if ( workerId !== - 1 ) {

					this._initWorker( workerId );

					this.workerStatus |= 1 << workerId;
					this.workersResolve[ workerId ] = resolve;
					this.workers[ workerId ].postMessage( msg, transfer );

				} else {

					this.queue.push( {
						resolve,
						msg,
						transfer
					} );

				}

			} );

		}

		dispose() {

			this.workers.forEach( worker => worker.terminate() );
			this.workersResolve.length = 0;
			this.workers.length = 0;
			this.queue.length = 0;
			this.workerStatus = 0;

		}

	}

	THREE.WorkerPool = WorkerPool;

} )();
