export class ResourceMgr {
	public static readonly Instance: ResourceMgr = new ResourceMgr();

	/**脏标记字段 */
	private static readonly _inPoolMark: string = "__IsInPool__";

	/**标识 */
	private static readonly _signMark: string = "__sign__";

	/**已经加载好了的资源id */
	private _assetLoadedSet: Set<string> = new Set();

	/**{@link Core.GameObject}对象池 */
	private _pool: Map<string, Core.GameObject[]> = new Map();

	private constructor() {}

	/**
	 * 添加多个资源
	 * @param assetGuid
	 */
	public addAssets(assetGuid: string[]): Promise<{ successCount: number; failCount: number }> {
		return new Promise(async (res) => {
			const count = assetGuid.length;
			const value = { successCount: 0, failCount: 0 };
			for (const guid of assetGuid) {
				this.addAsset(guid).then((success) => {
					if (success) value.successCount++;
					else value.failCount++;
					if (value.successCount + value.failCount >= count) return res(value);
				});
			}
		});
	}

	/**
	 * 添加单个资源
	 * @param assetGuid
	 * @returns
	 */
	public async addAsset(assetGuid: string) {
		if (!assetGuid || assetGuid.length >= 8 || isNaN(Number(assetGuid))) {
			logW(`资源id不正确----------> [> ${assetGuid} <]`);
			return false;
		}
		const asset = this._assetLoadedSet;
		if (asset.has(assetGuid)) {
			return true;
		}
		if (AssetUtil.assetLoaded(assetGuid)) {
			asset.add(assetGuid);
			return true;
		}
		const success = await AssetUtil.asyncDownloadAsset(assetGuid);
		if (success) {
			asset.add(assetGuid);
		}
		return success;
	}

	/**
	 * 获得一个资源
	 * @param guid 资源的guid或者模型的guid或者prefab的guid
	 * @param replicates
	 * @returns
	 */
	public getResource(guid: string, replicates?: boolean) {
		const len = guid.length;
		if (len < 8) {
			return this.getGameObjectByAsset(guid, replicates);
		}
		if (len < 32) {
			return this.getGameObjectByGuid(guid);
		}
		return this.getPrefabByGuid(guid, replicates);
	}

	/**
	 * 通过资源id得到对应的{@link Core.GameObject}对象
	 * @param guid 资源id
	 * @param replicates 是否同步
	 * @returns
	 */
	public getGameObjectByAsset<T extends Core.GameObject>(guid: string, replicates?: boolean) {
		if (!this._assetLoadedSet.has(guid)) {
			this.addAsset(guid);
		}
		const pool = this.getPool(guid);
		let item: Core.GameObject = null;
		if (pool.length > 0) {
			item = this.getGameObject(pool);
		} else {
			item = Core.GameObject.spawn({ guid, replicates });
			pool.push(item);
		}
		item[ResourceMgr._inPoolMark] = false;
		return item as T;
	}

	/**
	 * 获得一个prefab
	 * @param guid prefab的guid
	 * @param replicates 是否同步
	 * @returns
	 */
	public getPrefabByGuid<T extends Core.GameObject>(guid: string, replicates?: boolean) {
		const pool = this.getPool(guid);
		let item: Core.GameObject = null;
		if (pool.length > 0) {
			item = this.getGameObject(pool);
		} else {
			item = Core.GameObject.spawn({ guid, replicates });
			pool.push(item);
		}
		item[ResourceMgr._inPoolMark] = false;
		item[ResourceMgr._signMark] = guid;
		return item as T;
	}

	/**
	 * 通过对象模型的guid获得对象
	 * @param guid 模型guid
	 * @returns
	 */
	public getGameObjectByGuid<T extends Core.GameObject>(guid: string) {
		const pool = this.getPool(guid);
		let item: Core.GameObject = null;
		if (pool.length > 0) {
			item = this.getGameObject(pool);
		} else {
			item = Core.GameObject.find(guid);
			pool.push(item);
		}
		item[ResourceMgr._inPoolMark] = false;
		item[ResourceMgr._signMark] = guid;
		return item as T;
	}

	/**
	 * 通过资源id获得对应得对象池
	 * @param guid 资源id
	 * @returns
	 */
	private getPool(guid: string) {
		if (this._pool.has(guid)) {
			return this._pool.get(guid);
		}
		const pool: Core.GameObject[] = [];
		this._pool.set(guid, pool);
		return pool;
	}

	/**
	 * 获得一个对象
	 * @param pool 从哪个对象池里获得
	 * @returns
	 */
	private getGameObject(pool: Core.GameObject[]) {
		let item = pool.find((v) => v[ResourceMgr._inPoolMark]);
		if (!item) {
			item = pool[0].clone();
			pool.push(item);
		}
		return item;
	}

	/**
	 * 回收一个对象
	 * @param go
	 * @param force 是否强制回收，false: 当{@link go}带有{@link ResourceMgr._inPoolMark}时才会被回收进pool里， default: false
	 */
	public recover(go: Core.GameObject, force: boolean = false) {
		if (Object.prototype.hasOwnProperty(ResourceMgr._inPoolMark) || force) {
			let guid: string;
			if (Object.prototype.hasOwnProperty(ResourceMgr._signMark)) {
				guid = go[ResourceMgr._signMark];
			}
			if (!guid) {
				guid = go.getSourceAssetGuid();
			}
			this.getPool(guid).push(go);
			go[ResourceMgr._inPoolMark] = true;
		}
	}
}
