// Copyright (c) 2021 Mokusei Penguin
// http://opensource.org/licenses/mit-license.php
/*:
 * @target MV MZ
 * @plugindesc 
 * @author Mokusei Penguin
 * @url 
 *
 * @help 
 * @command delete
 * @desc 
 *
 * @arg mapIds
 * @desc 0:Current map / Range can be specified
 * @default 0
 * 
 * @arg indexes
 * @desc Range can be specified
 * @default 0
 * 
 * @--------------------------------
 * 
 * @command set
 * @desc 
 *
 * @arg mapIds
 * @desc 0:Current map / Range can be specified
 * @default 0
 * 
 * @arg eventIds
 * @desc Range can be specified
 * @default 0
 * 
 * @arg indexes
 * @desc Range can be specified
 * @default 0
 * 
 * @arg value
 * @desc 
 * @type number
 * @min -99999999
 * @max 99999999
 * @default 0
 * 
 * @--------------------------------
 * 
 * @param Variables
 * @desc Number of variable to be self-variable
 * (Range can be specified)
 * @default 
 * 
 */
/*:ja
 * @target MV MZ
 * @plugindesc 
 * @author 木星ペンギン
 * @url 
 *
 * @help 
 * @command delete
 * @text セルフ変数削除
 * @desc 
 *
 * @arg mapIds
 * @text マップID
 * @desc 0:現在のマップ / 範囲指定可
 * @default 0
 * 
 * @arg indexes
 * @text 変数番号
 * @desc 範囲指定可
 * @default 0
 * 
 * @--------------------------------
 * 
 * @command set
 * @text セルフ変数変更
 * @desc 
 *
 * @arg mapIds
 * @text マップID
 * @desc 0:現在のマップ / 範囲指定可
 * @default 0
 * 
 * @arg eventIds
 * @text イベントID
 * @desc 範囲指定可
 * @default 0
 * 
 * @arg indexes
 * @text 変数番号
 * @desc 範囲指定可
 * @default 0
 * 
 * @arg value
 * @text 値
 * @desc 
 * @type number
 * @min -99999999
 * @max 99999999
 * @default 0
 * 
 * @--------------------------------
 * 
 * @param Variables
 * @desc セルフ変数にする変数の番号
 * (範囲指定可)
 * @default 
 * 
 */
