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



/**
 * Entity - 实体
 *
 * 在场景中加载 3D 模型资源的物体,支持动画播放,可加载预制件。
 * @class Entity
 * @memberof THING
 * @extends THING.BaseEntity
 * @public
 * @example
 const entity = new THING.Entity({
   url: './models/robot.glb',
   position: [0, 0, 0],
   onComplete: (e) => {
     console.log('模型加载完成', e);
   },
 });
 */
class Entity extends BaseEntity {



	static defaultTagArray = ['Entity'];



	/**
	 * Entity的构造函数
	 * 也可用于在场景中加载预制资源的预制对象。
	 * @constructor
	 * @public
	 * @param {object} param 初始化参数 这里支持Object3D初始化的所有参数,此外可接受url作为资源路径
	 * @param {string} [param.url] 资源路径
	 */

	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 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) {
					if (bundle.isBundle) {
						_private.bundle = bundle;
					}

					else {
						_private.loadResult = bundle;
					}
				}



				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;
		}

		else if (_private.loadResult) {
			const map = _private.loadResult.data;

			if (map) {
				map.forEach(element => {
					element.unload && element.unload();
				});
			}
		}



		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 {Promise<void>} 导入外部组件的Promise。
	 * @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 {object} 外部组件。
	 * @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;
	}



	promoteNode(name, parent, cls = Entity) {
		return super.promoteNode(name, parent, cls);
	}



	// #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. 获取输出函数的方法
	 */



	/**

	 预制件入口
	 * @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 }