import { EventHandler } from '../../core/event-handler.js';
import { platform } from '../../core/platform.js';
import { UnifiedSortWorker } from './gsplat-unified-sort-worker.js';

class GSplatUnifiedSorter extends EventHandler {
		constructor(){
				super(), this.bufferLength = 0, this.availableOrderData = [], this.jobsInFlight = 0, this.hasNewVersion = false, this.centersSet = new Set();
				const workerSource = `(${UnifiedSortWorker.toString()})()`;
				if (platform.environment === 'node') {
						this.worker = new Worker(workerSource, {
								eval: true
						});
						this.worker.on('message', this.onSorted.bind(this));
				} else {
						this.worker = new Worker(URL.createObjectURL(new Blob([
								workerSource
						], {
								type: "application/javascript"
						})));
						this.worker.addEventListener('message', this.onSorted.bind(this));
				}
		}
		onSorted(message) {
				const msgData = message.data ?? message;
				const orderData = new Uint32Array(msgData.order);
				this.jobsInFlight--;
				this.fire('sorted', msgData.count, msgData.version, orderData);
				if (orderData.length === this.bufferLength) {
						this.availableOrderData.push(orderData);
				}
		}
		destroy() {
				this.worker.terminate();
				this.worker = null;
		}
		setCenters(id, centers) {
				if (centers) {
						if (!this.centersSet.has(id)) {
								this.centersSet.add(id);
								const centersBuffer = centers.buffer.slice();
								this.worker.postMessage({
										command: 'addCenters',
										id: id,
										centers: centersBuffer
								}, [
										centersBuffer
								]);
						}
				} else {
						if (this.centersSet.has(id)) {
								this.centersSet.delete(id);
								this.worker.postMessage({
										command: 'removeCenters',
										id: id
								});
						}
				}
		}
		setSortParameters(payload) {
				this.hasNewVersion = true;
				const { textureSize } = payload;
				const newLength = textureSize * textureSize;
				if (newLength !== this.bufferLength) {
						this.bufferLength = newLength;
						this.availableOrderData.length = 0;
				}
				this.worker.postMessage(payload);
		}
		setSortParams(params) {
				if (this.hasNewVersion || this.jobsInFlight === 0) {
						let orderData = this.availableOrderData.pop();
						if (!orderData) {
								orderData = new Uint32Array(this.bufferLength);
						}
						this.jobsInFlight++;
						this.hasNewVersion = false;
						this.worker.postMessage({
								command: 'sort',
								sortParams: params,
								order: orderData.buffer
						}, [
								orderData.buffer
						]);
				}
		}
}

export { GSplatUnifiedSorter };
