//=============================================================================
// RPG Maker MZ - AsyncLoadImage
//
// Copyright 2024 Panzer-IV. All rights reserved.
// This source code or any portion thereof must not be
// reproduced or used without licensed in any manner whatsoever.
//=============================================================================
/*:
 * @target MZ
 * @plugindesc 画像の非同期読み込みプラグイン
 * @author 四号戦車
 * @base PluginCommonBase
 * @orderAfter PluginCommonBase
 *
 * @help AsyncLoadImage.js
 *
 * Version: 0.0.1
 *
 * ImageManagerのBitmapローディングは非同期なので困る場面があるため、
 * コールバック地獄回避のためにPromiseで返す処理を追加するプラグイン
 *
 */
(() => {
	"use strict";

    const pluginName = "AsyncLoadImage";
    const script = document.currentScript;
    const param  = PluginManagerEx.createParameter(script);

    //========================================================================
    // モジュールインポート
    //========================================================================
    const fs = require('fs');

    /********************************************************************************************************
     * 共有基盤
     ********************************************************************************************************/
    const log = (text) => {
        if (DebugSwitch.isEnable()) {
            console.log(text);
        }
    };

    const fileExists = (path) => {
        try {
            const realPath = Utils.hasEncryptedImages() ? `${path}_` : path;
            fs.accessSync(realPath, fs.constants.R_OK);
            return true;
        } catch (err) {
            Logger.w(`cannot access: path = ${path}, err = ${err}`);
            return false;
        }
    };

    /********************************************************************************************************
     * ImageManagerへのfunction追加
     ********************************************************************************************************/
    const _Bitmap_prototype_initialize = Bitmap.prototype.initialize;
    Bitmap.prototype.initialize = function(width, height) {
        _Bitmap_prototype_initialize.apply(this, arguments);
        this._errorListeners = [];
    }

    Bitmap.prototype.addErrorListener = function(listner) {
        if (this._loadingState !== "error") {
            this._errorListeners.push(listner);
        } else {
            listner(this);
        }
    };

    const _Bitmap_prototype__onError = Bitmap.prototype._onError;
    Bitmap.prototype._onError = function() {
        _Bitmap_prototype__onError.apply(this, arguments);
        while (this._errorListeners.length > 0) {
            const listener = this._errorListeners.shift();
            listener(this);
        }
    }

    /********************************************************************************************************
     * ImageManagerへのfunction追加
     ********************************************************************************************************/
    ImageManager.loadAnimationAsync = function(filename) {
        return this.loadBitmapAsync("img/animations/", filename);
    };

    ImageManager.loadBattleback1Async = function(filename) {
        return this.loadBitmapAsync("img/battlebacks1/", filename);
    };

    ImageManager.loadBattleback2Async = function(filename) {
        return this.loadBitmapAsync("img/battlebacks2/", filename);
    };

    ImageManager.loadEnemyAsync = function(filename) {
        return this.loadBitmapAsync("img/enemies/", filename);
    };

    ImageManager.loadCharacterAsync = function(filename) {
        return this.loadBitmapAsync("img/characters/", filename);
    };

    ImageManager.loadFaceAsync = function(filename) {
        return this.loadBitmapAsync("img/faces/", filename);
    };

    ImageManager.loadParallaxAsync = function(filename) {
        return this.loadBitmapAsync("img/parallaxes/", filename);
    };

    ImageManager.loadPictureAsync = function(filename) {
        return this.loadBitmapAsync("img/pictures/", filename);
    };

    ImageManager.loadSvActorAsync = function(filename) {
        return this.loadBitmapAsync("img/sv_actors/", filename);
    };

    ImageManager.loadSvEnemyAsync = function(filename) {
        return this.loadBitmapAsync("img/sv_enemies/", filename);
    };

    ImageManager.loadSystemAsync = function(filename) {
        return this.loadBitmapAsync("img/system/", filename);
    };

    ImageManager.loadTilesetAsync = function(filename) {
        return this.loadBitmapAsync("img/tilesets/", filename);
    };

    ImageManager.loadTitle1Async = function(filename) {
        return this.loadBitmapAsync("img/titles1/", filename);
    };

    ImageManager.loadTitle2Async = function(filename) {
        return this.loadBitmapAsync("img/titles2/", filename);
    };

    ImageManager.loadBitmapAsync = function(folder, filename) {
        if (!!filename) {
            const url = folder + Utils.encodeURI(filename) + ".png";
            return this.loadBitmapFromUrlAsync(url);
        } else {
            return Promise.resolve(this._emptyBitmap);
        }
    }

    ImageManager.loadBitmapFromUrlAsync = function(url) {
        return new Promise((resolve, reject) => {
            const cache = url.includes("/system/") ? this._system : this._cache;
            if (!cache[url] || !cache[url]._baseTexture) {
                if (fileExists(decodeURIComponent(url))) {
                    const bitmap = Bitmap.load(url);
                    cache[url] = bitmap;
                    bitmap.addLoadListener(() => {
                        resolve(bitmap);
                    });
                    bitmap.addErrorListener(() => reject(bitmap));
                } else {
                    reject(`file does not exists, url = ${decodeURIComponent(url)}`);
                }
            } else {
                resolve(cache[url]);
            }
        });
    };

})();
