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
 * 基础资源。
 * @memberof THING
 * @extends THING.RefCountObject
 * @public
 */
class BaseResource extends RefCountObject {

	/**
	 * 基础资源类构造函数,用于初始化资源对象。
	 * @param {object} param - 初始参数对象,包含资源的配置和回调。
	 * @param {string} param.extras - 额外的资源信息,通常用于存储附加数据。
	 * @param {string} param.external - 外部资源的引用,可能用于加载外部文件。
	 * @param {Function} param.onComplete - 资源加载完成后的回调函数。
	 */
	constructor(param = {}) {
		super();

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

		_private.app = param['app'] || Utils.getCurrentApp();

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

	/**
	 * 等待资源加载完成。
	 * @returns {Promise<any>} 返回一个 Promise 对象。
	 * @public
	 * @example
	 * let resource = new THING.BaseResource();
	 * resource.waitForComplete().then((resource) => {
	 *   console.log('Resource loaded');
	 * });
	 */
	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;
	}

	/**
	 * 获取唯一标识符。
	 * @type {string}
	 * @public
	 */
	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

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

		return _private.app;
	}

	/**
	 * 检查类类型。
	 * @type {boolean}
	 * @public
	 */
	get isBaseResource() {
		return true;
	}

	// #endregion

}

export { BaseResource }