//=============================================================================
// RPG Maker MZ - InputExtension
//
// 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 InputExtension.js
 *
 * Version: 0.0.2
 *
 * ゲーム中のユーザ入力処理を拡張します。
 * キーコンフィグが行えるようになります。
 *
 */
(() => {
	"use strict";

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

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

    const KeyCode = {
        KEYCODE_0: 48,
        KEYCODE_1: 49,
        KEYCODE_2: 50,
        KEYCODE_3: 51,
        KEYCODE_4: 52,
        KEYCODE_5: 53,
        KEYCODE_6: 54,
        KEYCODE_7: 55,
        KEYCODE_8: 56,
        KEYCODE_9: 57,
        KEYCODE_A: 65,
        KEYCODE_B: 66,
        KEYCODE_C: 67,
        KEYCODE_D: 68,
        KEYCODE_E: 69,
        KEYCODE_F: 70,
        KEYCODE_G: 71,
        KEYCODE_H: 72,
        KEYCODE_I: 73,
        KEYCODE_J: 74,
        KEYCODE_K: 75,
        KEYCODE_L: 76,
        KEYCODE_M: 77,
        KEYCODE_N: 78,
        KEYCODE_O: 79,
        KEYCODE_P: 80,
        KEYCODE_Q: 81,
        KEYCODE_R: 82,
        KEYCODE_S: 83,
        KEYCODE_T: 84,
        KEYCODE_U: 85,
        KEYCODE_V: 86,
        KEYCODE_W: 87,
        KEYCODE_X: 88,
        KEYCODE_Y: 89,
        KEYCODE_Z: 90,
        KEYCODE_F1: 112,
        KEYCODE_F2: 113,
        KEYCODE_F3: 114,
        KEYCODE_F4: 115,
        KEYCODE_F5: 116,
        KEYCODE_F6: 117,
        KEYCODE_F7: 118,
        KEYCODE_F8: 119,
        KEYCODE_F9: 120,
        KEYCODE_F10: 121,
        KEYCODE_F11: 122,
        KEYCODE_F12: 123,
        KEYCODE_NUM_0: 96,
        KEYCODE_NUM_1: 97,
        KEYCODE_NUM_2: 98,
        KEYCODE_NUM_3: 99,
        KEYCODE_NUM_4: 100,
        KEYCODE_NUM_5: 101,
        KEYCODE_NUM_6: 102,
        KEYCODE_NUM_7: 103,
        KEYCODE_NUM_8: 104,
        KEYCODE_NUM_9: 105,
        KEYCODE_NUM_ASTER: 106,
        KEYCODE_NUM_PLUS: 107,
        KEYCODE_NUM_MINUS: 109,
        KEYCODE_NUM_DOT: 110,
        KEYCODE_NUM_SLASH: 111,
        KEYCODE_BACK_SPACE: 8,
        KEYCODE_TAB: 9,
        KEYCODE_ENTER: 13,
        KEYCODE_SHIFT: 16,
        KEYCODE_CTRL: 17,
        KEYCODE_ALT: 18,
        KEYCODE_PAUSE_BREAK: 19,
        KEYCODE_CAPS_LOCK: 20,
        KEYCODE_ESCAPE: 27,
        KEYCODE_SPACE: 32,
        KEYCODE_PAGE_UP: 33,
        KEYCODE_PAGE_DOWN: 34,
        KEYCODE_END: 35,
        KEYCODE_HOME: 36,
        KEYCODE_CURSOR_LEFT: 37,
        KEYCODE_CURSOR_UP: 38,
        KEYCODE_CURSOR_RIGHT: 39,
        KEYCODE_CURSOR_DOWN: 40,
        KEYCODE_INSERT: 45,
        KEYCODE_DELETE: 46,
        KEYCODE_NUMLOCK: 144,
        KEYCODE_SCROLL_ROCK: 145,
        KEYCODE_HANKAKU: 244,
        KEYCODE_ZENKAKU: 243,
    };

    // for W3C GamePad Specifications
    // https://w3c.github.io/gamepad/#remapping
    const GamePad = {
        GAMEPAD_A: 0,
        GAMEPAD_B: 1,
        GAMEPAD_X: 2,
        GAMEPAD_Y: 3,
        GAMEPAD_LB: 4,
        GAMEPAD_RB: 5,
        GAMEPAD_LT: 6,
        GAMEPAD_RT: 7,
        GAMEPAD_BACK: 8,
        GAMEPAD_START: 9,
        GAMEPAD_L3: 10,
        GAMEPAD_R3: 11,
        GAMEPAD_UP: 12,
        GAMEPAD_DOWN: 13,
        GAMEPAD_LEFT: 14,
        GAMEPAD_RIGHT: 15,
        GAMEPAD_XBOX: 16,
    };

    const InputClass = {
        INPUT_CLASS_TAB: 'tab',
        INPUT_CLASS_OK: 'ok',
        INPUT_CLASS_CANCEL: 'cancel',
        INPUT_CLASS_SHIFT: 'shift',
        INPUT_CLASS_MENU: 'menu',
        INPUT_CLASS_CTRL: 'control',
        INPUT_CLASS_ESC: 'escape',
        INPUT_CLASS_PAGE_UP: 'pageup',
        INPUT_CLASS_PAGE_DOWN: 'pagedown',
        INPUT_CLASS_LEFT: 'left',
        INPUT_CLASS_UP: 'up',
        INPUT_CLASS_RIGHT: 'right',
        INPUT_CLASS_DOWN: 'down',
        INPUT_CLASS_DEBUG: 'debug',
        INPUT_CLASS_START: 'start',     // Extend by this plug-in
        INPUT_CLASS_SELECT: 'select',    // Extend by this plug-in
        INPUT_CLASS_AUTO: 'auto',     // Extend by this plug-in
    };

    window.KeyCode = Object.freeze(KeyCode);
    window.GamePad = Object.freeze(GamePad);
    window.InputClass = Object.freeze(InputClass);

    /********************************************************************************************************
     * Inputクラス拡張
     ********************************************************************************************************/
    Input.keyMapper[KeyCode.KEYCODE_TAB] = InputClass.INPUT_CLASS_TAB;
    Input.keyMapper[KeyCode.KEYCODE_ENTER] = InputClass.INPUT_CLASS_OK;
    Input.keyMapper[KeyCode.KEYCODE_SHIFT] = InputClass.INPUT_CLASS_SHIFT;
    Input.keyMapper[KeyCode.KEYCODE_CTRL] = InputClass.INPUT_CLASS_CTRL;
    Input.keyMapper[KeyCode.KEYCODE_ALT] = InputClass.INPUT_CLASS_CTRL;
    Input.keyMapper[KeyCode.KEYCODE_ESCAPE] = InputClass.INPUT_CLASS_ESC;
    Input.keyMapper[KeyCode.KEYCODE_SPACE] = InputClass.INPUT_CLASS_OK;
    Input.keyMapper[KeyCode.KEYCODE_PAGE_UP] = InputClass.INPUT_CLASS_PAGE_UP;
    Input.keyMapper[KeyCode.KEYCODE_PAGE_DOWN] = InputClass.INPUT_CLASS_PAGE_DOWN;
    Input.keyMapper[KeyCode.KEYCODE_CURSOR_LEFT] = InputClass.INPUT_CLASS_LEFT;
    Input.keyMapper[KeyCode.KEYCODE_CURSOR_UP] = InputClass.INPUT_CLASS_UP;
    Input.keyMapper[KeyCode.KEYCODE_CURSOR_RIGHT] = InputClass.INPUT_CLASS_RIGHT;
    Input.keyMapper[KeyCode.KEYCODE_CURSOR_DOWN] = InputClass.INPUT_CLASS_DOWN;
    Input.keyMapper[KeyCode.KEYCODE_NUM_0] = InputClass.INPUT_CLASS_ESC;
    Input.keyMapper[KeyCode.KEYCODE_NUM_2] = InputClass.INPUT_CLASS_DOWN;
    Input.keyMapper[KeyCode.KEYCODE_NUM_4] = InputClass.INPUT_CLASS_LEFT;
    Input.keyMapper[KeyCode.KEYCODE_NUM_6] = InputClass.INPUT_CLASS_RIGHT;
    Input.keyMapper[KeyCode.KEYCODE_NUM_8] = InputClass.INPUT_CLASS_UP;
    Input.keyMapper[KeyCode.KEYCODE_F9] = InputClass.INPUT_CLASS_DEBUG;
    Input.keyMapper[KeyCode.KEYCODE_W] = InputClass.INPUT_CLASS_UP;     // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_A] = InputClass.INPUT_CLASS_LEFT;   // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_S] = InputClass.INPUT_CLASS_DOWN;   // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_D] = InputClass.INPUT_CLASS_RIGHT;  // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_Q] = InputClass.INPUT_CLASS_AUTO;  // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_E] = InputClass.INPUT_CLASS_SELECT;  // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_R] = InputClass.INPUT_CLASS_START;  // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_C] = InputClass.INPUT_CLASS_MENU;  // Extend by this plug-in
    Input.keyMapper[KeyCode.KEYCODE_NUM_7] = InputClass.INPUT_CLASS_AUTO; // Extend by this plug-in

    Input.gamepadMapper[GamePad.GAMEPAD_A] = InputClass.INPUT_CLASS_OK;
    Input.gamepadMapper[GamePad.GAMEPAD_B] = InputClass.INPUT_CLASS_CANCEL;
    Input.gamepadMapper[GamePad.GAMEPAD_X] = InputClass.INPUT_CLASS_SHIFT;
    Input.gamepadMapper[GamePad.GAMEPAD_Y] = InputClass.INPUT_CLASS_MENU;
    Input.gamepadMapper[GamePad.GAMEPAD_LB] = InputClass.INPUT_CLASS_PAGE_UP;
    Input.gamepadMapper[GamePad.GAMEPAD_RB] = InputClass.INPUT_CLASS_PAGE_DOWN;
    Input.gamepadMapper[GamePad.GAMEPAD_LT] = InputClass.INPUT_CLASS_AUTO;
    Input.gamepadMapper[GamePad.GAMEPAD_RT] = InputClass.INPUT_CLASS_CTRL;
    Input.gamepadMapper[GamePad.GAMEPAD_UP] = InputClass.INPUT_CLASS_UP;
    Input.gamepadMapper[GamePad.GAMEPAD_DOWN] = InputClass.INPUT_CLASS_DOWN;
    Input.gamepadMapper[GamePad.GAMEPAD_LEFT] = InputClass.INPUT_CLASS_LEFT;
    Input.gamepadMapper[GamePad.GAMEPAD_RIGHT] = InputClass.INPUT_CLASS_RIGHT;
    Input.gamepadMapper[GamePad.GAMEPAD_BACK] = InputClass.INPUT_CLASS_SELECT;  // Extend by this plug-in
    Input.gamepadMapper[GamePad.GAMEPAD_START] = InputClass.INPUT_CLASS_START;  // Extend by this plug-in

    /*
     * Inputクラスでキーコードの取得を行えるように改造
     */
    Input._latestKey = null;
    Input._keyPressedTime = 0;
    Input._currentKeyState = {};
    Input._previousKeyState = {};
    Input._keySpressed = false;
    Input._gamePadUpdateCount = 0;

    const _Input_update = Input.update;
    Input.update = function() {
        _Input_update.apply(this, arguments);
        if (this._currentKeyState[this._latestKey]) {
            this._keyPressedTime++;
        } else {
            this._latestKey = null;
        }

        for (const code in this._currentKeyState) {
            if (this._currentKeyState[code] && !this._previousKeyState[code]) {
                this._latestKey = code;
                this._keyPressedTime = 0;
            }
            this._previousKeyState[code] = this._currentKeyState[code];
        }
    }

    const _Input_onKeyDown = Input._onKeyDown;
    Input._onKeyDown = function(event) {
        _Input_onKeyDown.apply(this, arguments);
        this._currentKeyState['key_' + event.keyCode] = true;
    };

    const _Input_onKeyUp = Input._onKeyUp;
    Input._onKeyUp = function(event) {
        _Input_onKeyUp.apply(this, arguments);

        // 何故かSだけ押した瞬間onKeydownと同時にonKeyupが飛んでくる現象の回避
        if (event.keyCode === KeyCode.KEYCODE_S && !this._keySpressed) {
            this._keySpressed = true;
            return;
        }
        this._currentKeyState['key_' + event.keyCode] = false;
    };

    const _Input_clear = Input.clear;
    Input.clear = function() {
        _Input_clear.apply(this, arguments);
        this._latestKey = null;
        this._currentKeyState = {};
        this._previousKeyState = {};
        this._keySpressed = false;
    }

    Input.isKeyCodeTriggered = function(keyCode) {
        if (this._isEscapeCompatible(this.keyMapper[keyCode]) && this.isTriggered(InputClass.INPUT_CLASS_ESC)) {
            return true;
        } else {
            return this._latestKey === keyCode && this._keyPressedTime === 0;
        }
    };

    const _Input__updateGamepadState = Input._updateGamepadState;
    Input._updateGamepadState = function(gamepad) {
        if (this._gamePadUpdateCount === 0) {
            _Input__updateGamepadState.apply(this, arguments);
        }
        this._gamePadUpdateCount = (this._gamePadUpdateCount + 1) % 8;
    };

    const _Input__isEscapeCompatible = Input._isEscapeCompatible;
    Input._isEscapeCompatible = function(keyName) {
        return keyName === InputClass.INPUT_CLASS_CANCEL || keyName === InputClass.INPUT_CLASS_MENU;
    };
})();
