import {isString} from './../../core/Type';
import {BaseParamType} from './_Base';
import {TypedPathParam} from './_BasePath';
import {CoreWalker, TypedParamPathParamValue} from '../../core/Walker';
import {BaseNodeType} from '../nodes/_Base';
import {ParamType} from '../poly/ParamType';
import {ParamValuesTypeMap} from './types/ParamValuesTypeMap';
import {ParamInitValuesTypeMap} from './types/ParamInitValuesTypeMap';

const tmpConvertedValue = new TypedParamPathParamValue();
export class ParamPathParam extends TypedPathParam<ParamType.PARAM_PATH> {
	static override type() {
		return ParamType.PARAM_PATH;
	}
	protected override _initializeParam() {
		this._value = new TypedParamPathParamValue();
	}

	override defaultValueSerialized() {
		return this._default_value;
	}
	override rawInputSerialized() {
		return `${this._raw_input}`;
	}
	override valueSerialized() {
		return `${this.value}`;
	}
	protected override _copyValue(param: ParamPathParam) {
		this.set(param.valueSerialized());
	}
	static override areRawInputEqual(
		raw_input1: ParamInitValuesTypeMap[ParamType.PARAM_PATH],
		raw_input2: ParamInitValuesTypeMap[ParamType.PARAM_PATH]
	) {
		return raw_input1 == raw_input2;
	}
	static override areValuesEqual(
		val1: ParamValuesTypeMap[ParamType.PARAM_PATH],
		val2: ParamValuesTypeMap[ParamType.PARAM_PATH]
	) {
		return val1 == val2;
	}
	override isDefault(): boolean {
		return this._raw_input == this._default_value;
	}
	setParam(param: BaseParamType) {
		this.set(param.path());
	}

	protected _assignValue(value: ParamValuesTypeMap[ParamType.PARAM_PATH] | string): void {
		const path = isString(value) ? value : value.path();
		if (this._value.path() != path) {
			this._setValuePathAndFindTarget(path, false);
		}
	}
	override convert(rawVal: any): ParamValuesTypeMap[ParamType.PARAM_PATH] | null {
		if (isString(rawVal)) {
			tmpConvertedValue.setPath(rawVal);
			return tmpConvertedValue;
		} else {
			return null;
		}
	}
	// protected override async processComputation() {
	// 	this.findTarget();
	// }
	protected _findTarget() {
		if (!this.node) {
			return;
		}
		const path = this._value.path();
		let param: BaseParamType | null = null;
		const pathNonEmpty = path != null && path !== '';

		this.scene().referencesController.resetReferenceFromParam(this); // must be before decomposed path is changed
		this.decomposedPath.reset();
		if (pathNonEmpty) {
			param = CoreWalker.findParam(this.node, path, this.decomposedPath);
		}

		const currentFoundEntity = this._value.param();
		const newlyFoundEntity = param;

		// if the param refers to itself, we set an error
		if (newlyFoundEntity) {
			if (newlyFoundEntity.graphNodeId() == this.graphNodeId()) {
				this.states.error.set(`param cannot refer to itself`);
				return;
			}
		}

		this._handleReferences(param, path);

		if (currentFoundEntity?.graphNodeId() !== newlyFoundEntity?.graphNodeId()) {
			const dependentOnFoundParam = this.options.dependentOnFoundParam();

			const previouslyFoundParam = this._value.param();
			if (previouslyFoundParam) {
				if (dependentOnFoundParam) {
					this.removeGraphInput(previouslyFoundParam);
				} else {
					// this._found_node.remove_param_referree(this) // TODO: typescript
				}
				previouslyFoundParam.deregisterOnDispose(this._onResolvedParamDisposeBound);
			}

			if (param) {
				this._assignFoundParam(param);
			} else {
				this._value.setParam(null);
			}

			this.options.executeCallback();
		}
		this.removeDirtyState();
	}

	private _assignFoundParam(param: BaseParamType) {
		const dependentOnFoundParam = this.options.dependentOnFoundParam();
		// if (this._is_node_expected_context(node)) {
		// 	if (this._is_node_expected_type(node)) {
		this._value.setParam(param);
		if (dependentOnFoundParam) {
			this.addGraphInput(param);
		}
		param.onDispose(this._onResolvedParamDisposeBound);
		// 	} else {
		// 		this.states.error.set(
		// 			`node type is ${node.type} but the params expects one of ${(this._expected_node_types() || []).join(
		// 				', '
		// 			)}`
		// 		);
		// 	}
		// } else {
		// 	this.states.error.set(
		// 		`node context is ${node.node_context()} but the params expects a ${this._expected_context()}`
		// 	);
		// }
	}

	// private _expected_context() {
	// 	return this.options.node_selection_context;
	// }
	// private _is_node_expected_context(node: BaseNodeType) {
	// 	const expected_context = this._expected_context();
	// 	if (expected_context == null) {
	// 		return true;
	// 	}
	// 	const node_context = node.parent?.childrenController?.context;
	// 	return expected_context == node_context;
	// }
	// private _expected_node_types() {
	// 	return this.options.node_selection_types;
	// }

	// private _is_node_expected_type(node: BaseNodeType) {
	// 	const expected_types = this._expected_node_types();
	// 	if (expected_types == null) {
	// 		return true;
	// 	}
	// 	return expected_types?.includes(node.type);
	// }

	notifyPathRebuildRequired(param: BaseParamType) {
		this.decomposedPath.updateFromNameChange(param);
		const new_path = this.decomposedPath.toPath();
		this.set(new_path);
	}
	notifyTargetParamOwnerParamsUpdated(node: BaseNodeType) {
		this.setDirty();
	}
	private _onResolvedParamDisposeBound = this._onResolvedParamDispose.bind(this);
	private async _onResolvedParamDispose() {
		this.setDirty();
		await this.compute();
	}
}
