Source: selector/DynamicSelector.js

import { Utils } from '../common/Utils';
import { EventType } from '../const';
import { ActionManager } from '../managers/ActionManager';
import { Selector } from './Selector';

const _createEventTagName = '__DynamicSelector__createEventTagName';
const _destroyEventTagName = '__DynamicSelector__destroyEventTagName';

/**
 * @class DynamicSelector
 * The dynamic selector to find objects with some conditions.
 * @memberof THING
 */
class DynamicSelector extends Selector {

	constructor(parent, condition, param = {}) {
		super(param);

		this._parent = parent;
		this._condition = condition;

		this._callbacks = {
			onAdd: Utils.parseValue(param.onAdd, null),
			onRemove: Utils.parseValue(param.onRemove, null),
		};

		this._registerEvents();

		this.messages = [];
	}

	/**
	 * dispose
	 * @public
	 */
	dispose() {
		this._unregisterEvents();
		super.clear();
	}

	run(message, options = {}) {
		// Process the action.
		this._processActionByMessage(message, this.objects);

		// When needs auto merge the same message,delete the old same message.
		let autoMerge = options.autoMerge || true;

		if (autoMerge) {
			this.messages = this.messages.filter(msg => {
				return msg.type != message.type;
			});
		}

		// Save the message.
		this.messages.push(message);
	}

	// #region Private

	_check(object) {
		return object.queryable && object.test(this._condition);
	}

	_registerEvents() {
		this._parent.on(EventType.Create, '*', (ev) => {
			let object = ev.object;
			if (!this._check(object)) {
				return;
			}

			this._onCreateObject(object);
		}, _createEventTagName);

		this._parent.on(EventType.BeforeDestroy, '*', (ev) => {
			let object = ev.object;
			if (!this._check(object)) {
				return;
			}

			this._onDestroyObject(object);
		}, _destroyEventTagName);
	}

	_unregisterEvents() {
		this._parent.off(EventType.Create, '*', _createEventTagName);
		this._parent.off(EventType.BeforeDestroy, '*', _destroyEventTagName);
	}

	_onCreateObject(object) {
		let objects = this._objects;

		let index = objects.indexOf(object);
		if (index !== -1) {
			return;
		}

		objects.push(object);

		let onAdd = this._callbacks['onAdd'];
		if (onAdd) {
			onAdd(object);
		}

		// Let all objects run these actions.
		this.messages.forEach((msg) => {
			this._processActionByMessage(msg, [object]);
		})
	}

	_processActionByMessage(message, objects) {
		// Get the action by message type.
		let { type, params } = message;

		let action = Utils.getCurrentApp().actionManager.getActionByName(type);
		if (!action) {
			return;
		}

		// Process the action.
		ActionManager.processAction(action, objects, null, params);
	}

	_onDestroyObject(object) {
		let objects = this._objects;

		let index = objects.indexOf(object);
		if (index === -1) {
			return;
		}

		objects.splice(index, 1);

		let onRemove = this._callbacks['onRemove'];
		if (onRemove) {
			onRemove(object);
		}
	}

	// #endregion

	onCreateInstance() {
		return new DynamicSelector(this._parent, this._condition, arguments[0]);
	}

	get callbacks() {
		return this._callbacks;
	}

	/**
	 * Check class type.
	 * @type {Boolean}
	 * @example
	 * let object = new THING.Object3D();
	 * let selector = new THING.DynamicSelector(object, '*');
	 * // @expect(selector.isDynamicSelector == true);
	 */
	get isDynamicSelector() {
		return true;
	}

}

export { DynamicSelector };