//=============================================================================
// RPG Maker MZ - Sprite_InputHandle
//
// 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 DebugSwitch
 * @base AsyncLoadImage
 * @base SmartAnimation
 * @orderAfter DebugSwitch
 * @orderAfter AsyncLoadImage
 * @orderAfter SmartAnimation
 *
 * @param comment
 * @text 備考欄
 * @desc プラグイン側では使用しません。
 *       メモ欄としてお使いください。
 * @type multiline_string
 * @default
 *
 * @help Sprite_InputHandle.js
 *
 * Version: 0.0.1
 *
 * 内部のDisplayObjectの展開領域に関わらず固定の表示領域にクロップするSpriteです。
 */
(() => {
	"use strict";

    //========================================================================
    // プラグイン定義
    //========================================================================
    const pluginName = "Sprite_InputHandle";
    const script = document.currentScript;
    const param  = PluginManagerEx.createParameter(script);

    //========================================================================
    // 定数定義
    //========================================================================

    //========================================================================
    // クラス定義 (Sprite_InputHandle)
    //========================================================================
    class Sprite_InputHandle extends Sprite {
        constructor(bitmap) {
            super(bitmap);
            this._enterListeners = [];
            this._hoverListeners = [];

            this._pressed = false;
            this._hovered = false;
            this._isTouchEnabled = true;
            this._isKeepEntryUntilDragging = false;
            this._excludeTransparentPixel = false;
            this._lastSnappedChildren = 0;
            this._snap = null;
        }

        onHandleEnter() {
            if (this.isTouchEnabled()) {
                this._fireEnter(this._name);
            }
        }

        onPressed(pressed) {
        }

        onPointerEntry(entry) {
            if (this.isTouchEnabled()) {
                this._fireHover(this._name, entry);
            }
        }

        addEnterListener(listener) {
            if (!!listener && typeof listener === 'function') {
                this._enterListeners.push(listener);
            }
        }

        removeEnterListener(listener) {
            this._enterListeners = this._enterListeners.filter(l => l !== listener);
        }

        addHoverListener(listener) {
            if (!!listener && typeof listener === 'function') {
                this._hoverListeners.push(listener);
            }
        }

        removeHoverListener(listener) {
            this._hoverListeners = this._hoverListeners.filter(l => l !== listener);
        }

        update() {
            super.update();
            this.updateSnap();
            this.processTouch();
        }

        isTouchEnabled() {
            return this.worldVisible && this.isTouchEnable();
        }

        enableTouch(value) {
            this._isTouchEnabled = value;
        }

        isTouchEnable() {
            return this._isTouchEnabled;
        }

        isKeepEntryUntilDragging() {
            return this._isKeepEntryUntilDragging;
        }

        isPressed() {
            return this._pressed;
        }

        isHovered() {
            return this._hovered;
        }

        isBeingTouched() {
            const touchPos = new Point(TouchInput.x, TouchInput.y);
            const localPos = this.worldTransform.applyInverse(touchPos);
            return this.hitTest(localPos.x, localPos.y);
        }

        createSnap() {
            const width = this.width;
            const height = this.height;
            const bitmap = new Bitmap(width, height);
            const renderTexture = PIXI.RenderTexture.create(width, height);

            const renderer = Graphics.app.renderer;
            renderer.render(this, renderTexture);
            this.worldTransform.identity();
            const canvas = renderer.extract.canvas(renderTexture);
            bitmap.context.drawImage(canvas, 0, 0);
            canvas.width = 0;
            canvas.height = 0;

            renderTexture.destroy({ destroyBase: true });
            bitmap.baseTexture.update();

            this._lastSnappedChildren = this.children.length;

            return bitmap;
        }

        getSnap() {
            if (!this._snap || (this.children.length !== this._lastSnappedChildren)) {
                this._snap = this.createSnap();
            }
            return this._snap;
        }

        updateSnap() {
            if (this._excludeTransparentPixel) {
                const childrenSize = this.children.length;
                if (childrenSize !== this._lastSnappedChildren) {
                    this._snap = this.createSnap();
                }
            }
        }

        hitTest(x, y) {
            const rect = new Rectangle(
                -this.anchor.x * this.width,
                -this.anchor.y * this.height,
                this.width,
                this.height
            );

            if (this._excludeTransparentPixel === true) {
                if (rect.contains(x, y)) {
                    const snap = this.getSnap();
                    const alpha = snap.getAlphaPixel(x, y);
                    snap.destroy();
                    return alpha > 0;
                } else {
                    return false;
                }
            } else {
                return rect.contains(x, y);
            }
        }

        isIncludeOnParentFrame() {
            if (this.worldVisible === false) {
                return false;
            }

            let item = this.parent;
            while (!!item) {
                if (item instanceof Sprite_Scrollable) {
                    if (!item.isTouchedInsideFrame()) {
                        return false;
                    }
                }
                item = item.parent;
            }
            return true;
        }

        processTouch() {
            if (this.isTouchEnabled()) {
                if (this.isBeingTouched() || (this.isKeepEntryUntilDragging() && this.isPressed())) {
                    if (!this._hovered && TouchInput.isHovered()) {
                        this._hovered = true;
                        this.onPointerEntry(true);
                    }
                    if (TouchInput.isTriggered()) {
                        this.onPressed(true);
                        this._pressed = true;
                    }
                } else {
                    if (this._hovered) {
                        this.onPointerEntry(false);
                    }
                    this._pressed = false;
                    this._hovered = false;
                }
                if (this._pressed && TouchInput.isReleased() && this.isIncludeOnParentFrame()) {
                    this.onHandleEnter();
                    this.onPressed(false);
                    this._pressed = false;
                }
            } else {
                if (this._pressed) {
                    this.onPressed(false);
                    this._pressed = false;
                }

                if (this._hovered) {
                    this.onPointerEntry(false);
                    this._hovered = false;
                }
            }
        }

        _checkTouchInput() {
            const point = this.toLocal(new Point(TouchInput.x, TouchInput.y));
            const bounds = this.getLocalBounds();
            return bounds.contains(point.x, point.y);
        }

        _fireEnter() {
            this._enterListeners.forEach(listener => {
                listener(this);
            })
        }

        _fireHover(inEntry) {
            this._hoverListeners.forEach(listener => {
                listener(this, inEntry);
            })
        }

        enableTransparent(value) {
            this._excludeTransparentPixel = !value;
        }

        canTouchTransparent() {
            return !this._excludeTransparentPixel;
        }
    };
    window.Sprite_InputHandle = Sprite_InputHandle;

    //========================================================================
    // クラス定義 (Sprite_SimpleButton)
    //========================================================================
    class Sprite_SimpleButton extends Sprite_InputHandle {
        constructor(baseBitmap, option) {
            super();
            this.option = option;
            this.setFrame(0, 0, baseBitmap.width, baseBitmap.height);
            this.base = new Sprite(baseBitmap);
            this.addChild(this.base);

            const normalColor = this.getNormalColor();
            if (!!normalColor) {
                this.base.tint = normalColor;
            }

            this.content = new Sprite(new Bitmap(baseBitmap.width, baseBitmap.height));
            this.content.zIndex = 1;
            this.paintContent(this.content.bitmap);
            this.addChild(this.content);

            this._pressedMask = this.createPressedMask(baseBitmap);
            this._pressedMask.zIndex = 2;
            this.addChild(this._pressedMask);
            this._pressedMask.hide();

            this.focused = false;
            this.interactive = true;
            this.buttonMode = true;
        }

        paintContent(content) {
            // for sub-class override
        }

        getNormalColor() {
            // for sub-class override
            return undefined;
        }

        getFocusColor() {
            // for sub-class override
            return undefined;
        }

        createPressedMask(baseBitmap) {
            const bitmap = new Bitmap(baseBitmap.width, baseBitmap.height);
            const sprite = new Sprite(bitmap);

            const mask = new Sprite(baseBitmap);
            sprite.addChild(mask);
            sprite.mask = mask;

            bitmap.paintOpacity = PluginUtils.percent(255, 40);
            bitmap.fillAll('#000000');
            bitmap.paintOpacity = 255;

            return sprite;
        }

        setFocus(focus) {
            if (this.focused !== focus) {
                const tint = focus ? this.getFocusColor() : this.getNormalColor();
                if (!!tint) {
                    this.base.tint = tint;
                }
            }
            this.focused = focus;
        }

        isFocused() {
            return this.focused;
        }

        onPointerEntry(entry) {
            super.onPointerEntry(entry);
            this.setFocus(entry);
        }

        onPressed(pressed) {
            super.onPressed(pressed);
            Logger.d(`onPressed: pressed = ${pressed}`);
            this._pressedMask.visible = pressed;
        }
    }
    window.Sprite_SimpleButton = Sprite_SimpleButton;
})();
