import AssetsBundleMgr from "../../utils/AssetsBundleMgr";
import { EResType } from "./ResBaseAsset";
import { ResCollector } from "./ResCollector";


const { ccclass } = cc._decorator;

declare global {
    /**
     * !API
     */
    interface IResKeeper {
        // 组件销毁时自动释放某个资源
        AutoReleaseAsset<T extends cc.Asset>(asset: T, url: string): void;

        // 加载资源
        LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise<void>;

    }
}

/**
 * 销毁时自动释放资源组件
 */
// @ccclass("ResKeeper")
@ccclass
export class ResKeeper extends cc.Component implements IResKeeper {
    /** 是否调试状态 */
    static DEBUG: boolean = CC_PREVIEW ? true : false;
    private _i_LoadedAssets: Array<AssetKeepItem> = undefined;

    /** 已加载资源列表 */
    private get m_LoadedAssets(): Array<AssetKeepItem> {
        if (!this._i_LoadedAssets) {
            this._i_LoadedAssets = [];
        }
        return this._i_LoadedAssets;
    }
    /**
     * 从目标节点或其父节点递归查找一个资源挂载（销毁时自动释放）组件
     * @param attachNode 目标节点
     * @param autoCreate 当目标节点找不到 ResKeeper 时是否自动创建一个
     */
    public static Get(attachNode: cc.Node, autoCreate: boolean = false): IResKeeper {
        if (attachNode && cc.isValid(attachNode)) {
            let ret = attachNode.getComponent(ResKeeper);
            if (!ret) {
                if (autoCreate) {
                    return attachNode.addComponent(ResKeeper);
                } else {
                    if (!attachNode.parent) {
                        console.error(`ResKeeper >> attachNode 的父节点不存在 >> attachNode.name = ${attachNode.name}`);
                        return null;
                    }
                    return ResKeeper.Get(attachNode.parent, autoCreate);
                }
            }
            return ret;
        }
        console.error(`ResKeeper >> attachNode 为空节点`);
        return null;
    }


    /**
     * 加载自动释放的资源
     * @param resCollector 
     */
    async LoadResCollector(resCollector: ResCollector, promise: IPromiseFunc): Promise<void> {
        const that = this;
        let __bindAutoReleaseAssetToComp = <T extends cc.Asset>(url: string, res: T) => {
            // 性能调优： 调试状态才打印加载的 url
            // ResKeeper.DEBUG && console.log(`url: ${url} 加载成功`)
            if (cc.isValid(that)) {
                that.AutoReleaseAsset(res, url);
            } else {
                console.warn("资源加载完毕时，节点已不可用")
            }
        }
        let _onResLoaded = (url: string, err, res, resolve) => {
            if (err) {
                console.error(err);
                /** 释放加载项 */
                resolve();
                return;
            }
            __bindAutoReleaseAssetToComp(url, res);
            resolve();
        }
        let proArr: Promise<cc.Asset>[] = [];
        resCollector.GetResList().forEach(loadAssetItem => {
            proArr.push(new Promise((resolve, reject) => {
                switch (loadAssetItem.typ) {
                    case EResType.Prefab: {
                        const bundleUrl = loadAssetItem.data;
                        AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
                            if (err) {
                                console.log("SpineNode:bundle load failed:", err)
                                return
                            }
                            // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab);
                            // if (cacheAsset) {
                            //     _onResLoaded(bundleUrl.url, err, cacheAsset, resolve);
                            // } else {
                            bundle.load(bundleUrl.url, cc.Prefab, (err: Error, asset: cc.Prefab) => {
                                _onResLoaded(bundleUrl.url, err, asset, resolve);
                            })
                            // }
                        })
                    } break;
                    case EResType.Spine: {
                        const bundleUrl = loadAssetItem.data;
                        AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
                            if (err) {
                                console.log("SpineNode:bundle load failed:", err)
                                return
                            }
                            // let cacheAsset = bundle.get(bundleUrl.url, cc.Prefab);
                            // if (cacheAsset) {
                            //     _onResLoaded(bundleUrl.url, err, cacheAsset, resolve);
                            // } else {
                            bundle.load(bundleUrl.url, sp.SkeletonData, (err: Error, asset: sp.SkeletonData) => {
                                _onResLoaded(bundleUrl.url, err, asset, resolve);
                            })
                            // }

                        })
                    } break;
                    case EResType.SpriteFrame: {
                        const bundleUrl = loadAssetItem.data;
                        AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
                            if (err) {
                                console.log("SpriteFrame:bundle load failed:", err)
                                return
                            }
                            bundle.load(bundleUrl.url, cc.SpriteFrame, (err: Error, asset: cc.SpriteFrame) => {
                                _onResLoaded(bundleUrl.url, err, asset, resolve);
                            })
                        })
                    } break;
                    case EResType.Json: {
                        const bundleUrl = loadAssetItem.data;
                        AssetsBundleMgr.loadBundle(bundleUrl.bundle, (err, bundle) => {
                            if (err) {
                                console.log("SpriteFrame:bundle load failed:", err)
                                return
                            }
                            bundle.load(bundleUrl.url, cc.JsonAsset, (err: Error, asset: cc.JsonAsset) => {
                                _onResLoaded(bundleUrl.url, err, asset, resolve);
                            })
                        })
                    } break;
                    default: {
                        console.error(`【ResKeeper】 >> 未处理的资源加载类型 ${loadAssetItem.typ}`)
                    } break;
                }
            }))

        });
        return promise(async (resolve, reject) => {
            await Promise.all(proArr).then((values) => {
                resolve();
            });
        });
    }



    /**
     * 添加自动释放引用
     * @param keepItem 
     */
    AutoReleaseAsset<T extends cc.Asset>(asset: T, url: string): void {
        /** Borrow 时自动添加了引用计数，销毁时，解引用即可 */
        this.m_LoadedAssets.push(new AssetKeepItem(asset, url));
    }

    /**
     * 释放所有引用的资源
     */
    onDestroy() {
        if (this._i_LoadedAssets) {
            this._i_LoadedAssets.forEach(it => {
                if (it && it.DecRef) {
                    it.DecRef()
                }
            });
            this._i_LoadedAssets = undefined;
        }
    }

}

class AssetKeepItem {
    // --------------------------------
    mAsset: cc.Asset;
    mUrl: string;

    constructor(asset: cc.Asset, url: string) {
        this.mAsset = asset;
        this.mUrl = url;
        this.AddRef();
    }

    public AddRef(): void {
        this.mAsset.addRef();
    }

    public DecRef(): void {
        this.mAsset.decRef();
    }

}
