Source: core/Global.js

import { Utils } from '../common/Utils';
import { ImageTexture } from '../resources/ImageTexture';
import { EmptyTexture } from '../resources/EmptyTexture';
import water_normal_image from '../../assets/images/water_normal.jpg';

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

/**
 * @class Global
 * The global.
 * @memberof THING
 */
class Global {

	/**
	 * The global information.
	 * @param {Object} param The initial parameters.
	 */
	constructor(param = {}) {
		this[__.private] = {};
		let _private = this[__.private];

		_private.readyPromise = null;

		_private.cache = {
			classes: new Map(),
			models: {},
			images: {},
			ready: false,
		};

		_private.gc = _DEBUG ? Utils.createObject('GarbageCollector', (key) => {
			Utils.log(`GC destroy {${key}} object`);
		}) : null;

		this._setupMacros(param);
		this._setupFactory(param);
		this._setupCache();
	}

	// #region Private

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

		let images = _private.cache.images;

		// Create empty texture
		images['empty_image_texture'] = new EmptyTexture();

		// Create images cache
		return [
			{ name: 'water_normal_image', resource: water_normal_image },
		].map(info => {
			return new Promise((resolve, reject) => {
				let image = Utils.loadImageFileFromBase64(info.resource);

				let imageTexture = new ImageTexture({ resource: image });
				images[info.name] = imageTexture;

				resolve();
			});
		});
	}

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

		let models = _private.cache.models;

		// Create nodes cache
		let box = resourceManager.parseModel('Box');
		box.setVisible(false);

		let plane = resourceManager.parseModel('Plane');
		plane.setVisible(false);

		// Save nodes cache
		models['box'] = box;
		models['plane'] = plane;
	}

	_setupMacros(param) {
		let macros = Utils.getMacros();

		macros['PRO'] = typeof _PRO_ !== 'undefined' ? _PRO_ : false;
		macros['VERSION'] = typeof _VERSION !== 'undefined' ? _VERSION : '';
		macros['COMPILETIME'] = typeof _COMPILETIME !== 'undefined' ? _COMPILETIME : '';
		macros['GITCOMMITHASH'] = typeof _GITCOMMITHASH !== 'undefined' ? _GITCOMMITHASH : '';
		macros['GITBRANCH'] = typeof _GITBRANCH !== 'undefined' ? _GITBRANCH : '';
		macros['DEBUG'] = Utils.parseValue(param['debugMode'], typeof _DEBUG !== 'undefined' ? _DEBUG : false);
		macros['VERBOSE'] = Utils.parseValue(param['verboseMode'], typeof _VERBOSE !== 'undefined' ? _VERBOSE : false);
	}

	_setupFactory(param) {
		// Get factories
		let factories = param['factory'] || [];
		if (!Utils.isArray(factories)) {
			factories = [factories];
		}

		// Add factories
		factories.forEach(factory => {
			Utils.addFactory(factory);
		});
	}

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

		let cache = _private.cache;

		// Get resource from cache.
		cache.getResource = function (classType) {
			let resource = cache[classType];
			if (resource === undefined) {
				resource = this.createObject(classType);

				cache[classType] = resource;
			}

			return resource;
		}
	}

	// #endregion

	// #region Cache

	/**
	 * Load resource.
	 * @param {Object} resourceManager The resource manager.
	 * @private
	 */
	loadResource(resourceManager) {
		let _private = this[__.private];
		let cache = _private.cache;

		// Load models
		this._loadModels(resourceManager);

		// Load textures
		let promises = [];
		promises.push(...this._loadImages());

		// Wait for all resources loaded
		_private.readyPromise = Promise.all(promises).then(() => {
			cache['ready'] = true;

			if (cache['resolves']) {
				cache['resolves'].forEach(resolve => {
					resolve();
				});

				cache['resolves'] = [];
			}
		});
	}

	/**
	 * Get the class names of object.
	 * @param {Object} object The class object.
	 * @returns {Array<String>}
	 * @private
	 */
	getClassNamesFromObject(object) {
		let _private = this[__.private];

		let proto = object.__proto__;

		let className = Utils.getClassNameFromProto(proto);

		let classes = _private.cache.classes;
		let classNames = classes.get(className);
		if (!classNames) {
			classNames = Utils.getClassNamesFromProto(object.__proto__);
			classes.set(className, classNames);
		}

		return classNames;
	}

	get cache() {
		return this[__.private].cache;
	}

	get gc() {
		return this[__.private].gc;
	}

	// #endregion

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

		let cache = _private.cache;
		cache.classes.clear();

		for (let key in cache.models) {
			cache.models[key].dispose();
		}

		cache.models = {};

		for (let key in cache.images) {
			cache.images[key].dispose();
		}

		cache.images = {};
	}

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

		return _private.readyPromise;
	}

	/**
	 * Get/Set global http request headers.
	 * @type {Object}
	 * @private
	 */
	get httpRequestHeaders() {
		return Utils._system.getHttpRequestHeaders();
	}
	set httpRequestHeaders(value) {
		Utils._system.setHttpRequestHeaders(value);
	}

	/**
	 * Get/Set the callback function when get resource type in ajax/fetch hook.
	 * @type {Function}
	 * @private
	 */
	get onGetResourceTypeFromUrl() {
		return Utils._system.onGetResourceTypeFromUrl;
	}
	set onGetResourceTypeFromUrl(value) {
		Utils._system.onGetResourceTypeFromUrl = value;
	}

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

}

export { Global }