import {BasePersistedConfig, PersistedConfigWithShaders} from '../../../../utils/BasePersistedConfig';
import {BuilderPostNode} from '../../../../post/Builder';
import {GlParamConfig} from '../../utils/GLParamConfig';
import {IUniformsWithTime} from '../../../../../scene/utils/UniformsController';
import {IUniforms} from '../../../../../../core/geometry/Material';

export interface PersistedConfigBasePostData 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 PostPersistedConfig extends BasePersistedConfig {
	constructor(protected override node: BuilderPostNode) {
		super(node);
	}
	override async toData(): Promise<PersistedConfigBasePostData | 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: PersistedConfigBasePostData = {
			// fragment_shader: this.node.fragmentShader(),
			uniforms: this.node.uniforms(),
			param_uniform_pairs: param_uniform_pairs,
			uniforms_time_dependent: assemblerController.assembler.uniformsTimeDependent(),
			uniforms_resolution_dependent: assemblerController.assembler.uniformsResolutionDependent(),
			shaders: {
				[FRAGMENT_KEY]: this.node.fragmentShader(),
			},
		};

		return data;
	}
	override load(data: PersistedConfigBasePostData) {
		const assemblerController = this.node.assemblerController();
		if (assemblerController) {
			return;
		}

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

		BuilderPostNode.handleDependencies(
			this.node,
			data.uniforms_time_dependent || false,
			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();
			}
		}
	}
}
