/*:
 * @plugindesc カスタマイズ可能なゲージウィンドウを作成するプラグイン（完全版・修正済み）
 * @author えむうん
 * @target MZ
 * @help
 * このプラグインは、カスタマイズ可能なゲージウィンドウを作成します。
 * マップシーンとバトルシーンの両方で使用可能です。
 * 
 * 機能:
 * - カスタマイズ可能なゲージウィンドウの作成
 * - 変数の増減時にSEを再生
 * - 変数の増減時にエフェクトアニメを表示（ゲージのサイズに関わらず全体表示）
 * - SEとエフェクトは増減時で別々に設定可能
 * - マップシーンとバトルシーンの両方で使用可能
 * - ゲージの方向を自由に設定可能（右、左、上、下）
 * - ウィンドウ背景とコンテンツの透明度を別々に設定可能
 * 
 * プラグインコマンド:
 *   CreateGaugeWindow [ID] [X] [Y] [Width] [Height] [WindowOpacity] [ContentsOpacity] [ImageFront] [ImageBack] [Variable] [MaxValue] [Label] [ShowValue] [Direction] [IncreaseSE] [DecreaseSE] [IncreaseAnimation] [DecreaseAnimation]
 *   ShowGaugeWindow [ID]
 *   HideGaugeWindow [ID]
 * 
 * [Direction] には以下の値を使用できます:
 * right, left, up, down
 * 
 * @command CreateGaugeWindow
 * @text ゲージウィンドウ作成
 * @desc 新しいゲージウィンドウを作成します。
 * 
 * @arg ID
 * @type number
 * @text ウィンドウID
 * @desc ウィンドウを識別するための一意のID
 * 
 * @arg X
 * @type number
 * @text X座標
 * @desc ウィンドウのX座標
 * 
 * @arg Y
 * @type number
 * @text Y座標
 * @desc ウィンドウのY座標
 * 
 * @arg Width
 * @type number
 * @text 幅
 * @desc ウィンドウの幅
 * 
 * @arg Height
 * @type number
 * @text 高さ
 * @desc ウィンドウの高さ
 * 
 * @arg WindowOpacity
 * @type number
 * @text ウィンドウ背景の透明度
 * @desc ウィンドウ背景の透明度 (0-255)
 * @default 255
 * 
 * @arg ContentsOpacity
 * @type number
 * @text コンテンツの透明度
 * @desc ウィンドウコンテンツ（ゲージ、テキストなど）の透明度 (0-255)
 * @default 255
 * 
 * @arg ImageFront
 * @type file
 * @dir img/system
 * @text 前面画像
 * @desc ゲージの前面画像
 * 
 * @arg ImageBack
 * @type file
 * @dir img/system
 * @text 背面画像
 * @desc ゲージの背面画像
 * 
 * @arg Variable
 * @type variable
 * @text 変数
 * @desc 表示する変数のID
 * 
 * @arg MaxValue
 * @type number
 * @text 最大値
 * @desc 変数の最大値
 * 
 * @arg Label
 * @type string
 * @text ラベル
 * @desc ウィンドウに表示するラベル
 * 
 * @arg ShowValue
 * @type boolean
 * @text 数値表示
 * @desc 数値を表示するかどうか
 * 
 * @arg Direction
 * @type select
 * @option 右
 * @value right
 * @option 左
 * @value left
 * @option 上
 * @value up
 * @option 下
 * @value down
 * @text ゲージ方向
 * @desc ゲージの増加方向
 * 
 * @arg IncreaseSE
 * @type file
 * @dir audio/se
 * @text 増加時SE
 * @desc 変数が増加したときに再生するSE
 * 
 * @arg DecreaseSE
 * @type file
 * @dir audio/se
 * @text 減少時SE
 * @desc 変数が減少したときに再生するSE
 * 
 * @arg IncreaseAnimation
 * @type animation
 * @text 増加時アニメーション
 * @desc 変数が増加したときに表示するアニメーション
 * 
 * @arg DecreaseAnimation
 * @type animation
 * @text 減少時アニメーション
 * @desc 変数が減少したときに表示するアニメーション
 * 
 * @command ShowGaugeWindow
 * @text ゲージウィンドウ表示
 * @desc 指定したIDのゲージウィンドウを表示します。
 * 
 * @arg ID
 * @type number
 * @text ウィンドウID
 * @desc 表示するウィンドウのID
 * 
 * @command HideGaugeWindow
 * @text ゲージウィンドウ非表示
 * @desc 指定したIDのゲージウィンドウを非表示にします。
 * 
 * @arg ID
 * @type number
 * @text ウィンドウID
 * @desc 非表示にするウィンドウのID
 */

