Source: objects/Entity.js

import { Utils } from '../common/Utils';
import { BaseEntity } from "./BaseEntity";
import { PrefabMixLoader } from '../loaders/PrefabMixLoader';


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

// #region Private Functions

// Build params.
function _buildParams(param) {
	let extras = param['extras'] || param['external'];
	if (extras) {
		let prefab = extras['prefab'];
		if (prefab) {
			param['url'] = param['url'] || prefab;
		}

		let autoCompile = extras['autoCompile'];
		if (autoCompile) {
			param['autoCompile'] = true;
		}
	}

	return param;
}

// #endregion

/**
 * @class Entity
 * The entity object.
 * @memberof THING
 * @extends THING.BaseEntity
 * @public
 */
class Entity extends BaseEntity {

	static defaultTagArray = ['Entity'];

	/**
	 * The prefabe object that load prefab resource in scene.
	 * @param {Object} param The initial parameters.
	 */
	constructor(param = {}) {
		super(_buildParams(param));

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

		_private.isPrefabObject = false;
		_private.prefabLoader = null;
	}

	static onParseCreateOptions(createOptions, options) {
		[
			'autoCompile',
			'isEditor',
		].forEach(key => {
			if (Utils.isValid(options[key])) {
				createOptions[key] = options[key];
			}
		});
	}

	// #region Private

	_complete(resolve) {
		this.onSyncComplete();
		this.onDelayComplete();

		resolve();
	}

	// #endregion

	// #region Overrides

	onCollectMesh() {
		// All prefab object resource is external(outside), so skip to collect mesh when export
		if (this.isPrefabObject || this.isSceneRoot) {
			return false;
		}
		return true;
	}

	onLoadRenderableResource(options, resolve, reject) {
		if (this.destroyed) {
			reject(`The object had been destroyed, skip to load resources`);
		}
		else {
			const _resolve = () => {
				this.clearInternalAnimation();
				resolve();
			};

			const isModelResource = Utils.parseBoolean(this.options.isModelResource, false);
			if (isModelResource) {
				super.onLoadRenderableResource(this.options, _resolve, reject);
				return;
			}

			const url = this.resource.url;

			// Determine the blob type
			if (Utils.isBlobUrl(url)) {
				super.onLoadRenderableResource(this.options, _resolve, reject);
				return;
			}
			let _private = this[__.private];

			_private.prefabLoader = new PrefabMixLoader();

			_private.prefabLoader.load(this, options).then((bundle) => {
				_private.prefabLoader = null;

				if (bundle && bundle.isBundle) {
					_private.bundle = bundle;
				}

				this._complete(resolve);
			}, (e) => {
				this.triggerError(e, reject);
			});
		}
	}

	onImportComponents(components, options, param) {
		if (param) {
			let extras = param['extras'] || param['external'];
			if (extras && extras.prefab) {
				return;
			}
		}

		super.onImportComponents(components, options);
	}

	onNotifyComplete() {
		let dynamic = this.options && this.options.dynamic;
		if (!dynamic) {
			this.waitForComplete().then(() => {
				super.onNotifyComplete();
			});
		}
		else {
			super.onNotifyComplete();
		}
	}

	onUnloadResource() {
		let _private = this[__.private];
		if (_private.bundle) {
			this.app.unloadBundle(_private.bundle);
			_private.bundle = null;
		}

		super.onUnloadResource();
	}

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

		let data = super.onExportExternalData(options) || {};

		// Compatibility bundle
		if (_private.bundle) {
			data.autoCompile = Utils.parseValue(this.options['autoCompile'], false);
			data.prefab = this.resource.url;
		}

		return data;
	}

	/**
	 * Import external components (This is only used by bundles)
	 * @param {Object} externalComponents
	 * @returns
	 * @private
	 */
	onImportExternalComponents(externalComponents) {
		return this.waitForComplete().then(() => {
			// Import all children's components
			for (let uuid in externalComponents) {
				// Find the child object to import component
				let child = this.find(`[external/uuid=${uuid}]`);
				if (!child) {
					continue;
				}

				// Import all components of child
				let childExternalComponents = externalComponents[uuid];
				childExternalComponents.forEach(externalComponent => {
					let componentName = externalComponent.name;
					let component = child[componentName];

					if (component && component.onImport) {
						component.onImport(externalComponent.external || externalComponent.params);
					}
				});
			}
		});
	}

	/**
	 * Export external components (This is only used by bundles)
	 * @returns
	 * @private
	 */
	onExportExternalComponents() {
		if (!this.bundle) {
			return null;
		}
		let suffixName = '_' + this.bundle.uuid;

		let options = {
			onGetComponentType: (name) => {
				if (name._endsWith(suffixName)) {
					return name._trimRight(suffixName);
				}
				else {
					return name;
				}
			}
		};

		let externalComponents = super.onExportExternalComponents() || {};
		this.children.traverse(child => {
			let childComponents = child.onExportComponents(options);
			if (!childComponents) {
				return;
			}

			let external = child.external;
			if (!external) {
				return;
			}

			let uuid = external.uuid;
			externalComponents[uuid] = childComponents;
		});

		return externalComponents;
	}

	onBeforeDestroy() {
		let canDestroy = super.onBeforeDestroy();
		if (canDestroy) {
			let _private = this[__.private];
			if (_private.prefabLoader) {
				_private.prefabLoader.dispose();
				_private.prefabLoader = null;
			}
		}

		return canDestroy;
	}

	// #endregion

	// #region Accessors

	/**
	 * @typedef {Object} PrefabEntrance
	 * @property {Function} getBlueprintClasses Get the blueprint classes.
	 * @property {Function} getExportProperties Get the export properties.
	 * @property {Function} getExportFunctions Get the export functions.
	 */

	/**
	 * Get the entrance.
	 * @type {PrefabEntrance}
	 * @private
	 */
	get entrance() {
		let _private = this[__.private];
		let bundle = _private.bundle;
		if (!bundle) {
			return null;
		}

		let bundleInfo = bundle.prefab ? bundle.prefab : bundle.model;
		return bundleInfo.entrance;
	}

	/**
	 * Get the bundle.
	 * @type {THING.Bundle}
	 * @private
	 */
	get bundle() {
		let _private = this[__.private];

		return _private.bundle;
	}

	/**
	 * Check whether it's PrefabObject type or inherit from it.
	 * @type {Boolean}
	 * @private
	 */
	get isPrefabObject() {
		let _private = this[__.private];
		return _private.isPrefabObject;
	}

	// #endregion

}

export { Entity }