import {BasePersistedConfig, PersistedConfigWithShaders} from '../../../../utils/BasePersistedConfig';
import {BuilderCopNode} from '../../../../cop/Builder';
import {Builder2DArrayCopNode} from '../../../../cop/Builder2DArray';
import {GlParamConfig} from '../../utils/GLParamConfig';
import {IUniformsWithTime} from '../../../../../scene/utils/UniformsController';
import {IUniforms} from '../../../../../../core/geometry/Material';
import {handleCopBuilderDependencies} from '../../../../cop/utils/BuilderUtils';

export interface PersistedConfigBaseTextureData extends PersistedConfigWithShaders {
	// fragment_shader: string;
	uniforms: IUniforms;
	param_uniform_pairs: [string, string][];
	uniforms_time_dependent?: boolean;
	uniforms_resolution_dependent?: boolean;
}
const FRAGMENT_KEY = 'fragment';
export class TexturePersistedConfig extends BasePersistedConfig {
	constructor(protected override node: BuilderCopNode | Builder2DArrayCopNode) {
		super(node);
	}
	override async toData(): Promise<PersistedConfigBaseTextureData | undefined> {
		const assemblerController = this.node.assemblerController();
		if (!assemblerController) {
			return;
		}

		// params updating uniforms
		const param_uniform_pairs: [string, string][] = [];
		const param_configs = assemblerController.assembler.param_configs();
		for (const param_config of param_configs) {
			param_uniform_pairs.push([param_config.name(), param_config.uniformName()]);
		}

		const data: PersistedConfigBaseTextureData = {
			// fragment_shader: this.node.textureMaterial.fragmentShader,
			uniforms: this.node.textureMaterial.uniforms,
			param_uniform_pairs: param_uniform_pairs,
			uniforms_time_dependent: assemblerController.assembler.uniformsTimeDependent(),
			uniforms_resolution_dependent: assemblerController.assembler.uniformsResolutionDependent(),
			shaders: {
				[FRAGMENT_KEY]: this.node.textureMaterial.fragmentShader,
			},
		};
		return data;
	}
	override load(data: PersistedConfigBaseTextureData) {
		const assemblerController = this.node.assemblerController();
		if (assemblerController) {
			return;
		}

		this.node.textureMaterial.uniforms = data.uniforms;
		const shaders = data.shaders;
		if (shaders && shaders[FRAGMENT_KEY] != null) {
			this.node.textureMaterial.fragmentShader = shaders[FRAGMENT_KEY];
		} else {
			console.warn(`${this.node.path()}: persisted config has no fragment shader`);
		}

		handleCopBuilderDependencies({
			node: this.node,
			timeDependent: data.uniforms_time_dependent || false,
			uniforms: data.uniforms as IUniformsWithTime,
		});

		for (const pair of data.param_uniform_pairs) {
			const param = this.node.params.get(pair[0]);
			const uniform = data.uniforms[pair[1]];
			if (param && uniform) {
				const callback = () => {
					GlParamConfig.callback(param, uniform);
				};
				param.options.set({
					callback: callback,
				});
				// it's best to execute the callback directly
				// as it may otherwise be prevented if the scene is loading for instance
				// and this is currently necessary for ramp params, when no assembler is loaded
				callback();
				// param.options.executeCallback();
			}
		}
	}
}
