import { Asset } from '../asset/asset.js';
import { Http, http } from '../../platform/net/http.js';
import { GSplatResource } from '../../scene/gsplat/gsplat-resource.js';
import { GSplatSogsData } from '../../scene/gsplat/gsplat-sogs-data.js';
import { GSplatSogsResource } from '../../scene/gsplat/gsplat-sogs-resource.js';

const combineProgress = (target, assets)=>{
		const map = new Map();
		const fire = ()=>{
				let loaded = 0;
				let total = 0;
				map.forEach((value)=>{
						loaded += value.loaded;
						total += value.total;
				});
				target.fire('progress', loaded, total);
		};
		assets.forEach((asset)=>{
				const progress = (loaded, total)=>{
						map.set(asset, {
								loaded,
								total
						});
						fire();
				};
				const done = ()=>{
						asset.off('progress', progress);
						asset.off('load', done);
						asset.off('error', done);
				};
				asset.on('progress', progress);
				asset.on('load', done);
				asset.on('error', done);
		});
};
const upgradeMeta = (meta)=>{
		const result = {
				version: 1,
				count: meta.means.shape[0],
				means: {
						mins: meta.means.mins,
						maxs: meta.means.maxs,
						files: meta.means.files
				},
				scales: {
						mins: meta.scales.mins,
						maxs: meta.scales.maxs,
						files: meta.scales.files
				},
				quats: {
						files: meta.quats.files
				},
				sh0: {
						mins: meta.sh0.mins,
						maxs: meta.sh0.maxs,
						files: meta.sh0.files
				}
		};
		if (meta.shN) {
				result.shN = {
						mins: meta.shN.mins,
						maxs: meta.shN.maxs,
						files: meta.shN.files
				};
		}
		return result;
};
class SogsParser {
		constructor(app, maxRetries){
				this.app = app;
				this.maxRetries = maxRetries;
		}
		async loadTextures(url, callback, asset, meta) {
				if (meta.version !== 2) {
						meta = upgradeMeta(meta);
				}
				const { assets } = this.app;
				const subs = [
						'means',
						'quats',
						'scales',
						'sh0',
						'shN'
				];
				const textures = {};
				const promises = [];
				subs.forEach((sub)=>{
						const files = meta[sub]?.files ?? [];
						textures[sub] = files.map((filename)=>{
								const texture = new Asset(filename, 'texture', {
										url: asset.options?.mapUrl?.(filename) ?? new URL(filename, new URL(url.load, window.location.href).toString()).toString(),
										filename
								}, {
										mipmaps: false
								});
								const promise = new Promise((resolve, reject)=>{
										texture.on('load', ()=>resolve(null));
										texture.on('error', (err)=>reject(err));
								});
								assets.add(texture);
								promises.push(promise);
								return texture;
						});
				});
				const textureAssets = subs.map((sub)=>textures[sub]).flat();
				combineProgress(asset, textureAssets);
				textureAssets.forEach((t)=>assets.load(t));
				await Promise.allSettled(promises);
				const data = new GSplatSogsData();
				data.meta = meta;
				data.numSplats = meta.count;
				data.means_l = textures.means[0].resource;
				data.means_u = textures.means[1].resource;
				data.quats = textures.quats[0].resource;
				data.scales = textures.scales[0].resource;
				data.sh0 = textures.sh0[0].resource;
				data.sh_centroids = textures.shN?.[0]?.resource;
				data.sh_labels = textures.shN?.[1]?.resource;
				const decompress = asset.data?.decompress;
				if (!decompress) {
						await data.prepareGpuData();
				}
				const resource = decompress ? new GSplatResource(this.app.graphicsDevice, await data.decompress()) : new GSplatSogsResource(this.app.graphicsDevice, data);
				callback(null, resource);
		}
		load(url, callback, asset) {
				if (asset.data?.means) {
						this.loadTextures(url, callback, asset, asset.data);
				} else {
						if (typeof url === 'string') {
								url = {
										load: url,
										original: url
								};
						}
						const options = {
								retry: this.maxRetries > 0,
								maxRetries: this.maxRetries,
								responseType: Http.ResponseType.JSON
						};
						http.get(url.load, options, (err, meta)=>{
								if (!err) {
										this.loadTextures(url, callback, asset, meta);
								} else {
										callback(`Error loading gsplat meta: ${url.original} [${err}]`);
								}
						});
				}
		}
}

export { SogsParser };