(() => {
    'use strict';
    const pluginName = 'JsScript52Set';
    const parameters = PluginManager.parameters(pluginName);
    const convertToArray = (param) => {
        return param.split(',').reduce((r, item) => {
            const match = /(\d+)-(\d+)/.exec(item);
            if (match) {
                const start = Number(match[1]);
                const end = Number(match[2]);
                return r.concat([...Array(end + 1).keys()].slice(start));
            } else {
                return item ? r.concat(Number(item)) : r;
            }
        }, []);
    };
    const params_Variables = convertToArray(parameters['Variables']);
    const _Game_Temp_initialize = Game_Temp.prototype.initialize;
    Game_Temp.prototype.initialize = function() {
        _Game_Temp_initialize.apply(this, arguments);
        this._reservedCommonEventId = 0;
    };
    Game_Temp.prototype.reservedCommonEventId = function() {
        return this._reservedCommonEventId;
    };
    const _Game_Temp_reservedCommonEvent = Game_Temp.prototype.reservedCommonEvent;
    Game_Temp.prototype.reservedCommonEvent = function() {
        this._reservedCommonEventId = this._commonEventId;
        return _Game_Temp_reservedCommonEvent.apply(this, arguments);
    };
    const _Game_Temp_retrieveCommonEvent = Game_Temp.prototype.retrieveCommonEvent;
    Game_Temp.prototype.retrieveCommonEvent = function() {
        this._reservedCommonEventId = this._commonEventQueue[0] || 0;
        return _Game_Temp_retrieveCommonEvent.apply(this, arguments);
    };
    const _Game_Variables_clear = Game_Variables.prototype.clear;
    Game_Variables.prototype.clear = function() {
        _Game_Variables_clear.apply(this, arguments);
        this._selfVariables = {};
        this._mapId = 0;
        this._eventId = 0;
    };
    const _Game_Variables_value = Game_Variables.prototype.value;
    Game_Variables.prototype.value = function(variableId) {
        if (this._eventId > 0 && params_Variables.includes(variableId)) {
            const key = [this._mapId, this._eventId, variableId];
            return this._selfVariables[key] || 0;
        } else {
            return _Game_Variables_value.apply(this, arguments);
        }
    };
    const _Game_Variables_setValue = Game_Variables.prototype.setValue;
    Game_Variables.prototype.setValue = function(variableId, value) {
        if (this._eventId > 0 && params_Variables.includes(variableId)) {
            const key = [this._mapId, this._eventId, variableId];
            this.setSelfValue(key, value);
        } else {
            _Game_Variables_setValue.apply(this, arguments);
        }
    };
    Game_Variables.prototype.setSelfValue = function(key, value) {
        if (typeof value === 'number') {
            value = Math.floor(value);
        }
        this._selfVariables[key] = value;
        this.onChange();
    };
    Game_Variables.prototype.deleteSelfValues = function(mapId, evIds, indexes) {
        const re = new RegExp('%1,(\\d+),(\\d+)'.format(mapId));
        for (const key in this._selfVariables) {
            const match = re.exec(key);
            if (match) {
                if (
                    (evIds.length === 0 || evIds.includes(Number(match[1]))) &&
                    (indexes.length === 0 || indexes.includes(Number(match[2])))
                ) {
                    delete this._selfVariables[key];
                }
            }
        }
        this.onChange();
    };
    Game_Variables.prototype.reserveEvent = function(mapId, eventId) {
        this._mapId = mapId;
        this._eventId = eventId;
    };
    const _Game_Event_findProperPageIndex = Game_Event.prototype.findProperPageIndex;
    Game_Event.prototype.findProperPageIndex = function() {
        $gameVariables.reserveEvent(this._mapId, this._eventId);
        return _Game_Event_findProperPageIndex.apply(this, arguments);
    };
    const _Game_CommonEvent_refresh = Game_CommonEvent.prototype.refresh;
    Game_CommonEvent.prototype.refresh = function() {
        _Game_CommonEvent_refresh.apply(this, arguments);
        if (this._interpreter) {
            this._interpreter._commonEventId = this._commonEventId;
        }
    };
    const _Game_Interpreter_clear = Game_Interpreter.prototype.clear;
    Game_Interpreter.prototype.clear = function() {
        _Game_Interpreter_clear.apply(this, arguments);
        this._commonEventId = 0;
    };
    const _Game_Interpreter_setupReservedCommonEvent = Game_Interpreter.prototype.setupReservedCommonEvent;
    Game_Interpreter.prototype.setupReservedCommonEvent = function() {
        const result = _Game_Interpreter_setupReservedCommonEvent.apply(this, arguments);
        if (result) {
            this._commonEventId = $gameTemp.reservedCommonEventId();
        }
        return result;
    };
    const _Game_Interpreter_update = Game_Interpreter.prototype.update;
    Game_Interpreter.prototype.update = function() {
        this.reserveSelfVar();
        _Game_Interpreter_update.apply(this, arguments);
    };
    const _Game_Interpreter_setupChild = Game_Interpreter.prototype.setupChild;
    Game_Interpreter.prototype.setupChild = function(list, eventId) {
        _Game_Interpreter_setupChild.apply(this, arguments);
        this._childInterpreter._commonEventId = this._commonEventId;
    };
    Game_Interpreter.prototype.reserveSelfVar = function() {
        if (this._commonEventId > 0) {
            $gameVariables.reserveEvent(-1, this._commonEventId);
        } else if (this._eventId > 0) {
            $gameVariables.reserveEvent(this._mapId, this._eventId);
        }
    };
    const _mzCommands = {
        DeleteSelfVariable: { name:'delete', keys:['mapIds', 'indexes'] },
        SetSelfVariable: {
            name:'set',
            keys:['mapIds', 'eventIds', 'indexes', 'value']
        }
    };
    Object.assign(_mzCommands, {
        'セルフ変数削除': _mzCommands.DeleteSelfVariable,
        'セルフ変数操作': _mzCommands.SetSelfVariable
    });
    const _Game_Interpreter_pluginCommand = Game_Interpreter.prototype.pluginCommand;
    Game_Interpreter.prototype.pluginCommand = function(command, args) {
        _Game_Interpreter_pluginCommand.apply(this, arguments);
        const mzCommand = _mzCommands[command];
        if (mzCommand) {
            const args2 =
                Object.assign( ...mzCommand.keys.map((k,i) => ({[k]:args[i]})) );
            PluginManager.callCommand(this, pluginName, mzCommand.name, args2);
        }
    };
    PluginManager._commands = PluginManager._commands || {};
    if (!PluginManager.registerCommand) {
        PluginManager.registerCommand = function(pluginName, commandName, func) {
            const key = pluginName + ":" + commandName;
            this._commands[key] = func;
        };
    }
    if (!PluginManager.callCommand) {
        PluginManager.callCommand = function(self, pluginName, commandName, args) {
            const key = pluginName + ":" + commandName;
            const func = this._commands[key];
            if (typeof func === "function") {
                func.bind(self)(args);
            }
        };
    }
    PluginManager.registerCommand(pluginName, 'delete', args => {
        const indexes = PluginManager.mppConvertRange(args.indexes);
        for (let mapId of PluginManager.mppConvertRange(args.mapIds)) {
            mapId = mapId || $gameMap.mapId();
            $gameVariables.deleteSelfValues(mapId, [], indexes);
        }
    });
    PluginManager.registerCommand(pluginName, 'set', args => {
        const eventIds = PluginManager.mppConvertRange(args.eventIds);
        const indexes = PluginManager.mppConvertRange(args.indexes);
        const value = PluginManager.mppValue(args.value);
        for (let mapId of PluginManager.mppConvertRange(args.mapIds)) {
            mapId = mapId || $gameMap.mapId();
            for (const evId of eventIds) {
                for (const index of indexes) {
                    if (
                        mapId > 0 &&
                        evId > 0 &&
                        params_Variables.includes(index)
                    ) {
                        const key = [mapId, evId, index];
                        $gameVariables.setSelfValue(key, value);
                    }
                }
            }
        }
    });
    PluginManager.mppValue = function(value) {
        const match = /^V\[(\d+)\]$/i.exec(value);
        return match ? $gameVariables.value(+match[1]) : +value;
    };
    PluginManager.mppConvertRange = function(text) {
        return convertToArray(text.replace(/V\[(\d+)\]/gi, (_, p1) =>
            $gameVariables.value(parseInt(p1))
        ));
    };
})();