(() => {
    const pluginName = "CustomGaugeWindow";

    class GaugeWindow extends Window_Base {
        constructor(id, x, y, width, height) {
            super(new Rectangle(x, y, width, height));
            this._gaugeId = id;
            this._value = 0;
            this._maxValue = 100;
            this._label = "";
            this._showValue = true;
            this._imageFront = null;
            this._imageBack = null;
            this._direction = 'right';
            this._needsRefresh = true;
            this._increaseSE = null;
            this._decreaseSE = null;
            this._increaseAnimation = null;
            this._decreaseAnimation = null;
            this.opacity = 255;  // デフォルトで不透明
            this.contentsOpacity = 255;  // デフォルトで不透明
            this.hide();
        }

        setProperties(windowOpacity, contentsOpacity, imageFront, imageBack, variableId, maxValue, label, showValue, direction, increaseSE, decreaseSE, increaseAnimation, decreaseAnimation) {
            this.opacity = windowOpacity;
            this.contentsOpacity = contentsOpacity;
            this._imageFront = ImageManager.loadSystem(imageFront);
            this._imageBack = ImageManager.loadSystem(imageBack);
            this._variableId = variableId;
            this._maxValue = maxValue;
            this._label = label;
            this._showValue = showValue;
            this._direction = direction;
            this._increaseSE = increaseSE;
            this._decreaseSE = decreaseSE;
            this._increaseAnimation = increaseAnimation;
            this._decreaseAnimation = decreaseAnimation;
            this._needsRefresh = true;
        }

        update() {
            super.update();
            if (this.visible) {
                const newValue = $gameVariables.value(this._variableId);
                if (this._value !== newValue) {
                    this.updateValueChange(newValue);
                    this._value = newValue;
                    this._needsRefresh = true;
                }
                if (this._needsRefresh) {
                    this.refresh();
                    this._needsRefresh = false;
                }
            }
        }

        updateValueChange(newValue) {
            const change = newValue - this._value;
            if (change > 0) {
                this.playIncreaseSE();
                this.showIncreaseAnimation();
            } else if (change < 0) {
                this.playDecreaseSE();
                this.showDecreaseAnimation();
            }
        }

        playIncreaseSE() {
            if (this._increaseSE) {
                AudioManager.playSe({name: this._increaseSE, pan: 0, pitch: 100, volume: 100});
            }
        }

        playDecreaseSE() {
            if (this._decreaseSE) {
                AudioManager.playSe({name: this._decreaseSE, pan: 0, pitch: 100, volume: 100});
            }
        }

        showIncreaseAnimation() {
            if (this._increaseAnimation) {
                this.showAnimation($dataAnimations[this._increaseAnimation]);
            }
        }

        showDecreaseAnimation() {
            if (this._decreaseAnimation) {
                this.showAnimation($dataAnimations[this._decreaseAnimation]);
            }
        }

        showAnimation(animation) {
            if (SceneManager._scene.createGaugeAnimation) {
                SceneManager._scene.createGaugeAnimation(this, animation);
            }
        }

        refresh() {
            this.contents.clear();
            this.contents.paintOpacity = this.contentsOpacity;  // コンテンツの透明度を設定
            this.drawGauge();
            this.drawLabel();
            if (this._showValue) {
                this.drawValue();
            }
            this.contents.paintOpacity = 255;  // 透明度をリセット
        }

        drawGauge() {
            if (!this._imageBack || !this._imageFront || !this._imageBack.isReady() || !this._imageFront.isReady()) return;

            const gaugeRect = this.gaugeRect();
            const aspect = this._imageBack.width / this._imageBack.height;
            let drawWidth, drawHeight;

            if (this._direction === 'right' || this._direction === 'left') {
                drawHeight = gaugeRect.height;
                drawWidth = drawHeight * aspect;
            } else {
                drawWidth = gaugeRect.width;
                drawHeight = drawWidth / aspect;
            }

            const x = gaugeRect.x + (gaugeRect.width - drawWidth) / 2;
            const y = gaugeRect.y + (gaugeRect.height - drawHeight) / 2;

            this.contents.blt(this._imageBack, 0, 0, this._imageBack.width, this._imageBack.height, x, y, drawWidth, drawHeight);

            const fillPercentage = Math.min(Math.max(this._value / this._maxValue, 0), 1);
            let fillRect = this.fillRect(new Rectangle(x, y, drawWidth, drawHeight), fillPercentage);

            const sourceRect = this.getSourceRect(fillPercentage);
            this.contents.blt(
                this._imageFront,
                sourceRect.x, sourceRect.y, sourceRect.width, sourceRect.height,
                fillRect.x, fillRect.y, fillRect.width, fillRect.height
            );
        }

        getSourceRect(fillPercentage) {
            const w = this._imageFront.width;
            const h = this._imageFront.height;
            switch (this._direction) {
                case 'right':
                    return new Rectangle(0, 0, w * fillPercentage, h);
                case 'left':
                    return new Rectangle(w * (1 - fillPercentage), 0, w * fillPercentage, h);
                case 'up':
                    return new Rectangle(0, h * (1 - fillPercentage), w, h * fillPercentage);
                case 'down':
                    return new Rectangle(0, 0, w, h * fillPercentage);
            }
        }

        gaugeRect() {
            const margin = 4;
            return new Rectangle(
                margin,
                this.lineHeight(),
                this.contents.width - margin * 2,
                this.contents.height - this.lineHeight() * 2
            );
        }

        fillRect(gaugeRect, fillPercentage) {
            switch (this._direction) {
                case 'right':
                    return new Rectangle(
                        gaugeRect.x,
                        gaugeRect.y,
                        gaugeRect.width * fillPercentage,
                        gaugeRect.height
                    );
                case 'left':
                    return new Rectangle(
                        gaugeRect.x + gaugeRect.width * (1 - fillPercentage),
                        gaugeRect.y,
                        gaugeRect.width * fillPercentage,
                        gaugeRect.height
                    );
                case 'up':
                    return new Rectangle(
                        gaugeRect.x,
                        gaugeRect.y + gaugeRect.height * (1 - fillPercentage),
                        gaugeRect.width,
                        gaugeRect.height * fillPercentage
                    );
                case 'down':
                    return new Rectangle(
                        gaugeRect.x,
                        gaugeRect.y,
                        gaugeRect.width,
                        gaugeRect.height * fillPercentage
                    );
            }
        }

        drawLabel() {
            this.drawText(this._label, 0, 0, this.contents.width, 'left');
        }

        drawValue() {
            this.drawText(this._value + "/" + this._maxValue, 0, this.contents.height - this.lineHeight(), this.contents.width, 'right');
        }

        show() {
            super.show();
            this._needsRefresh = true;
        }

        createAnimationTarget() {
            const sprite = new Sprite();
            sprite.x = this.width / 2;
            sprite.y = this.height / 2;
            sprite._animationTarget = true;  // アニメーションターゲットであることを示すフラグ
            
            sprite.setBlendColor = function(color) {
                this._blendColor = color.clone();
            };
            
            sprite.getBlendColor = function() {
                return this._blendColor ? this._blendColor.clone() : [0, 0, 0, 0];
            };
            
            this.addChild(sprite);
            return sprite;
        }
    }

    const _Scene_Base_start = Scene_Base.prototype.start;
    Scene_Base.prototype.start = function() {
        _Scene_Base_start.call(this);
        if (!this._gaugeWindows) {
            this._gaugeWindows = {};
        }
        if (!this._gaugeWindowLayer) {
            this.createGaugeWindows();
        }
    };

    Scene_Base.prototype.createGaugeWindows = function() {
        this._gaugeWindowLayer = new WindowLayer();
        this._gaugeWindowLayer.x = 0;
        this._gaugeWindowLayer.y = 0;
        this.addChild(this._gaugeWindowLayer);
    };

    Scene_Base.prototype.addGaugeWindow = function(window) {
        this._gaugeWindowLayer.addChild(window);
        this._gaugeWindows[window._gaugeId] = window;
    };

    Scene_Base.prototype.refreshGaugeWindows = function(variableId) {
        for (const id in this._gaugeWindows) {
            const window = this._gaugeWindows[id];
            if (window._variableId === variableId) {
                window._needsRefresh = true;
            }
        }
    };

    Scene_Base.prototype.createGaugeAnimation = function(gaugeWindow, animation) {
        const target = gaugeWindow.createAnimationTarget();
        const sprite = new Sprite_Animation();
        sprite.setup([target], animation, false, 0);
        sprite.x = gaugeWindow.x;
        sprite.y = gaugeWindow.y;
        this.addChild(sprite);
        this._gaugeAnimationSprites = this._gaugeAnimationSprites || [];
        this._gaugeAnimationSprites.push(sprite);
    };

    const _Scene_Base_update = Scene_Base.prototype.update;
    Scene_Base.prototype.update = function() {
        _Scene_Base_update.call(this);
        this.updateGaugeAnimations();
    };

    Scene_Base.prototype.updateGaugeAnimations = function() {
        if (this._gaugeAnimationSprites) {
            this._gaugeAnimationSprites = this._gaugeAnimationSprites.filter(sprite => {
                if (sprite.isPlaying()) {
                    sprite.update();
                    return true;
                } else {
                    this.removeChild(sprite);
                    if (sprite._targets && sprite._targets[0]) {
                        const target = sprite._targets[0];
                        if (target._animationTarget) {
                            target.parent.removeChild(target);
                        }
                    }
                    return false;
                }
            });
        }
    };

    const _Scene_Map_createAllWindows = Scene_Map.prototype.createAllWindows;
    Scene_Map.prototype.createAllWindows = function() {
        _Scene_Map_createAllWindows.call(this);
        this.createGaugeWindows();
    };

    const _Scene_Battle_createAllWindows = Scene_Battle.prototype.createAllWindows;
    Scene_Battle.prototype.createAllWindows = function() {
        _Scene_Battle_createAllWindows.call(this);
        this.createGaugeWindows();
    };

    const _Game_Variables_setValue = Game_Variables.prototype.setValue;
    Game_Variables.prototype.setValue = function(variableId, value) {
        const oldValue = this.value(variableId);
        _Game_Variables_setValue.call(this, variableId, value);
        if (oldValue !== this.value(variableId)) {
            this.onVariableChange(variableId);
        }
    };

    Game_Variables.prototype.onVariableChange = function(variableId) {
        if (SceneManager._scene.refreshGaugeWindows) {
            SceneManager._scene.refreshGaugeWindows(variableId);
        }
    };

    PluginManager.registerCommand(pluginName, "CreateGaugeWindow", args => {
        const scene = SceneManager._scene;
        if (scene.addGaugeWindow) {
            const id = Number(args.ID);
            const window = new GaugeWindow(id, Number(args.X), Number(args.Y), Number(args.Width), Number(args.Height));
            window.setProperties(
                Number(args.WindowOpacity),
                Number(args.ContentsOpacity),
                args.ImageFront,
                args.ImageBack,
                Number(args.Variable),
                Number(args.MaxValue),
                args.Label,
                args.ShowValue === "true",
                args.Direction,
                args.IncreaseSE,
                args.DecreaseSE,
                Number(args.IncreaseAnimation),
                Number(args.DecreaseAnimation)
            );
            scene.addGaugeWindow(window);
        }
    });

    PluginManager.registerCommand(pluginName, "ShowGaugeWindow", args => {
        const scene = SceneManager._scene;
        if (scene._gaugeWindows) {
            const id = Number(args.ID);
            if (scene._gaugeWindows[id]) {
                scene._gaugeWindows[id].show();
            }
        }
    });

    PluginManager.registerCommand(pluginName, "HideGaugeWindow", args => {
        const scene = SceneManager._scene;
        if (scene._gaugeWindows) {
            const id = Number(args.ID);
            if (scene._gaugeWindows[id]) {
                scene._gaugeWindows[id].hide();
            }
        }
    });
})();