import { StringEncoder, Flags, Callbacks } from '@uino/base-thing';
import { Utils, _getAuthData } from '../common/Utils';
import { Selector } from '../selector/Selector';
import { MathUtils } from '../math/MathUtils';
import { Camera } from '../objects/Camera';
import { ResourceManager } from '../managers/ResourceManager';
import { EventManager } from '../managers/EventManager';
import { ObjectManager } from '../managers/ObjectManager';
import { TweenManager } from '../managers/TweenManager';
import { ActionManager } from '../managers/ActionManager';
import { CreateObjectAction } from '../actions/CreateObjectAction';
import { CameraFlyToAction } from '../actions/CameraFlyToAction';
import { ObjectSetColorAction } from '../actions/ObjectSetColorAction';
import { ObjectSetPropertyAction } from '../actions/ObjectSetPropertyAction';
import { StateManager } from '../managers/StateManager';
import { StateGroupManager } from '../managers/StateGroupManager';
import { ColliderManager } from '../managers/ColliderManager';
import { RelationshipManager } from '../managers/RelationshipManager';
import { DummyEventManager } from '../managers/DummyEventManager';
import { ViewPointManager } from '../managers/ViewPointManager';
import { Component } from '../components/Component';
import { AppTimerComponent } from '../components/AppTimerComponent';
import { AppHelperComponent } from '../components/AppHelperComponent';
import { AppResourcePoolComponent } from '../components/AppResourcePoolComponent';
import { BaseComponentGroup } from '../components/BaseComponentGroup';
import { AppRenderConfigComponent } from '../components/AppRenderConfigComponent';
import { AppTimeOfDayComponent } from '../components/AppTimeOfDayComponent';
import { Picker } from './Picker';
import { Global } from './Global';
import { Scene } from './Scene';
import { Marker } from './Marker';
import { EventType, ActionType } from '../const';
import { ThemeManager } from '../managers/ThemeManager';
import { AppPluginComponent } from '../components/AppPluginComponent';
import { AppBundleComponent } from '../components/AppBundleComponent';
import { MonitorManagerComponent } from '../components/MonitorManagerComponent';
import { SelectionComponent } from '../components/SelectionComponent';

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

const State = {
	Waiting: 0,
	Running: 1,
	Stopped: 2
};

const Flag = {
	Disposing: 1 << 0,
	Disposed: 1 << 1
};

const DumnyFunction = function () { }

const registerComponentParam = { isResident: true };

const _authDataEvent = StringEncoder.toText("<@secret __!!a$u^t@h*event@__>");
const _askAuthDataEvent = StringEncoder.toText("<@secret __a)s*k&!!a$u^t@h*event@__>");

/**
 * @public
 * @class App
 * App 是 ThingJS 库入口,提供加载场景、搜索、事件绑定、摄像机控制等功能
 * @memberof THING
 * @extends THING.BaseComponentGroup
 */
class App extends BaseComponentGroup {

	static instances = [];

	static completeCallbacks = [];

	/**
	 * 当前App对象
	 * @type {THING.App}
	 * @example
	 * let app = THING.App.current;
	 * let ret = app.picker != null;
	 * // @expect(ret == true);
	 * @public
	 */
	static get current() {
		return Utils.getCurrentApp();
	}

	static set current(app) {
		Utils.setCurrentApp(app);
	}

	static defaultSettings = {
		envMap: true, // Control default envMap
	};

	/**
	 * Get alive instances.
	 * @returns {Array<THING.App>} 存活的应用实例
	 * @private
	 * @example
	 * let instances = THING.App.getAliveInstances();
	 * let ret = instances.length > 0;
	 * // @expect(ret == true);
	 */
	static getAliveInstances() {
		return App.instances.filter(app => {
			return !app.isDisposed;
		});
	}

	/**
	 * App实例初始化结束的回调
	 * @param {Function} callback 回调函数
	 * @param {number} priority 优先级
	 * @public
	 * @example
	 * THING.App.addCompleteCallback((app)=>{
	 *		console.log(The app instantiation is complete.);
	 * },-10);
	 */
	static addCompleteCallback(callback, priority = 0) {
		App.instances.forEach(app => {
			if (app.isDisposed) {
				return;
			}

			callback(app);
		});

		App.completeCallbacks.push({ callback, priority });

		App.completeCallbacks.sort((a, b) => {
			return b.priority - a.priority;
		});
	}


	/**
	 * App构造方法
	 * @param {object} param App初始化的参数
	 * @param {HTMLElement} param.container 渲染场景需要的dom元素
	 * @param {boolean} [param.isEditor=false] 是否在编辑器环境使用
	 * @param {boolean} [param.enableDefaultLevelControl=true] 是否默认开启层级控制
	 * @param {number | string | Array<number>} param.background? 背景
	 * @param {string} [param.envMap] 环境图url
	 * @param {string} [param.url] 场景url
	 * @param {Function} [param.onComplete] 场景加载完成后的回调函数
	 * @param {Function} [param.onProgress] 场景加载进度的回调函数
	 * @param {Function} [param.onError] 场景加载失败后的回调函数
	 * @example
	 * // Load bundle scene
	 * const app = new THING.App({
	 * 	 url: './scene-bundle',
	 * 	 onComplete: (e) => {
	 *     console.log(e);
	 *   }
	 * });
	 *
	 * // Load gltf scene
	 * const app = new THING.App({
	 *   url: './gltf/scene.gltf',
	 *   onComplete: (e) => {
	 *     console.log(e);
	 *   }
	 * });
	 * @constructor
	 * @public
	 */
	constructor(param = {}) {
		super(param);

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

		// Add instance
		App.instances.push(this);

		// Initialize global setting
		this.settings = {
			_useDefaultViewpoint: true
		};

		// The initial options
		_private.options = param;

		_private.isEditor = Utils.parseBoolean(param['isEditor'], false);

		// Initialize global
		_private.global = new Global(param);

		// Initialize application delegate
		this.#initAppDelegate(param);
		this.#initAppView(param);

		// Make as current
		this.makeCurrent();

		// The default pixel ratio is base on application delegate attributes
		let devicePixelRatio = _private.delegate.getAttribute('DevicePixelRatio');
		if (devicePixelRatio) {
			_private.view.setPixelRatio(devicePixelRatio);
		}

		// Initialize common
		_private.uuid = MathUtils.generateUUID();
		_private.size = [0, 0];
		_private.state = State.Waiting;
		_private.flags = new Flags();

		// Picker
		_private.picker = null;

		// Create managers
		this.#initManager();

		// Core
		_private.scene = null;
		_private.root = null;

		// Callbacks
		_private.beforeDisposeCallbacks = new Callbacks();
		_private.afterDisposeCallbacks = new Callbacks();
		_private.beforeTickCallbacks = new Callbacks();
		_private.afterTickCallbacks = new Callbacks();

		// Create scene
		let scene = Utils.createObject('Scene', { name: 'RootScene' });

		// Initialize core
		this.#initSelector(param);
		this.#initResourceManager(param['resourceManager']);
		this.#initScene(scene, param);
		this.#initPicker();
		this.#initCamera(param['camera']);
		this.#initComponents();
		this.#initEvents();
		this.#initLevelManager();

		// register actions
		this.#registerAction();

		// Initialize delegate
		_private.delegate.init();

		// Start to run
		this.run();

		// Get the initial size
		let size = [];
		_private.delegate.getSize(size);

		// Resize it
		this.resize(size[0], size[1]);

		// Notify resize event when start up
		this.trigger('Resize', {
			width: size[0],
			height: size[1]
		});

		this.#setupScene();

		// Create logo
		_private.marker = new Marker(this);
		const accessors = _private.scene.getAttribute(StringEncoder.toText("<@secret Accessors>"));
		_private.marker.init(accessors[StringEncoder.toText("<@secret overlayMarkerRootNode>")]);

		// Notify outside the application has been completed
		Utils.onCompleteApp(this);

		// Notify complete callbacks
		App.completeCallbacks.forEach(info => {
			info.callback(this);
		});

		// Load scene
		if (param.url) {
			this.load(param);
		}

		if (_NEED_LOGGER) {
			let msg = { "renderCapabilities": this.renderCapabilities }
			Utils.logger.api("App_create", { msg: JSON.stringify(msg) })
		}

		this._workPath = "";
	}

	// #region Private

	#initAppDelegate(param) {
		let _private = this[__.private];

		let delegate = Utils.createObject('AppDelegate', param);
		if (!delegate) {
			Utils.error(`Create app delegate failed`);
		}

