Source: resources/BaseResource.js

import { Flags, MathUtils, RefCountObject, ResolvablePromise } from '@uino/base-thing';
import { Utils } from '../common/Utils'

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

const Flag = {
	Loaded: 1 << 0,
}

/**
 * @class BaseResource
 * The base resource.
 * @memberof THING
 */
class BaseResource extends RefCountObject {

	constructor(param = {}) {
		super();

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

		_private.uuid = MathUtils.generateUUID();

		_private.flags = new Flags();
		_private.pendingPromise = null;

		_private.external = Utils.parseValue(param['extras'] || param['external'], null);

		_private.context = null;

		_private.complete = Utils.parseFunc(param['onComplete'] || param['complete']);
	}

	// #region Private

	// #endregion

	// #region Overrides

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

		if (_private.context) {
			_private.context.dispose();
			_private.context = null;
		}

		_private.pendingPromise = null;
	}

	/**
	 * Notify complete.
	 * @private
	 */
	onNotifyComplete() {
		let _private = this[__.private];

		let pendingPromise = _private.pendingPromise;
		if (pendingPromise) {
			if (this.refCount) {
				pendingPromise.resolve(this);
			}
			// Prevent dispose image in loading state
			else {
				pendingPromise.reject();
			}
		}

		_private.flags.set(Flag.Loaded, true);

		// Notify outside
		if (_private.complete) {
			_private.complete({ image: this });
			_private.complete = null;
		}
	}

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

		// Here we must create pending promise to prevent calling waitForComplete() to wait it finished
		_private.pendingPromise = _private.pendingPromise || new ResolvablePromise();
		_private.pendingPromise.reject(ev);
	}

	// #endregion

	/**
	 * Dispose.
	 * @private
	 */
	dispose() {
		this.release();
	}

	/**
	 * Unload resource.
	 * @private
	 */
	unloadResource() {
		if (!this.loaded) {
			return;
		}

		let _private = this[__.private];

		_private.flags.enable(Flag.Loaded, false);

		_private.pendingPromise = null;
	}

	/**
	 * Wait for object load completed.
	 * @returns {Promise<any>}
	 */
	waitForComplete() {
		if (this.loaded) {
			return Promise.resolve(this);
		}

		let _private = this[__.private];

		_private.pendingPromise = _private.pendingPromise || new ResolvablePromise();
		return _private.pendingPromise;
	}

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

		_private.context = value;
	}

	/**
	 * Convert to string(always use uuid to stringify).
	 * @returns {String}
	 * @private
	 */
	toString() {
		let _private = this[__.private];

		return _private.uuid;
	}

	// #region Accessors

	/**
	 * Get/Set the pending promise.
	 * @type {Promise<any>}
	 * @private
	 */
	get pendingPromise() {
		let _private = this[__.private];

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

		_private.pendingPromise = value;
	}

	/**
	 * Check whether it's already disposed.
	 * @type {Boolean}
	 * @private
	 */
	get disposed() {
		return this.refCount == 0;
	}

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

		return _private.external;
	}

	/**
	 * Get the unique ID.
	 * @type {String}
	 */
	get uuid() {
		let _private = this[__.private];

		return _private.uuid;
	}

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

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

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

		return _private.context;
	}

	// #endregion

	/**
	 * Check class type.
	 * @type {Boolean}
	 * @private
	 */
	get isBaseResource() {
		return true;
	}

	// #endregion

}

export { BaseResource }