import {BaseNodeType} from '../_Base';

type NodeLifeCycleControllerCallback = () => void;
type NodeLifeCycleControllerCallbackWithChildNode = (childNode: BaseNodeType) => void;

export class NodeLifeCycleController {
	protected _creationCompleted = false;
	protected _onChildAddCallbacks: NodeLifeCycleControllerCallbackWithChildNode[] | undefined;
	private _onChildRemoveCallbacks: NodeLifeCycleControllerCallbackWithChildNode[] | undefined;
	// _on_creation_completed_hooks are used in the importer, once the node has been created, added and params are set
	// private _on_creation_completed_hooks: Callback[] | undefined;
	private _onAfterCreatedCallbacks: NodeLifeCycleControllerCallback[] | undefined;
	private _onAfterAddedCallbacks: NodeLifeCycleControllerCallback[] | undefined;
	private _onBeforeDeletedCallbacks: NodeLifeCycleControllerCallback[] | undefined;
	private _onAfterDeletedCallbacks: NodeLifeCycleControllerCallback[] | undefined;
	constructor(protected node: BaseNodeType) {}

	dispose() {
		this._onChildAddCallbacks = undefined;
		this._onChildRemoveCallbacks = undefined;
		this._onAfterCreatedCallbacks = undefined;
		this._onAfterAddedCallbacks = undefined;
		this._onBeforeDeletedCallbacks = undefined;
		this._onAfterDeletedCallbacks = undefined;
	}

	setCreationCompleted() {
		if (!this._creationCompleted) {
			this._creationCompleted = true;
			// this.run_on_creation_completed_hooks();
		}
	}
	creationCompleted() {
		return this.node.scene().loadingController.loaded() && this._creationCompleted;
	}
	//
	//
	// ON CREATION COMPLETED
	//
	//
	// add_on_creation_completed_hook(callback: Callback) {
	// 	this._on_creation_completed_hooks = this._on_creation_completed_hooks || [];
	// 	this._on_creation_completed_hooks.push(callback);
	// }
	// private run_on_creation_completed_hooks() {
	// 	if (this._on_creation_completed_hooks) {
	// 		console.log('run_on_creation_completed_hooks', this.node.name);
	// 	}
	// 	this.execute_hooks(this._on_creation_completed_hooks);
	// }
	//
	//
	// ON CHILD ADD
	//
	//
	onChildAdd(callback: NodeLifeCycleControllerCallbackWithChildNode) {
		this._onChildAddCallbacks = this._onChildAddCallbacks || [];
		this._onChildAddCallbacks.push(callback);
	}
	runOnChildAddCallbacks(node: BaseNodeType) {
		this._runCallbacksWithChildNode(this._onChildAddCallbacks, node);
	}

	//
	//
	// ON CHILD REMOVE
	//
	//
	onChildRemove(callback: NodeLifeCycleControllerCallbackWithChildNode) {
		this._onChildRemoveCallbacks = this._onChildRemoveCallbacks || [];
		this._onChildRemoveCallbacks.push(callback);
	}
	runOnChildRemoveCallbacks(node: BaseNodeType) {
		this._runCallbacksWithChildNode(this._onChildRemoveCallbacks, node);
	}

	//
	//
	// ON CREATE
	//
	//
	onAfterCreated(callback: NodeLifeCycleControllerCallback) {
		this._onAfterCreatedCallbacks = this._onAfterCreatedCallbacks || [];
		this._onAfterCreatedCallbacks.push(callback);
	}
	runOnAfterCreatedCallbacks() {
		this._runCallbacks(this._onAfterCreatedCallbacks);
	}

	//
	//
	// ON ADD
	//
	//
	onAfterAdded(callback: NodeLifeCycleControllerCallback) {
		this._onAfterAddedCallbacks = this._onAfterAddedCallbacks || [];
		this._onAfterAddedCallbacks.push(callback);
	}
	runOnAfterAddedCallbacks() {
		this._runCallbacks(this._onAfterAddedCallbacks);
	}

	//
	//
	// ON DELETE
	//
	//
	onBeforeDeleted(callback: NodeLifeCycleControllerCallback) {
		this._onBeforeDeletedCallbacks = this._onBeforeDeletedCallbacks || [];
		this._onBeforeDeletedCallbacks.push(callback);
	}
	runOnBeforeDeleteCallbacks() {
		this._runCallbacks(this._onBeforeDeletedCallbacks);
	}
	// TODO: onAfterDeleted is not very different than methods in .dispose
	// so this should probably be removed/refactored
	onAfterDeleted(callback: NodeLifeCycleControllerCallback) {
		this._onAfterDeletedCallbacks = this._onAfterDeletedCallbacks || [];
		this._onAfterDeletedCallbacks.push(callback);
	}
	runOnDeleteCallbacks() {
		this._runCallbacks(this._onAfterDeletedCallbacks);
	}

	//
	//
	// UTILS
	//
	//
	protected _runCallbacks(hooks: NodeLifeCycleControllerCallback[] | undefined) {
		if (!hooks) {
			return;
		}
		let hook: NodeLifeCycleControllerCallback | undefined;
		// do not flush, as this MAY BE needed multiple times
		for (hook of hooks) {
			hook();
		}
	}
	protected _runCallbacksWithChildNode(
		hooks: NodeLifeCycleControllerCallbackWithChildNode[] | undefined,
		childNode: BaseNodeType
	) {
		if (!hooks) {
			return;
		}
		let hook: NodeLifeCycleControllerCallbackWithChildNode | undefined;
		// do not flush, as this is needed multiple times
		for (hook of hooks) {
			hook(childNode);
		}
	}
}
