Source: components/RelationshipComponent.js

import { BaseComponent } from './BaseComponent';
import { Selector } from '../selector/Selector';

const __ = {
	private: Symbol('private'),
}

/**
 * @class RelationshipComponent
 * The relationship component.
 * @memberof THING
 * @extends THING.BaseComponent
 * @public
 */
class RelationshipComponent extends BaseComponent {

	static mustCopyWithInstance = true;

	/**
	 * The relationship of object.
	 */
	constructor() {
		super();

		this[__.private] = {};
		let _private = this[__.private];

		_private.relationships = null;
	}

	/**
	 * object add relationship
	 * @param {THING.Relationship} value
	 * @private
	 */
	addRelationship(value) {
		let _private = this[__.private];

		_private.relationships = _private.relationships || [];
		if (!_private.relationships.includes(value)) {
			_private.relationships.push(value);
		}
	}

	/**
	 * object remove relationship
	 * @param {THING.Relationship} value
	 * @private
	 */
	removeRelationship(value) {
		let _private = this[__.private];

		if (value) {
			let relationships = _private.relationships || [];
			let index = relationships.indexOf(value);
			if (index !== -1) {
				relationships.splice(index, 1);
			}
		}
	}
	// #endregion

	// #region query

	/**
	 * Query related objects.
	 * @param {Object} options
	 * @param {String} options.type The type of relationship.
	 * @param {String} options.name The name of relationship.
	 * @param {RelationshipDirection} options.queryDirection The query direction.
	 * @returns {THING.Selector}
	 * @example
	 * let source = new THING.Object3D();
	 * let target = new THING.Object3D();
	 * let rel = new THING.Relationship({
	 *      type: 'control',
	 *      source,
	 *      target
	 * })
	 * let result = source.relationship.query({type: 'control'});
	 * // @expect(result[0] == target)
	 * @public
	 */
	query(options = {}) {
		let _private = this[__.private];

		let objs = [];
		if (_private.relationships) {
			let queryDirection = options['queryDirection'];

			_private.relationships.forEach((relationship) => {
				if (relationship.test(options)) {
					let selector = relationship.queryObjects(this.object, queryDirection);
					objs.push(...selector.objects);
				}
			})
		}

		return new Selector([...new Set(objs)]);
	}

	/**
	 * Query related objects by relationship type.
	 * @param {String} type The relationship type.
	 * @param {RelationshipDirection} queryDirection The query direction.
	 * @returns {THING.Selector}
	 * @example
	 * let source = new THING.Object3D();
	 * let target = new THING.Object3D();
	 * let rel = new THING.Relationship({
	 *      type: 'control',
	 *      source,
	 *      target
	 * })
	 * let result = source.relationship.queryByType('control');
	 * // @expect(result[0] == target)
	 * @public
	 */
	queryByType(type, queryDirection) {
		return this.query({ type, queryDirection });
	}

	/**
	 * Query related objects by relationship name.
	 * @param {String} name The relationship name.
	 * @param {RelationshipDirection} queryDirection The query direction.
	 * @returns {THING.Selector}
	 * @example
	 * let source = new THING.Object3D();
	 * let target = new THING.Object3D();
	 * let rel = new THING.Relationship({
	 *      name: 'myControl',
	 *      type: 'control',
	 *      source,
	 *      target
	 * })
	 * let result = source.relationship.queryByName('myControl');
	 * // @expect(result[0] == target)
	 * @public
	 */
	queryByName(name, queryDirection) {
		return this.query({ name, queryDirection });
	}

	// #endregion

	// #region remove

	onRemove() {
		let _private = this[__.private];

		if (_private.relationships) {
			let relationships = _private.relationships.slice(0);

			relationships.forEach(relationship => {
				this.app.relationshipManager.notifyRelationshipRemoveObject(relationship, this.object);
			});

			_private.relationships = null;
		}

		super.onRemove();
	}

	// #endregion

	// #region Accessors

	get relationships() {
		let _private = this[__.private];

		return _private.relationships || [];
	}

	// #endregion

}

export { RelationshipComponent }