		_private.delegate = delegate;
	}

	#initAppView(param) {
		let _private = this[__.private];

		let canvas = Utils.parseValue(param['canvas'], null);
		let compatibleOptions = param.compatibleOptions || {};
		let asyncGPUPicking = param['asyncGPUPicking'] || false;

		if (Utils.settings['compatibleOptions']) {
			compatibleOptions = Utils.mergeObject(compatibleOptions, Utils.settings['compatibleOptions']);
		}

		_private.view = Utils.createObject('AppView', { canvas, appDelegate: this.delegate, compatibleOptions, asyncGPUPicking });
		_private.renderCapabilities = _private.view.getCapabilities();

		if (_DEBUG) {
			let rendererType = _private.view.getAttribute('RendererType');
			let disableBackward = _private.delegate.getAttribute('DisableBackward');

			Utils.log(`Init app view(rendererType: ${rendererType}, disableBackward: ${disableBackward})`);
		}
	}

	#initManager() {
		this.addComponent(EventManager, 'eventManager', registerComponentParam);
		this.addComponent(ObjectManager, 'objectManager', registerComponentParam);
		this.addComponent(TweenManager, 'tweenManager', registerComponentParam);
		this.addComponent(ColliderManager, 'colliderManager', registerComponentParam);
		this.addComponent(ActionManager, 'actionManager', registerComponentParam);
		this.addComponent(StateManager, 'stateManager', registerComponentParam);
		this.addComponent(StateGroupManager, 'stateGroupManager', registerComponentParam);
		this.addComponent(RelationshipManager, 'relationshipManager', registerComponentParam);
		this.addComponent(ThemeManager, 'themeManager', registerComponentParam);
		this.addComponent(ViewPointManager, 'viewPointManager', registerComponentParam);
	}

	#initSelector(param) {
		Selector.init(param['selector']);
	}

	#initScene(scene, param) {
		let _private = this[__.private];

		_private.scene = new Scene({
			app: this,
			scene,
			initLights: param['initLights'],
			envMap: param['envMap'] || param['env'],
			defaultSettings: App.defaultSettings,
		});

		_private.root = _private.scene.rootObjects['scene'];
	}

	#initResourceManager(options = {}) {
		let _private = this[__.private];

		const resourceManager = new ResourceManager(options);
		this.addComponent(resourceManager, 'resourceManager', registerComponentParam);

		// Load resources for global cache
		_private.global.loadResource(resourceManager);
	}

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

		_private.picker = new Picker();
	}

	#initCamera(options = {}) {
		let _private = this[__.private];

		// We set initial position to prevent target and position is the same
		let cameraOptions = Object.assign({}, options);
		cameraOptions['position'] = [10, 10, 10];
		cameraOptions['tags'] = ['MainCamera'];

		let camera = new Camera(cameraOptions);
		camera.enable = true;
		camera.enableViewport = true;
		camera.background = Utils.parseValue(cameraOptions['background'], [0.5647058823529412, 0.5647058823529412, 0.5647058823529412]);
		camera.control.autoAdjustInstanceOffset = true;

		// Add camera into scene
		_private.root.add(camera);

		// Make as main camera
		let scene = _private.scene;
		scene.camera = camera;
		scene.renderCamera = camera;
	}

	#initComponents() {
		this.addComponent(AppTimerComponent, 'timer', registerComponentParam);
		this.addComponent(AppHelperComponent, 'helper', registerComponentParam);
		this.addComponent(AppResourcePoolComponent, 'resourcePool', registerComponentParam);
		this.addComponent(AppRenderConfigComponent, 'renderConfig', registerComponentParam);
		this.addComponent(AppPluginComponent, 'plugin', registerComponentParam);
		this.addComponent(AppBundleComponent, 'bundle', registerComponentParam);
		this.addComponent(MonitorManagerComponent, 'monitorManager', registerComponentParam);
		this.addComponent(AppTimeOfDayComponent, 'time', registerComponentParam);
		this.addComponent(SelectionComponent, 'selection', registerComponentParam);
	}

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

		let root = _private.root;

		// Hook focus in
		root.on('windowfocusin', (ev) => {
			root.trigger(EventType.FocusIn, ev);
		}, {
			enumerable: false
		});

		// Hook focus out
		root.on('windowfocusout', (ev) => {
			root.trigger(EventType.FocusOut, ev);
		}, {
			enumerable: false
		});

		// Hook resize event
		root.on('windowresize', (ev) => {
			this.resize(ev.width, ev.height);
		}, {
			enumerable: false
		});

		// Hook drop files
		root.on('windowdrop', (ev) => {
			root.trigger(EventType.DropFiles, ev);
		}, {
			enumerable: false
		});

		// Hook auth event
		root.on(_askAuthDataEvent, (ev) => {
			let authData = _getAuthData();
			if (authData) {
				root.trigger(_authDataEvent, authData);
			}
		}, {
			enumerable: false
		});
	}

	#initLevelManager() {
		const _private = this[__.private];
		const LevelManager = Utils.getRegisteredClass('LevelManager');
		_private.level = new LevelManager(this);
	}

	#setupScene() {
		const _private = this[__.private];

		if (!_private.view.getAttribute('CompatibleRender') && _private.scene.mainLight) {
			_private.scene.mainLight.adapter.bindCamera(_private.scene.camera);
		}
	}

	#registerAction() {
		this.actionManager.register(ActionType.CreateObject, new CreateObjectAction());
		this.actionManager.register(ActionType.CameraFlyTo, new CameraFlyToAction());
		this.actionManager.register(ActionType.ObjectSetColor, new ObjectSetColorAction());
		this.actionManager.register(ActionType.ObjectSetProperty, new ObjectSetPropertyAction());
	}

	// #endregion

	// #region Context

	/**
	 * Make application as current.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.makeCurrent();
	 */
	makeCurrent() {
		Utils.setCurrentApp(this);

		const _private = this[__.private];
		if (_private.delegate) {
			_private.delegate.makeCurrent();
		}
	}

	// #endregion

	// #region Common

	/**
	 * When focus change.
	 * @callback onFocusChangeCallback
	 * @param {FocusEvent} ev The event info.
	 */

	/**
	 * Get/Set onFocusChange callback
	 * @type {onFocusChangeCallback}
	 * @private
	 */
	get onFocusChange() {
		let _private = this[__.private];
		_private.delegate.getFocusChangeCallback();
	}
	set onFocusChange(value) {
		let _private = this[__.private];
		_private.delegate.setFocusChangeCallback(value);
	}

	/**
	 * Get/Set resize throttling wait time in milliseconds.
	 * @type {number}
	 * @private
	 */
	get resizeThrottlingWaitTime() {
		let _private = this[__.private];
		return _private.delegate.getAttribute('ResizeThrottlingWaitTime');
	}
	set resizeThrottlingWaitTime(value) {
		let _private = this[__.private];
		_private.delegate.setAttribute('ResizeThrottlingWaitTime', value);
	}

	/**
	 * Set foucs on it.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.focus();
	 */
	focus() {
		let _private = this[__.private];

		_private.delegate.focus();
	}

	/**
	 * 销毁app
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * app.dispose();
	 */
	dispose() {
		let _private = this[__.private];

		if (_private.flags.has(Flag.Disposed)) {
			return;
		}

		this.trigger(EventType.AppQuit);

		_private.beforeDisposeCallbacks.invoke();
		{
			// Stop all blueprint, because onStop may use app and other interfaces
			// So it must be called before dispose
			this.stopAllBlueprints();
			_private.flags.enable(Flag.Disposing, true);

			this.stop();

			_private.level.dispose();
			_private.level = null;

			this.removeComponent('plugin');

			_private.scene.dispose();
			_private.scene = null;
			_private.root = null;

			_private.picker.dispose();
			_private.picker = null;

			this.removeAllComponents(true);

			// We do not set to null due to keep code clear
			this.addComponent(new DummyEventManager(), 'eventManager', registerComponentParam);

			_private.global.dispose();
			_private.global = null;

			_private.view.dispose();
			_private.view = null;

			_private.delegate.dispose();
			_private.delegate = null;

			_private.beforeTickCallbacks.clear();
			_private.afterTickCallbacks.clear();

			_private.flags.enable(Flag.Disposing, false);
		}
		_private.afterDisposeCallbacks.invoke();

		// Dispose complete
		_private.flags.enable(Flag.Disposed, true);

		// Update current app if it's needed
		if (App.current == this) {
			let instances = App.instances;
			Utils.removeFromArray(instances, this);

			if (instances.length) {
				App.current = instances[0];
				App.current.makeCurrent();
			}
			else {
				App.current = null;
			}
		}

		// Make some interfaces as dummy function for easy coding
		this.trigger = DumnyFunction;

		if (_NEED_LOGGER) {
			let msg = { "renderCapabilities": this.renderCapabilities };
			Utils.logger.api("App_dispose", { msg: JSON.stringify(msg) })
		}
	}

	/**
	 * 创建对象
	 * 2.0兼容1.0中通过app.create({type:'ClassName'})的方式创建对象,<br>
	 * 但是推荐使用new THING.ClassName({xxx})的方式创建对象。
	 * @param {object} param The parameter list 参数列表
	 * @param {string} param.type Object type 类型
	 * @param {object} param.options? Object create parameters 创建对于类型对象的初始化参数
	 * @returns {THING.BaseObject} 返回创建的对象
	 * @example
	 * let app =  THING.App.current;
	 * let box = app.create({
	 *   type: 'Box',
	 *   name: 'box',
	 *   position: [1, 1, 1],
	 *   onComplete: function() {
	 *   	console.log('box01 created!');
	 *   }
	 * });
	 * let ret = box instanceof THING.Box;
	 * // @expect(ret == true);
	 * @public
	 * @deprecated
	 */
	create(param) {
		if (!param) {
			console.warn('create object failed, due to param is null');
			return null;
		}

		const typeClass = Utils.getRegisteredClasses()[param['type']];
		if (!typeClass) {
			console.warn('Object creation failed, due to type not found or not supported');
			return null;
		}

		const paramCopy = Object.assign({}, param);
		delete paramCopy['type'];

		const targetOptions = paramCopy['options'] || paramCopy;
		if (targetOptions.app != null && targetOptions.app != this) {
			console.error('app.create failed, due to options.app is not this');
			return null;
		}

		const options = Object.assign({ app: this }, targetOptions);
		return new typeClass(options);
	}

	/**
	 * 加载场景
	 * @param {string | object} url 场景url
	 * @param {object} options 加载参数
	 * @param {boolean} options.dynamic 是否动态加载
	 * @param {boolean} options.hidden 加载时是否隐藏
	 * @param {boolean} options.useDefaultTheme 是否使用场景自带的样式
	 * @param {boolean} options.useDefaultViewpoint 是否使用场景自带的相机视角
	 * @param {boolean} options.useDefaultRenderSettings 是否使用场景自带的环境参数
	 * @param {number[]} options.position 场景位置
	 * @param {number[]} options.rotation 场景的旋转角度
	 * @param {Function} options.onComplete 加载完成的回调
	 * @param {Function} options.onProgress 加载进度的回调
	 * @param {Function} options.onError 加载异常的回调
	 * @returns {Promise} 由于加载时异步过程,返回一个Promise对象
	 * @example
	 * THING.App.current.load('./assets/scenes/scene.gltf').then((ev) => {
	 *     let objs = ev.objects;
	 * 	   let count =  objs.length;
	 *    // @expect(count > 0);
	 * })
	 * @public
	 */
	load(url, options) {
		return this.resourcePool.load(url, options);
	}

	/**
	 * 加载插件
	 * @param {string} url 插件路径
	 * @param {object} options 加载参数
	 * @param {string} options.name 插件名称
	 * @param {Function} options.onComplete 加载成功的回调
	 * @param {Function} options.onError 加载失败的回调
	 * @returns {Promise} Promise对象
	 * @example
	 * THING.App.current.loadPlugin('./assets/plugins/plugin.json').then((plugin)=>{
	 *	console.log(plugin);
	 * });
	 * @public
	 */
	loadPlugin(url, options) {
		return this.resourcePool.loadPlugin(url, options);
	}
	getRunState() {
		const _private = this[__.private];
		return _private.state
	}
	/**
	 * 加载图片纹理
	 * @param {string} url 资源路径
	 * @param {LoadTextureResourceSamplerInfo} sampler 纹理采样参数
	 * @returns {THING.ImageTexture} 返回图片纹理
	 * @example
	 * 	let image = THING.App.current.loadImageTexture('./flower.png');
	 * 	await image.waitForComplete();
	 * 	console.log(image);
	 * @public
	 */
	loadImageTexture(url, sampler) {
		return this.resourcePool.loadImageTexture(url, sampler);
	}

	/**
	 * Load cube texture。
	 * @param {string} url The resource url
	 * @param {LoadTextureResourceSamplerInfo} sampler The sampler info.
	 * @returns {THING.CubeTexture} 返回立方体纹理
	 * @example
	 * 	let image = THING.App.current.loadCubeTexture([
	 * 	'./skyboxes/bluesky/posx.jpg', './skyboxes/bluesky/negx.jpg',
	 * 	'./skyboxes/bluesky/posy.jpg', './skyboxes/bluesky/negy.jpg',
	 * 	'./skyboxes/bluesky/posz.jpg', './skyboxes/bluesky/negz.jpg'
	 * ]);
	 * @public
	 */
	loadCubeTexture(url, sampler) {
		return this.resourcePool.loadCubeTexture(url, sampler);
	}

	/**
	 * 根据名称卸载插件
	 * @param {string} name 插件名称
	 * @returns {boolean} 是否卸载成功
	 * @example
	 * THING.App.current.uninstall(plugin.name);
	 * @public
	 */
	uninstall(name) {
		return this.plugin.uninstall(name);
	}

	/**
	 * Load from URL or data.
	 * @param {object} options The options.
	 * @param {string | Array<string>} options.url The resource URL(s).
	 * @param {object} options.data The json data.
	 * @example
	 * app.load({ url: './blueprints/myBP.json' });
	 * app.run();
	 * @private
	 */
	loadBlueprint(options = {}) {
		this.blueprint.load(options);
		this.blueprint.run();
	}

	/**
	 * Stop all blueprints. include all objects' blueprints.
	 * @private
	 */
	stopAllBlueprints() {
		// RootObject is not queryable, so it must be stopped first
		this.blueprint.stop();
		let all = this.query('*');
		all.forEach(item => {
			item.hasComponent('blueprint') && item.blueprint.stop();
		});
	}

	/**
	 * Check whether it's disposing.
	 * @type {boolean}
	 * @private
	 */
	get isDisposing() {
		let _private = this[__.private];

		return _private.flags.has(Flag.Disposing);
	}

	/**
	 * Check whether it had been disposed.
	 * @type {boolean}
	 * @private
	 */
	get isDisposed() {
		let _private = this[__.private];

		return _private.flags.has(Flag.Disposed);
	}

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

		return _private.options;
	}

	/**
	 * Get the global.
	 * @type {THING.Global}
	 * @private
	 */
	get global() {
		return this[__.private].global;
	}

	/**
	 * @public
	 * 获取全局渲染配置。
	 * @name renderSettings
	 * @memberof THING.App
	 * @instance
	 * @kind member
	 * @type {RenderSettingsInfo}
	 */

	/**
	 * Get the uuid.
	 * @type {string}
	 * @private
	 */
	get uuid() {
		return this[__.private].uuid;
	}

	/**
	 * Get the system info.
	 * @type {object}
	 * @private
	 */
	get systemInfo() {
		let _private = this[__.private];

		let extensions = _private.view.getExtensions();

		return _private.delegate.getAttribute('System', {
			context: _private.view.getContext(),
			extensions,
		});
	}

	/**
	 * 获取拾取器
	 * @type {THING.Picker}
	 * @example
	 * let picker = app.picker;
	 * // @expect(picker != null)
	 * picker.enable = false;
	 * // @expect(picker.enable == false)
	 * @public
	 */
	get picker() {
		let _private = this[__.private];

		return _private.picker;
	}

	// #endregion

	// #region Components

	addComponent(component, name, args) {
		if (component.className == Component.className) {
			Utils.error(`Add component (name: '${name}') to app failed`);
			return false;
		}

		return super.addComponent(component, name, args);
	}

	/**
	 * Get the blueprint component.
	 * @type {THING.BlueprintComponent}
	 * @private
	 */
	get blueprint() {
		return this.root.blueprint;
	}

	/**
	 * Get the dynamic load component.
	 * @type {THING.DynamicLoadComponent}
	 * @private
	 */
	get dynamicLoad() {
		return this.root.dynamicLoad;
	}

	// #endregion

	// #region Manager

	/**
	 * 获取帮助对象
	 * @member {THING.AppHelperComponent} helper
	 * @memberof THING.App
	 * @public
	 * @instance
	 */

	/**
	 * 获取TweenManager
	 * @member {THING.TweenManager} tweenManager
	 * @memberof THING.App
	 * @public
	 * @instance
	 */

	/**
	 * 获取关系管理器
	 * @member {THING.RelationshipManager} relationshipManager
	 * @memberof THING.App
	 * @public
	 * @instance
	 */

	/**
	 * 获取层级管理器
	 * @type {THING.LevelManager}
	 * @example
	 * let app = THING.App.current;
	 * let level = app.level;
	 * let target = app.query('.Entity')[0];
	 * level.change(target, {
	 *   onComplete: function(){
	 * 		let ret = level.current == target;
	 * 		// @expect(ret == true);
	 *   }
	 * });
	 * @public
	 */
	get level() {
		return this[__.private].level;
	}

	/**
	 * Get the level manager.
	 * @type {THING.LevelManager}
	 * @example
	 * let app = THING.App.current;
	 * let level = app.level;
	 * let levelManager = app.levelManager;
	 * let ret = level == levelManager;
	 * // @expect(ret == true);
	 * @deprecated 2.7
	 * @private
	 */
	get levelManager() {
		return this[__.private].level;
	}

	// #endregion

	// #region Render

	/**
	 * Add callback before dispose.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addBeforeDisposeCallback(function() {
	 *     console.log('before dispose');
	 * });
	 */
	addBeforeDisposeCallback(callback) {
		this[__.private].beforeDisposeCallbacks.add(callback);
	}

	/**
	 * Remove callback before dispose.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeBeforeDisposeCallback(function() {
	 *     console.log('before dispose');
	 * });
	 */
	removeBeforeDisposeCallback(callback) {
		this[__.private].beforeDisposeCallbacks.remove(callback);
	}

	/**
	 * Add callback after dispose.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addAfterDisposeCallback(function() {
	 *     console.log('after dispose');
	 * });
	 */
	addAfterDisposeCallback(callback) {
		this[__.private].afterDisposeCallbacks.add(callback);
	}

	/**
	 * Remove callback after dispose.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeAfterDisposeCallback(function() {
	 *     console.log('after dispose');
	 * });
	 */
	removeAfterDisposeCallback(callback) {
		this[__.private].afterDisposeCallbacks.remvoe(callback);
	}

	/**
	 * Add callback before tick.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addBeforeTickCallback(function() {
	 *     console.log('before tick');
	 * });
	 */
	addBeforeTickCallback(callback) {
		this[__.private].beforeTickCallbacks.add(callback);
	}

	/**
	 * Remove callback before tick.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeBeforeTickCallback(function() {
	 *     console.log('before tick');
	 * });
	 */
	removeBeforeTickCallback(callback) {
		this[__.private].beforeTickCallbacks.remove(callback);
	}

	/**
	 * Add callback after tick.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addAfterTickCallback(function() {
	 *     console.log('after tick');
	 * });
	 */
	addAfterTickCallback(callback) {
		this[__.private].afterTickCallbacks.add(callback);
	}

	/**
	 * Remove callback after tick.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeAfterTickCallback(function() {
	 *     console.log('after tick');
	 * });
	 */
	removeAfterTickCallback(callback) {
		this[__.private].afterTickCallbacks.remove(callback);
	}

	/**
	 * Add callback before render.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addBeforeRenderCallback(function() {
	 *     console.log('before render');
	 * });
	 */
	addBeforeRenderCallback(callback) {
		this[__.private].scene.addBeforeRenderCallback(callback);
	}

	/**
	 * Remove callback before render.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeBeforeRenderCallback(function() {
	 *     console.log('before render');
	 * });
	 */
	removeBeforeRenderCallback(callback) {
		this[__.private].scene.removeBeforeRenderCallback(callback);
	}

	/**
	 * Add callback after render.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.addAfterRenderCallback(function() {
	 *     console.log('after render');
	 * });
	 */
	addAfterRenderCallback(callback) {
		this[__.private].scene.addAfterRenderCallback(callback);
	}

	/**
	 * Remove callback after render.
	 * @param {Function} callback The function.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.removeAfterRenderCallback(function() {
	 *     console.log('after render');
	 * });
	 */
	removeAfterRenderCallback(callback) {
		this[__.private].scene.removeAfterRenderCallback(callback);
	}

	/**
	 * Convert pixel to screen coordinates in [-1, 1].
	 * @param {Array<number>} position The position.
	 * @returns {Array<number>} 返回屏幕坐标
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * let position = [100, 100];
	 * let screenPosition = app.pixelToScreenCoordinate(position);
	 * // @expect(screenPosition[0] == 0.5 && screenPosition[1] == 0.5);
	 */
	pixelToScreenCoordinate(position) {
		return MathUtils.pixelToScreenCoordinate(position, this.size);
	}

	/**
	 * Convert screen coordinates in [-1, 1] to pixel.
	 * @param {Array<number>} position The position in [-1, 1].
	 * @returns {Array<number>} 返回像素坐标
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * let position = [0.5, 0.5];
	 * let pixelPosition = app.screenCoordinateToPixel(position);
	 * // @expect(pixelPosition[0] == 100 && pixelPosition[1] == 100);
	 */
	screenCoordinateToPixel(position) {
		return MathUtils.screenCoordinateToPixel(position, this.size);
	}

	/**
	 * 启动app的运行和渲染
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * app.run();
	 */
	run() {
		let _private = this[__.private];

		if (_private.state == State.Running) {
			return;
		}

		_private.state = State.Running;

		// Get internal variables
		let timer = this.timer;

		// Start to render, elapsedTime is in milliseconds
		timer.init({
			frameRate: _private.options['frameRate'],
			onTick: (deltaTime) => {
				// The delta time in seconds

				// Before tick
				_private.beforeTickCallbacks.invoke(deltaTime);

				// Update core objects
				_private.marker.update(deltaTime);

				// Update managers
				this.objectManager.update(deltaTime);
				this.tweenManager.update(deltaTime);
				this.colliderManager.update(deltaTime);
				this.stateManager.update(deltaTime);
				this.eventManager.update(deltaTime);
				this.monitorManager.update(deltaTime);

				// After tick
				this.objectManager.lateUpdate(deltaTime);
				_private.afterTickCallbacks.invoke(deltaTime);

				_private.view.update(deltaTime);


				// Render scene
				_private.scene.render(deltaTime);
			}
		});

		// Start timer with delegate
		_private.delegate.run((elapsedTime, frame) => {
			this.timer.update(elapsedTime);
		});
	}

	/**
	 * 停止app的渲染
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * app.stop();
	 */
	stop() {
		let _private = this[__.private];

		if (_private.state == State.Stopped) {
			return;
		}

		_private.state = State.Stopped;

		_private.delegate.stop();
	}

	/**
	 * Lock size to ignore resize event.
	 * @param {number} width The width in pixel.
	 * @param {number} height The height in pixel.
	 * @returns {boolean} 返回是否锁定成功
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * let ret = app.lockSize(1024, 768);
	 * // @expect(ret == true);
	 */
	lockSize(width, height) {
		let _private = this[__.private];

		if (!_private.delegate.lockSize(width, height)) {
			return false;
		}

		this.resize(width, height);

		return true;
	}

	/**
	 * Unlock size.
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * app.unlockSize();
	 */
	unlockSize() {
		let _private = this[__.private];

		_private.delegate.unlockSize();

		let size = [0, 0];
		_private.delegate.getSize(size);

		this.resize(size[0], size[1]);
	}

	/**
	 * Capture screen shot into pixel buffer in RGBA color format.
	 * @param {number} width The image width in pixel, if it not provide then use the current width.
	 * @param {number} height The image height in pixel, if it not provide then use the current height.
	 * @returns {Uint8Array} 返回像素缓冲区
	 * @example
	 * 	let data = THING.App.current.captureScreenshotToData(640, 480);
	 *  let ret = data.length == (640 * 480 * 4);
	 *  // @expect(ret == true);
	 */
	captureScreenshotToData(width, height) {
		return this.camera.captureToData(width, height);
	}

	/**
	 * Capture screen shot into image.
	 * @param {number} width The image width in pixel.
	 * @param {number} height The image height in pixel.
	 * @returns {object} 返回图片对象
	 * @example
	 * 	let image = THING.App.current.captureScreenshotToImage(640, 480);
	 * 	let ret1 = image instanceof Image;
	 *  let ret2 = image.width == 640 && image.height == 480;
	 *  THING.Utils.setTimeout(() => {
	 *	// @expect(ret1 == true && ret2 == true);
	 *	}, 100);
	 */
	captureScreenshotToImage(width, height) {
		return this.camera.captureToImage(width, height);
	}

	/**
	 * Capture screen shot into file.
	 * @param {string} fileName The file name.
	 * @param {number} width The image width in pixel.
	 * @param {number} height The image height in pixel.
	 * @example
	 * 	THING.App.current.captureScreenshotToFile('cameraCapture', 640, 480);
	 */
	captureScreenshotToFile(fileName, width, height) {
		this.camera.captureToFile(fileName, width, height);
	}

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

		return _private.delegate;
	}

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

		return _private.view;
	}

	/**
	 * Get the container.
	 * @type {object}
	 * @example
	 * //the html div id is div3d
	 * let id = THING.App.current.container.id;
	 * let ret = id == 'div3d';
	 * // @expect(ret == true);
	 * @public
	 */
	get container() {
		let _private = this[__.private];

		return _private.delegate.getAttribute('Container');
	}

	/**
	 * Get the cursor position in container.
	 * @type {Array<number>}
	 * @private
	 */
	get cursorPosition() {
		let _private = this[__.private];

		return _private.delegate.getAttribute('CursorPosition');
	}

	/**
	 * 获取大小。
	 * @type {Array<number>}
	 * @public
	 * @example
	 * // div3d的大小是1024*768
	 * let size = THING.App.current.size;
	 * // @expect(size[0] == 1024 && size[1] == 768);
	 */
	get size() {
		let _private = this[__.private];

		return _private.size.slice(0);
	}

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

		return _private.scene;
	}

	/**
	 * 获取根对象。
	 * @type {THING.RootObject}
	 * @public
	 * @example
	 * 	let root = THING.App.current.root;
	 *  // @expect(root.isRootObject == true)
	 */
	get root() {
		let _private = this[__.private];

		return _private.root;
	}

	/**
	 * 获取所有的对象关系。
	 * @type {THING.Relationship[]}
	 * @public
	 * @readonly
	 * @example
	 *  let app = THING.App.current;
	 *  let count1 = app.relationships.length;
	 *  app.relationshipManager.addRelationship({
	 * 		source: app.root,
	 * 	    target: app.root
	 *  })
	 *  let count2 = app.relationships.length;
	 *  let ret = count2 - count1;
	 *  // @expect(ret == 1)
	 */
	get relationships() {
		return this.relationshipManager.relationships;
	}

	/**
	 * 获取/设置 相机对象
	 * @type {THING.Camera}
	 * @public
	 * @example
	 * let camera = new THING.Camera();
	 * camera.far = 100;
	 * THING.App.current.camera = camera;
	 * let ret = THING.App.current.camera.far == 100;
	 * // @expect(ret == true)
	 */
	get camera() {
		return this[__.private].scene.camera;
	}
	set camera(value) {
		this[__.private].scene.camera = value;
	}

	/**
	 * Get/Set the render camera.
	 * @type {THING.Camera}
	 * @private
	 */
	get renderCamera() {
		return this[__.private].scene.renderCamera;
	}
	set renderCamera(value) {
		this[__.private].scene.renderCamera = value;
	}

	/**
	 * Get the render capabilities.
	 * @type {THING.RendererCapabilities}
	 * @private
	 */
	get renderCapabilities() {
		return this[__.private].renderCapabilities;
	}

	/**
	 * 获取/设置像素比例。
	 * 设置/获取像素比例,默认为1,可以设置为0-1之间的值。
	 * 值越大,渲染效果越清晰(帧率较低);
	 * 值越小,渲染效果越模糊(帧率较高)。
	 * 在移动设备上,为了提高渲染帧率,可以将app.pixelRatio设置为小于1的值。
	 * @type {number}
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * app.pixelRatio = 0.5;
	 * let ret = app.pixelRatio == 0.5;
	 * // @expect(ret == true)
	 */
	get pixelRatio() {
		let _private = this[__.private];

		return _private.view.getPixelRatio();
	}
	set pixelRatio(value) {
		let _private = this[__.private];

		_private.view.setPixelRatio(value);
	}

	// #endregion

	// #region logger

	_outputLogger(funcName, condition) {
		let msg = { "condition": condition };
		Utils.logger.api("App_" + funcName, { msg: JSON.stringify(msg) });
	}

	// #endregion

	// #region Query

	/**
	 * 查询app下所有符合条件的对象并返回第一个
	 * @param {string} condition The conditions. 查询条件
	 * @returns {THING.BaseObject} 返回第一个符合条件的对象
	 * @example
	 * 	let obj = THING.App.current.find('.BaseObject');
	 *  let ret = obj instanceof THING.BaseObject;
	 * // @expect(ret == true)
	 *  let cylinder = THING.App.current.find('.Cylinder');
	 *  ret = cylinder == null;
	 * // @expect(ret == true)
	 * @public
	 */
	find(condition) {
		return this.root.find(condition);
	}

	/**
	 * 根据条件查询对象 返回所有满足条件的对象集合
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数 主要包括是否递归查询和是否包含自身
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * // 1. 基本查询
	 * let obj = THING.App.current.query('.BaseObject');
	 * let ret = obj[0] instanceof THING.BaseObject;
	 * // @expect(ret == true)
	 *
	 * // 2. 按对象名称查询
	 * let car = app.query('car01'); // 精确匹配名称
	 * let door = app.query('正门摄像头'); // 查询名称为"正门摄像头"的对象
	 *
	 * // 3. 按正则表达式查询名称
	 * // 方式一: 直接使用正则
	 * let frontDoors = app.query(/正门/); // 查询名称包含"正门"的对象
	 *
	 * // 方式二: 构造正则
	 * let regex = new RegExp("南天门");
	 * let gates = app.query(regex);
	 *
	 * // 方式三: 使用正则组合
	 * let doors = app.query(/(正门|侧门|后门)/); // 查询包含任意门的对象
	 *
	 * // 方式四: 动态构造正则
	 * let name = "南天门";
	 * let dynamicRegex = eval("/"+name+"/");
	 * let dynamicResult = app.query(dynamicRegex);
	 *
	 * // 4. 按对象类查询
	 * let points = app.query('.GeoBasePoint'); // 查询所有地图点对象
	 * let campus = app.query('.Campus'); // 查询园区类
	 * let buildings = app.query('.Building'); // 查询建筑类
	 * let floors = app.query('.Floor'); // 查询楼层类
	 * let rooms = app.query('.Room'); // 查询房间类
	 * let devices = app.query('.Device'); // 查询设备类对象
	 * let groups = app.query('.Group'); // 查询组
	 * let cabinets = app.query('.Cabinet'); // 查询机柜
	 * let racks = app.query('.Rack'); // 查询柜内架式设备
	 *
	 * // 5. 按对象ID查询 (注意: ID查询通常只返回一个对象,建议加[0])
	 * let obj1 = app.query('#100')[0]; // id查询需要加#
	 * let earth = app.query('[uuid=earthRoot]')[0]; // 获取地图根对象
	 *
	 * // 6. 按对象分类查询
	 * let alarms = app.query('[alarm]'); // 查询有alarm属性的对象
	 * let cameras = app.query('[twinType=摄像头]'); // 查询分类为摄像头的对象
	 *
	 * // 7. 按对象属性查询 (注意: 条件需要用[]括起来)
	 * // 7.1 自定义属性查询
	 * let chinaDevices = app.query('[userData/DATA/产地=中国]');
	 *
	 * // 7.2 系统属性查询
	 * let dbObj = app.query('[userData/_DBID_=7499775862803471]'); // 按数据库ID查询
	 * let ciObj = app.query('[userData/_CICODE_=7366821180693235]'); // 按CICODE查询
	 * let typeObj = app.query('[userData/_SHOWTYPE_=建筑]'); // 按对象分类查询
	 * let childObj = app.query('[userData/_PARENT_=优锘产业园_PARK]'); // 按父对象编号查询
	 * let parentNameObj = app.query('[userData/_PARENTNAME_=T1]'); // 按父对象名称查询
	 *
	 * // 7.3 其他属性查询
	 * let upperFloors = app.query('[levelNum>2]'); // 查询2层以上的楼层
	 * let hasChildren = app.query('[children!=""]'); // 查询有子对象的对象
	 * let visibleObj = app.query('[visible=1]'); // 查询可见对象
	 * let hiddenObj = app.query('[visible="0"]'); // 查询隐藏对象
	 * let uuidObj = app.query('[uuid="xxxx"]'); // 查询指定uuid的对象
	 *
	 * // 8. 按监控数据查询
	 * let tempObj = app.query('[MONITOR/_/_/温度/value=38]'); // 查询温度为38的对象
	 * let highTempObj = app.query('[MONITOR/_/_/温度/value>18.8]'); // 查询温度大于18.8的对象
	 *
	 * // 9. 查询全部对象
	 * // 9.1 按类型查询全部
	 * let allDevices = app.query('.Device'); // 查询全部设备
	 * let allThings = app.query('.Thing'); // 查询全部物体对象(含设备、标记)
	 * let allTwins = app.query('[twinType]'); // 获取孪生体管理中全部对象
	 *
	 * // 9.2 查询所有可管理对象
	 * let allObjects = app.query('.BaseObject');
	 * // 或使用以下方式
	 * let objects1 = app.query('[isBaseObject=1]');
	 * let objects2 = app.query('[queryID]');
	 * let objects3 = app.query('[pickable=1]');
	 *
	 * // 9.3 通配符查询
	 * let everything1 = app.query('*'); // 使用星号通配符
	 *
	 * // 9.4 查询所有对象(包括运行时创建的)
	 * let all = app.query('/');
	 *
	 * // 10. 局部查询
	 * let building = app.query('.Building')[0];
	 * let upperFloors = building.query('[levelNum > 1]'); // 查询该建筑二层以上楼层
	 *
	 * // 11. 多条件查询
	 * let visibleTrains = app.query('[businessType=高铁列车]&&[visible=1]'); // 与查询
	 * let floorsAndBuildings = app.query('.Floor || .Building'); // 或查询
	 *
	 * // 12. 筛选查询
	 * // 12.1 链式查询
	 * let ibmDevices = app.query('.Device').query('[userData/DATA/品牌=IBM]');
	 *
	 * // 12.2 组合查询
	 * let brandAA = app.query('[userData/DATA/品牌=AA]');
	 * let deviceOrBrandAA = app.query('.Device').add(brandAA); // 并集
	 * let devicesExceptCar = app.query('.Device').not('car01'); // 差集
	 *
	 * // 13. 查询结果操作
	 * // 13.1 批量修改基本属性
	 * app.query('.Building').visible = false; // 隐藏所有建筑
	 *
	 * // 13.2 遍历操作
	 * app.query('.Building').forEach(building => {
	 *   console.log(building.name);
	 * });
	 *
	 * // 13.3 层级访问
	 * app.query('.Building').forEach(building => {
	 *   building.floors.forEach(floor => { // 访问建筑的楼层
	 *     floor.style.opacity = 0.5;
	 *   });
	 * });
	 *
	 * // 14. 根据标签查询
	 * // 查询包含 Floor 标签的对象
	 * let floors = app.query('tags:Floor');
	 *
	 * // 使用 and 操作符查询同时包含多个标签的对象
	 * let objects = app.query('tags:and(Floor,Wall)');
	 *
	 * // 使用 or 操作符查询包含任一标签的对象
	 * let objects = app.query('tags:or(Floor,Wall)');
	 *
	 * // 使用 not 操作符查询不包含指定标签的对象
	 * let objects = app.query('tags:not(Floor)');
	 * @public
	 */
	query(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('query', condition);
		}

		return this.root.query(condition, options);
	}

	/**
	 * 查询app下所有符合条件的对象并返回所有匹配的结果
	 * @param {string} condition The conditions. 查询条件
	 * @param {ObjectQueryOptions} options 查询参数 主要包括是否递归查询和是否包含自身
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let objects = THING.App.current.queryAll('.BaseObject');
	 * 	let ret = objects.length > 0;
	 * // @expect(ret == true)
	 * 	let cylinders = THING.App.current.queryAll('.Cylinder');
	 * 	ret = cylinders.length == 0;
	 * // @expect(ret == true)
	 * @public
	 */
	queryAll(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryAll', condition);
		}

		return this.root.queryAll(condition, options);
	}

	/**
	 * 根据名称查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数 主要包括是否递归查询和是否包含自身
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByName('car01')
	 * 	let ret = car.length == 0;
	 * 	// @expect(ret == true)
	 *  car = THING.App.current.queryByName('car1')
	 *  ret = car[0].name == 'car1';
	 * // @expect(ret == true)
	 * @public
	 */
	queryByName(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByName', condition);
		}

		return this.root.queryByName(condition, options);
	}

	/**
	 * 根据id查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryById('1')
	 * 	let ret = car.length == 0;
	 *  // @expect(ret == true);
	 *  car = THING.App.current.queryById('car01')
	 *  ret = car[0].id == 'car01';
	 *  // @expect(ret == true)
	 * @public
	 */
	queryById(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryById', condition);
		}

		return this.root.queryById(condition, options);
	}

	/**
	 * 根据tag查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByTags('car');
	 * 	let ret = car.length == 0;
	 *  // @expect(ret == true)
	 * 	let entity = THING.App.current.queryByTags('Entity');
	 * 	ret = entity[0].tags.has('Entity');
	 *  // @expect(ret == true)
	 * @public
	 */
	queryByTags(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByTags', condition);
		}

		return this.root.queryByTags(condition, options);
	}

	/**
	 * 根据uuid查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = app.queryByUUID('1')
	 * 	let ret = car.length == 0;
	 *  // @expect(ret == true);
	 *  car = app.queryByUUID('1605')
	 *  ret = car[0].uuid == '1605';
	 *  // @expect(ret == true)
	 * @public
	 */
	queryByUUID(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByUUID', condition);
		}

		return this.root.queryByUUID(condition, options);
	}

	/**
	 * 根据类型查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByType('car')
	 * 	let ret = car.length == 0;
	 * // @expect(ret == true)
	 *  let obj = THING.App.current.queryByType('Entity')
	 *  ret = obj[0].type == 'Entity';
	 * // @expect(ret == true)
	 * @public
	 */
	queryByType(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByType', condition);
		}

		return this.root.queryByType(condition, options);
	}

	/**
	 * 根据孪生体类型查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let equipment = THING.App.current.queryByDTType('Equipment')
	 * 	let ret = equipment.length == 0;
	 * // @expect(ret == true)
	 *  let sensor = THING.App.current.queryByDTType('Sensor')
	 *  ret = sensor[0].dtType == 'Sensor';
	 * // @expect(ret == true)
	 * @public
	 */
	queryByDTType(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByDTType', condition);
		}

		return this.root.queryByDTType(condition, options);
	}

	/**
	 * 根据userData(用户数据)查询
	 * @param {string} condition 查询条件
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByUserData('test=1');
	 * 	let ret = car.length == 0;
	 *  // @expect(ret == true)
	 *  car = THING.App.current.queryByUserData('id=666');
	 *  ret = car[0].userData.id == 666;
	 * // @expect(ret == true)
	 * @public
	 */
	queryByUserData(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByUserData', condition);
		}

		return this.root.queryByUserData(condition, options);
	}

	/**
	 * 根据userData查询子对象。
	 * @param {string} condition 查询条件。
	 * @param {ObjectQueryOptions} options 查询选项。
	 * @returns {THING.Selector} 返回查询结果
	 * @example
	 * 	let car = THING.App.current.queryByData('test=1');
	 * 	let ret = car.length == 0;
	 *  // @expect(ret == true)
	 *  car = THING.App.current.queryByData('id=666');
	 *  ret = car[0].userData.id == 666;
	 * // @expect(ret == true)
	 * @deprecated 2.7
	 * @private
	 */
	queryByData(condition, options) {
		return this.queryByUserData(condition, options);
	}

	/**
	 * 根据正则表达式查询
	 * @param {string} condition 正则表达式
	 * @param {ObjectQueryOptions} options 查询参数
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByRegExp(/car/);
	 * 	let ret = car.length == 4;
	 *  //@expect(ret == true)
	 * @public
	 */
	queryByRegExp(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryByRegExp', condition.toString());
		}

		return this.root.queryByRegExp(condition, options);
	}

	/**
	 * 根据正则表达式查询子对象。
	 * @param {string} condition 查询条件。
	 * @param {ObjectQueryOptions} options 查询选项。
	 * @returns {THING.Selector} 查询结果
	 * @example
	 * 	let car = THING.App.current.queryByRegExp(/car/);
	 * 	let ret = car.length == 4;
	 *  //@expect(ret == true)
	 * @deprecated 2.7
	 * @private
	 */
	queryByReg(condition, options) {
		return this.queryByRegExp(condition, options);
	}

	/**
	 * 异步的方式查询对象 返回一个Promise对象
	 * </br>以异步模式根据条件查询子对象。
	 * @param {string} condition 查询条件。
	 * @param {ObjectQueryOptions} options 查询选项。
	 * @returns {Promise<any>} 返回Promise对象
	 * @private
	 * @example
	 * let app = THING.App.current;
	 * let promise = app.queryAsync('.BaseObject');
	 * promise.then(function(objects) {
	 *     console.log(objects);
	 * });
	 */
	queryAsync(condition, options) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryAsync', condition);
		}

		return this.root.queryAsync(condition, options);
	}

	/**
	 * 查询关系
	 * @param {object} options 参数
	 * @param {string} options.type 关系类型
	 * @param {string} options.name 关系名称
	 * @returns {Array<THING.Relationship>} 返回关系数组
	 * @example
	 * let lightSwitch = new THING.Box();
	 * let light = new THING.Box();
	 * let rel = new THING.Relationship({
	 *      type: 'control',
	 *      source: lightSwitch,
	 *      target: light
	 * });
	 * let relationships = app.queryRelationships({type: 'control'});
	 * let ret1 = relationships[0].source == lightSwitch;
	 * let ret2 = relationships[0].target == light;
	 * // @expect(ret1 == true && ret2 == true);
	 * @public
	 */
	queryRelationships(options = {}) {
		if (_NEED_LOGGER) {
			this._outputLogger('queryRelationships', options);
		}

		return this.relationshipManager.queryRelationships(options);
	}

	// #endregion

	// #region Events

	/**
	 * 根据类型获取事件。
	 * @param {string} type 事件类型,null 表示获取所有事件。
	 * @returns {Array<ObjectListenerInfo>} 返回事件信息数组
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * let events = app.getEvents();
	 * let ret = events.length > 0;
	 * // @expect(ret == true);
	 */
	getEvents(type) {
		return this.root.getEvents(type);
	}

	/**
	 * 暂事件。
	 * @param {string} type 事件类型。
	 * @param {string} condition 选择对象的条件。
	 * @param {string} tag 事件标签。
	 * @returns {ObjectListenerInfo} 返回事件信息
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * let event = app.getEvent('test', '.BaseObject', 'test');
	 * let ret = event instanceof THING.ObjectListenerInfo;
	 * // @expect(ret == true);
	 */
	getEvent(type, condition, tag) {
		return this.root.getEvent(type.toLowerCase(), condition, tag);
	}

	/**
	 * The base event.
	 * @typedef BaseEvent
	 * @property {string} type The case-insensitive name identifying the type of the event.
	 * @property {THING.BaseObject} object The object that triggered the event.
	 * @property {THING.BaseObject} target A reference to the object to which the event was originally dispatched.
	 */

	/**
	 * The MouseEvent interface represents events that occur due to the user interacting with a pointing device (such as a mouse).
	 * @typedef {BaseEvent} MouseEvent
	 * @property {number} x The X coordinate of the mouse pointer in local (screen) coordinates.
	 * @property {number} y The Y coordinate of the mouse pointer in local (screen) coordinates.
	 * @property {number} button The button number that was pressed when the mouse event was fired.
	 * @property {string} buttonType The button type that was pressed when the mouse event was fired.
	 * @property {boolean} ctrlKey Returns true if the ctrl key was down when the mouse event was fired.
	 * @property {boolean} shiftKey Returns true if the shift key was down when the mouse event was fired.
	 * @property {boolean} altKey Returns true if the alt key was down when the mouse event was fired.
	 */

	/**
	 * The KeyboardEvent objects describe a user interaction with the keyboard.
	 * @typedef {BaseEvent} KeyboardEvent
	 * @property {number} keyCode Returns a number representing the key value of the key represented by the event.
	 * @property {string} code Returns a string representing the key value of the key represented by the event.
	 * @property {boolean} ctrlKey Returns a boolean value that is true if the Ctrl key was active when the key event was generated.
	 * @property {boolean} shiftKey Returns a boolean value that is true if the Shift key was active when the key event was generated.
	 * @property {boolean} altKey  Returns a boolean value that is true if the Alt key was active when the key event was generated.
	 */

	/**
	 * EventCallback表示一个事件触发的回调函数。
	 * @callback EventCallback
	 * @param {MouseEvent|KeyboardEvent} ev 事件对象,包含了事件的类型和相关属性。
	 * @returns {any}
	 * @public
	 */

	/**
	 * 注册事件
	 * @param {string} type 事件类型。
	 * @param {string} condition 选择对象的条件。在注册事件之前进行查询,对符合条件的对象进行注册。
	 * @param {EventCallback} callback 回调函数。事件触发时执行的函数。
	 * @param {string} tag 事件标签。用于标识事件。
	 * @param {number} priority 优先级值(默认为0,数值越高,优先级越高)。
	 * @param {ObjectEventOptions} options 事件参数。
	 * @example
	 * let app = THING.App.current;
	 * let markOn = 0;
	 * app.on('testOn', function(ev) {
	 * 		markOn = 1;
	 * });
	 * app.trigger('testOn');
	 * // @expect(markOn == 1);
	 * @public
	 * @note 以下事件默认开启 useCapture: true:
	 * 'wheel',
	 * 'mouseenter',
	 * 'mouseleave',
	 * 'mousemove',
	 * 'mousedown',
	 * 'mouseup',
	 * 'keydown',
	 * 'keyup',
	 * 'keypress',
	 * 'click',
	 * 'dblclick',
	 * 'beforeaddchild',
	 * 'afteraddchild',
	 * 'beforeremovechild',
	 * 'afterremovechild'
	 */
	on(type, condition, callback, tag, priority, options) {
		this.root.on(type, condition, callback, tag, priority, options);
	}

	/**
	 * 注册一次性的事件,此事件响应只会触发一次,触发后会被自动注销。
	 * @param {string} type 事件类型
	 * @param {string} condition 选择对象的条件。在注册事件之前进行查询,对符合条件的对象进行注册。
	 * @param {EventCallback} callback 回调函数。事件触发时执行的函数。
	 * @param {string} tag 事件标签。用于标识事件。
	 * @param {number} priority 优先级值(默认为0,数值越高,优先级越高)。
	 * @param {ObjectEventOptions} options 事件参数。
	 * @example
	 * let app = THING.App.current;
	 * let markOnce = 0;
	 * app.once('testOnce', function(ev) {
	 * 		markOnce = 1;
	 * });
	 * app.trigger('testOnce');
	 * // @expect(markOnce == 1);
	 * markOnce = 0;
	 * app.trigger('testOnce');
	 * // @expect(markOnce == 0);
	 * @public
	 */
	once(type, condition, callback, tag, priority, options) {
		this.root.once(type, condition, callback, tag, priority, options);
	}

	/**
	 * 注销事件
	 * @param {string} type 事件类型。
	 * @param {string} condition 选择对象的条件。注册事件的条件。
	 * @param {string} tag 事件标签。用于标识事件。
	 * @example
	 * let app = THING.App.current;
	 * let markOff = 0;
	 * app.on('testOff', function(ev) {
	 * 		markOff = 1;
	 * });
	 * app.trigger('testOff');
	 * // @expect(markOff == 1);
	 * markOff = 0;
	 * app.off('testOff');
	 * app.trigger('testOff');
	 * // @expect(markOff == 0);
	 * @public
	 */
	off(type, condition, tag) {
		this.root.off(type, condition, tag);
	}

	/**
	 * 暂停事件
	 * @param {string} type 事件类型
	 * @param {string} condition 注册事件的条件
	 * @param {string} tag 事件的tag
	 * @returns {boolean} 返回是否暂停成功
	 * @example
	 * let app = THING.App.current;
	 * let markPause = 0;
	 * app.on('testPause', function(ev) {
	 * 		markPause = 1;
	 * });
	 * app.trigger('testPause');
	 * // @expect(markPause == 1);
	 * markPause = 0;
	 * app.pauseEvent('testPause');
	 * app.trigger('testPause');
	 * // @expect(markPause == 0);
	 * @public
	 */
	pauseEvent(type, condition, tag) {
		return this.root.pauseEvent(type, condition, tag);
	}

	/**
	 * 恢复事件
	 * @param {string} type 事件类型。
	 * @param {string} condition 选择对象的条件。注册事件的条件。
	 * @param {string} tag 事件标签。用于标识事件。
	 * @returns {boolean} 返回是否恢复成功
	 * @example
	 * let app = THING.App.current;
	 * let markResume = 0;
	 * app.on('testResume', function(ev) {
	 * 		markResume = 1;
	 * });
	 * app.trigger('testResume');
	 * // @expect(markResume == 1);
	 * markResume = 0;
	 * app.pauseEvent('testResume');
	 * app.trigger('testResume');
	 * // @expect(markResume == 0);
	 * app.resumeEvent('testResume');
	 * app.trigger('testResume');
	 * // @expect(markResume == 1);
	 * @public
	 */
	resumeEvent(type, condition, tag) {
		return this.root.resumeEvent(type, condition, tag);
	}

	/**
	 * 触发事件。
	 * @param {string} type 事件类型。
	 * @param {string} condition 事件条件。
	 * @param {object} data 事件数据
	 * @param {string} tagName 事件标签名称。
	 * @returns {*} 返回事件信息
	 * @example
	 *  let markTrigger = {};
	 * 	THING.App.current.on('myEvent', function(ev) {
	 * 		markTrigger = ev;
	 * 	});
	 * 	THING.App.current.trigger('myEvent', { result: true });
	 *  let ret = markTrigger.result;
	 *  // @expect(ret == true);
	 *
	 * 	new THING.Box();
	 *
	 *  app.on('test', (e) => {
	 *      console.log('无条件+tag1', e)
	 *  }, 'MyTag1');
	 *  app.on('test', (e) => {
	 *      console.log('无条件+tag2', e)
	 *  }, 'MyTag2');
	 *
	 * app.on('test', ".Box", (e) => {
	 * 	    console.log('条件+tag1', e)
	 * }, 'MyTag1');
	 * app.on('test', ".Box", (e) => {
	 * 	    console.log('条件+tag2', e)
	 * }, 'MyTag2');
	 *
	 * // 事件类型 + 事件条件,触发事件。
	 * app.trigger('test', '.Box');
	 * // 事件类型 + 事件数据,触发事件
	 * app.trigger('test', { result: true });
	 * // 事件类型 + 事件条件 + 事件标签,触发事件
	 * app.trigger('test', '.Box', 'MyTag2');
	 * // 事件类型 + 事件条件 + 事件参数,触发事件
	 * app.trigger('test', '.Box', { result: true });
	 * // 事件类型 + 事件条件 + 事件数据 + 事件标签,触发事件
	 * app.trigger('test', '.Box', { result: true },'MyTag2');
	 *
	 * // 注意事项:
	 * // 事件标签不能单独使用,否则事件标签和事件条件无法区分。
	 * app.trigger('test', null, 'MyTag2');
	 * @public
	 */
	trigger(type, condition, data, tagName) {
		return this.root.trigger(type, condition, data, tagName);
	}

	/**
	 * 触发事件。
	 * @param {string} type 事件类型。
	 * @param {string} condition 事件条件。
	 * @param {object} data 事件数据
	 * @param {string} tagName 事件标签名称。
	 * @returns {Promis} 返回Promise
	 * @public
	 */
	invoke(type, condition, data, tagName) {
		return this.root.invoke(type, condition, data, tagName);
	}

	/**
	 * 添加事件过滤器
	 * @param {string} type 事件类型。
	 * @param {string} condition 事件条件。
	 * @param {object} tag 事件标签。
	 * @param {Function} callback 事件回调函数。
	 */
	addEventFilter(type, condition, tag, callback) {
		this.root.addEventFilter(type, condition, tag, callback);
	}
	/**
	 * 移除事件过滤器
	 * @param {string} type 事件类型。
	 * @param {string} condition 事件条件。
	 * @param {object} tag 事件标签。
	 */
	removeEventFilter(type, condition, tag) {
		this.root.removeEventFilter(type, condition, tag);
	}
	/**
	 * 移除所有事件过滤器
	 */
	removeAllEventFilters() {
		this.root.removeAllEventFilters();
	}
	// #endregion

	// #region Clear Buffers

	/**
	 * Enable/Disable auto clear color buffer.
	 * @type {boolean}
	 * @private
	 */
	get autoClearColorBuffer() {
		return this.camera.autoClearColorBuffer;
	}
	set autoClearColorBuffer(value) {
		this.camera.autoClearColorBuffer = value;
	}

	/**
	 * Enable/Disable auto clear depth buffer.
	 * @type {boolean}
	 * @private
	 */
	get autoClearDepthBuffer() {
		return this.camera.autoClearDepthBuffer;
	}
	set autoClearDepthBuffer(value) {
		this.camera.autoClearDepthBuffer = value;
	}

	/**
	 * Enable/Disable auto clear stencil buffer.
	 * @type {boolean}
	 * @private
	 */
	get autoClearStencilBuffer() {
		return this.camera.autoClearStencilBuffer;
	}
	set autoClearStencilBuffer(value) {
		this.camera.autoClearStencilBuffer = value;
	}

	/**
	 * scale of gpupicker‘s buffer
	 * @type {number}
	 * @private
	 */
	get gpuPickerBufferScale() {
		return this.camera.gpuPickerBufferScale;
	}
	set gpuPickerBufferScale(value) {
		this.camera.gpuPickerBufferScale = value;
	}

	// #endregion

	// #region Resources

	/**
	 * 获取/设置背景。
	 * @type {number | string | Array<number> | THING.ImageTexture | THING.VideoTexture}
	 * @example
	 * THING.App.current.background = 'gray';
	 * let ret = THING.App.current.background instanceof Array;
	 * // @expect(ret == true )
	 * // 设置背景为天空盒(多张)
	 * THING.App.current.background = new THING.CubeTexture([
	 *     './skyboxes/bluesky/posx.jpg', './skyboxes/bluesky/negx.jpg',
	 *     './skyboxes/bluesky/posy.jpg', './skyboxes/bluesky/negy.jpg',
	 *     './skyboxes/bluesky/posz.jpg', './skyboxes/bluesky/negz.jpg'
	 * ]);
	 * // 设置背景为天空盒(单张)
	 * // 使用环境贴图创建新的图像纹理,作为天空盒
	 * THING.App.current.background = new THING.ImageTexture({url: "../texture/envmap.jpg",  mappingType: THING.ImageMappingType.EquirectangularReflection });
	 * // 创建新的图像纹理,作为天空盒
	 * THING.App.current.background = new THING.ImageTexture({url: "../texture/envmap.jpg" });
	 * // 设置背景为图像路径,作为天空盒
	 * THING.App.current.background = "../texture/envmap.jpg";
	 * @public
	 */
	get background() {
		return this.camera.background;
	}
	set background(value) {
		this.camera.background = value;
	}

	/**
	 * 获取/设置场景默认的环境图。
	 * @type {THING.CubeTexture}
	 * @example
	 * THING.App.current.envMap = new THING.CubeTexture('./skyboxes/bluesky');
	 * let ret = THING.App.current.envMap.url == './skyboxes/bluesky';
	 * // @expect(ret == true )
	 * @public
	 */
	get envMap() {
		return this[__.private].scene.envMap;
	}
	set envMap(value) {
		this[__.private].scene.envMap = value;
	}

	// #endregion

	// #region Event

	/**
	 * 重置尺寸。
	 * @param {number} width 像素宽度。
	 * @param {number} height 像素高度。
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * app.resize(100, 100);
	 */
	resize(width, height) {
		let _private = this[__.private];

		// Check whether size has changed
		let size = _private.size;
		if (width == size[0] && height == size[1]) {
			return;
		}

		// Update size
		size[0] = width;
		size[1] = height;

		// Resize core and managers
		_private.scene.resize(width, height);
		this.objectManager.resize(width, height);

		// Reisze view
		_private.view.setSize([width, height]);

		// Notify resize event
		this.trigger('Resize', { width, height });
	}

	// #endregion

	/**
	 * 是否是app对象
	 * @type {boolean}
	 * @example
	 * let app = THING.App.current;
	 * if (app.isApp) {
	 * 		console.log(`It's app`);
	 * 	}
	 * @public
	 */
	get isApp() {
		return true;
	}

	/**
	 * 是否在编辑器环境运行
	 * @type {boolean}
	 * @public
	 */
	get isEditor() {
		let _private = this[__.private];
		return _private.isEditor;
	}

	/**
	 * 设置/获取工作路径 调用app.resolveURL(url)时,如果url以"/"开头,会在url之前拼上workPath
	 * @type {string}
	 * @example
	 * THING.App.current.workPath = 'project/root/';
	 * let ret = THING.App.current.workPath;
	 * // @expect(ret == 'project/root/' )
	 * @public
	 */
	get workPath() {
		return this._workPath;
	}
	set workPath(val) {
		this._workPath = val;
	}

	/**
	 * 解析url
	 * @param {string} url 带解析的url 解析时会根据app.workPath进行url的拼接
	 * @returns {string} 解析后的url
	 * @public
	 * @example
	 * let app = THING.App.current;
	 * let url = app.resolveURL('test.png');
	 * // @expect(url == 'project/root/test.png');
	 */
	resolveURL(url) {
		if (url._startsWith('/')) {
			if (this.workPath) {
				return this.workPath._appendURL(url);
			}
			else {
				return url;
			}
		}
		return url;
	}

}

let startupTimer = setInterval(() => {
	if (!Utils._scriptLoader) {
		return;
	}

	Utils._scriptLoader.startAnimationFrame((deltaTime) => {
		// Make delta time to seconds
		deltaTime /= 1000;

		// Update selector
		Selector.update(deltaTime);

		// Update Utils
		Utils.update(deltaTime);
	});

	clearInterval(startupTimer);
}, 100);

export { App }