/*:
 * @plugindesc v.3.1.1 [MV|MZ] Adds an action window for items in inventory.
 * @author DKPlugins
 * @url https://dk-plugins.ru
 * @target MZ
 * @help

 ### Info about plugin ###
 Title: DK_Item_Actions
 Author: DKPlugins
 Site: https://dk-plugins.ru
 Version: 3.1.1
 Release: 17.04.2025
 First release: 04.09.2023

 ###=========================================================================
 ## Compatibility
 ###=========================================================================
 RPG Maker MV: 1.5+
 RPG Maker MZ: 1.0+

 ###=========================================================================
 ## Instructions
 ###=========================================================================
 To utilize the hidden and disabled item actions features for
 items, weapons, and armor, you can use specific tags in the note box of each item.
 These tags will control the visibility and availability of actions.

 Hidden Actions:
 To hide specific actions for an item, weapon, or armor,
 add the following tag to the note box:
 <hide_item_actions: action1, action2, ...>

 Replace action1, action2, etc., with the symbols of the actions you want to hide.

 Disabled Actions:
 To disable specific actions for an item, weapon, or armor,
 add the following tag to the note box:
 <disable_item_actions: action1, action2, ...>

 Replace action1, action2, etc., with the symbols of the actions you want to disable.

 Example:
 If you want to hide the "use" action and disable the "equip" action
 for a specific item, weapon, or armor, you would add the following to the note box:
 <hide_item_actions: use>
 <disable_item_actions: equip>

 ###=========================================================================
 ## Add-ons
 ###=========================================================================
 Download add-ons for this plugin: https://dk-plugins.ru/item-actions/

 ###=========================================================================
 ## See also
 ###=========================================================================
 1. Mouse System (https://dk-plugins.ru/mouse-system/)
 Allows you to change the mouse cursor, activate events by clicking, hovering, etc.

 2. Picture Choices (https://dk-plugins.ru/picture-choices/)
 Allows to use a graphic buttons instead of the choice window.

 3. Pictures Glow (https://dk-plugins.ru/pictures-glow/)
 Allows highlighting pictures on mouse hover.

 4. Video Player (https://dk-plugins.ru/video-player/)
 Adds video to the title screen, credits, the layers on the map and other.

 5. Events Glow (https://dk-plugins.ru/events-glow/)
 Allows highlighting events on mouse hover.

 ###=========================================================================
 ## License and terms of use
 ###=========================================================================
 You can:
 -To use the plugin for your non-commercial projects
 -Change code of the plugin

 You cannot:
 -Delete or change any information about the plugin
 -Distribute the plugin and its modifications

 ## Commercial license ##
 Visit the page: https://dk-plugins.ru/commercial-license/

 ###===========================================================================
 ## Support
 ###===========================================================================
 Become a subscriber on boosty: https://boosty.to/dkplugins
 Become a subscriber on patreon: https://patreon.com/dkplugins
 Donate: https://dk-plugins.ru/donate



 * @param actions
 * @text Actions
 * @desc List of item actions
 * @type struct<ItemAction>[]
 * @default ["{\"name\":\"Use\",\"symbol\":\"use\",\"position\":\"top\",\"handler\":\"\\\"\\\"\",\"selectActor\":\"true\",\"inputNumber\":\"false\",\"loseItem\":\"false\",\"categories\":\"[\\\"item\\\",\\\"weapon\\\",\\\"armor\\\",\\\"keyItem\\\"]\",\"visible\":\"\\\"visible = true;\\\"\",\"enabled\":\"\\\"enabled = true;\\\"\",\"variables\":\"[]\",\"se\":\"{\\\"name\\\":\\\"\\\",\\\"volume\\\":\\\"90\\\",\\\"pitch\\\":\\\"100\\\",\\\"pan\\\":\\\"0\\\"}\"}","{\"name\":\"Throw\",\"symbol\":\"throw\",\"position\":\"bottom\",\"handler\":\"\\\"\\\"\",\"selectActor\":\"false\",\"inputNumber\":\"true\",\"loseItem\":\"true\",\"categories\":\"[\\\"item\\\",\\\"weapon\\\",\\\"armor\\\"]\",\"visible\":\"\\\"visible = false;\\\"\",\"enabled\":\"\\\"enabled = true;\\\"\",\"variables\":\"[]\",\"se\":\"{\\\"name\\\":\\\"\\\",\\\"volume\\\":\\\"90\\\",\\\"pitch\\\":\\\"100\\\",\\\"pan\\\":\\\"0\\\"}\"}"]

 * @param windowWidth
 * @text Window width
 * @desc Window width
 * @type number
 * @min 200
 * @default 250

 * @param maxVisibleRows
 * @text Max visible rows
 * @desc Max visible rows
 * @type number
 * @default 3

*/

/*:ru
 * @plugindesc v.3.1.1 [MV|MZ] Добавляет окно действий для предметов в инвентаре.
 * @author DKPlugins
 * @url https://dk-plugins.ru
 * @target MZ
 * @help

 ### Информация о плагине ###
 Название: DK_Item_Actions
 Автор: DKPlugins
 Сайт: https://dk-plugins.ru
 Контакты: https://dk-plugins.ru/contacts
 Версия: 3.1.1
 Релиз: 17.04.2025
 Первый релиз: 04.09.2023

 ###=========================================================================
 ## Совместимость
 ###=========================================================================
 RPG Maker MV: 1.5+
 RPG Maker MZ: 1.0+

 ###=========================================================================
 ## Инструкции
 ###=========================================================================
 # Скрытие и отключение действий #
 Чтобы использовать функции скрытых и недоступных действий для
 предметов, оружия и брони, Вы можете использовать специальные теги
 в окне заметок каждого предмета.
 Эти теги будут контролировать видимость и доступность действий.

 Скрытые действия:
 Чтобы скрыть определенные действия для предмета, оружия или брони,
 добавьте следующий тег в окно заметок:
 <hide_item_actions: действие1, действие2, ...>

 Замените действие1, действие2 и т.д. на символы действий, которые вы хотите скрыть.

 Недоступные действия:
 Чтобы отключить определенные действия для предмета, оружия или брони,
 добавьте следующий тег в окно заметок:
 <disable_item_actions: действие1, действие2, ...>

 Замените действие1, действие2 и т.д. на символы действий, которые вы хотите отключить.

 Пример:
 Если вы хотите скрыть действие "использовать" и отключить действие "экипировать"
 для конкретного предмета, оружия или брони, добавьте следующее в окно заметок:
 <hide_item_actions: use>
 <disable_item_actions: equip>


 ###=========================================================================
 ## Дополнения
 ###=========================================================================
 Скачать дополнения для этого плагина: https://dk-plugins.ru/item-actions/

 ###=========================================================================
 ## Смотрите также
 ###=========================================================================
 1. Система Мыши (https://dk-plugins.ru/mouse-system/)
 Позволяет изменять курсор мыши, активировать события нажатием, наведением и др.

 2. Выбор С Изображениями (https://dk-plugins.ru/picture-choices/)
 Позволяет использовать графические кнопки вместо окна выбора.

 3. Свечение Изображений (https://dk-plugins.ru/pictures-glow/)
 Позволяет подсвечивать изображения при наведении мыши.

 4. Видео плеер (https://dk-plugins.ru/video-player/)
 Добавляет видео перед титульным экраном, титры, слои на карте и другое.

 5. Свечение Событий (https://dk-plugins.ru/events-glow/)
 Позволяет подсвечивать события при наведении мыши.

 ###=========================================================================
 ## Лицензия и правила использования плагина
 ###=========================================================================
 Вы можете:
 -Использовать плагин в некоммерческих проектах
 -Изменять код плагина

 Вы не можете:
 -Удалять или изменять любую информацию о плагине
 -Распространять плагин и его модификации

 ## Коммерческая лицензия ##
 Посетите страницу: https://dk-plugins.ru/commercial-license/

 ###=========================================================================
 ## Поддержка
 ###=========================================================================
 Стать подписчиком на boosty: https://boosty.to/dkplugins
 Стать подписчиком на patreon: https://patreon.com/dkplugins
 Поддержать: https://dk-plugins.ru/donate



 * @param actions
 * @text Действия
 * @desc Список действий предметов
 * @type struct<ItemAction>[]
 * @default ["{\"name\":\"Использовать\",\"symbol\":\"use\",\"position\":\"top\",\"handler\":\"\\\"\\\"\",\"selectActor\":\"true\",\"inputNumber\":\"false\",\"loseItem\":\"false\",\"categories\":\"[\\\"item\\\",\\\"weapon\\\",\\\"armor\\\",\\\"keyItem\\\"]\",\"visible\":\"\\\"visible = true;\\\"\",\"enabled\":\"\\\"enabled = true;\\\"\",\"variables\":\"[]\",\"se\":\"{\\\"name\\\":\\\"\\\",\\\"volume\\\":\\\"90\\\",\\\"pitch\\\":\\\"100\\\",\\\"pan\\\":\\\"0\\\"}\"}","{\"name\":\"Выбросить\",\"symbol\":\"throw\",\"position\":\"bottom\",\"handler\":\"\\\"\\\"\",\"selectActor\":\"false\",\"inputNumber\":\"true\",\"loseItem\":\"true\",\"categories\":\"[\\\"item\\\",\\\"weapon\\\",\\\"armor\\\"]\",\"visible\":\"\\\"visible = false;\\\"\",\"enabled\":\"\\\"enabled = true;\\\"\",\"variables\":\"[]\",\"se\":\"{\\\"name\\\":\\\"\\\",\\\"volume\\\":\\\"90\\\",\\\"pitch\\\":\\\"100\\\",\\\"pan\\\":\\\"0\\\"}\"}"]

 * @param windowWidth
 * @text Ширина окна
 * @desc Ширина окна
 * @type number
 * @min 200
 * @default 250

 * @param maxVisibleRows
 * @text Максимальное количество строк
 * @desc Максимальное количество строк
 * @type number
 * @default 3

*/

/*~struct~ItemAction:

 * @param name
 * @text Action name
 * @desc Action name
 * @type combo
 * @option Use
 * @option Equip
 * @option Throw

 * @param symbol
 * @text Action symbol
 * @desc Action symbol. The value must be unique across all actions.
 * @type combo
 * @option use
 * @option equip
 * @option throw

 * @param position
 * @text Action position
 * @desc Position of the action in the list
 * @type select
 * @option Top (above commands from other plugins and add-ons)
 * @value top
 * @option Bottom (below commands from other plugins and add-ons)
 * @value bottom
 * @default top

 * @param handler
 * @text Action handler
 * @desc Your JavaScript code that is executed when the action is selected
 * @type note
 * @default "// you can use variables\n// 'user', 'item', 'actor' and 'number' in this scope"

 * @param selectActor
 * @text Required actor selection
 * @desc Is actor selection necessary ?
 * @type boolean
 * @default false

 * @param inputNumber
 * @text Input number
 * @desc Is it necessary to enter an item amount ?
 * @type boolean
 * @default false

 * @param loseItem
 * @text Lose item
 * @desc Remove the item from the inventory after performing the action ?
 * @type boolean
 * @default false

 * @param categories
 * @text Item categories
 * @desc Categories of items for which the action is displayed
 * @type select[]
 * @option item
 * @option weapon
 * @option armor
 * @option keyItem
 * @default ["item","weapon","armor","keyItem"]

 * @param visible
 * @text Action visible
 * @desc Your JavaScript code, which is responsible for the visibility of the action. Write true/false to the 'visible' variable.
 * @type note
 * @default "// you can use variable 'user' and 'item' in this scope\nvisible = true;"

 * @param enabled
 * @text Action enabled
 * @desc Your JavaScript code, which is responsible for the availability of the action. Write true/false to the 'enabled' variable.
 * @type note
 * @default "// you can use variable 'user' and 'item' in this scope\nenabled = true;"

 * @param variables
 * @text Variables
 * @desc Variables that increase when the action is performed
 * @type struct<ItemVariable>[]
 * @default []

 * @param se
 * @text Sound effect
 * @desc Sound effect after performing an action
 * @type struct<SE>
 * @default {"name":"","volume":"90","pitch":"100","pan":"0"}

*/

/*~struct~ItemAction:ru

 * @param name
 * @text Название действия
 * @desc Название действия
 * @type combo
 * @option Использовать
 * @option Экипировать
 * @option Выбросить

 * @param symbol
 * @text Символ действия
 * @desc Символ действия. Значение должно быть уникальным для всех действий.
 * @type combo
 * @option use
 * @option equip
 * @option throw

 * @param position
 * @text Позиция действия
 * @desc Позиция действия в списке
 * @type select
 * @option Сверху (выше команд из других плагинов и дополнений)
 * @value top
 * @option Снизу (ниже команд из других плагинов и дополнений)
 * @value bottom
 * @default top

 * @param handler
 * @text Обработчик действия
 * @desc Ваш JavaScript код, который выполняется при выборе действия
 * @type note
 * @default "// you can use variables\n// 'user', 'item', 'actor' and 'number' in this scope"

 * @param selectActor
 * @text Выбор персонажа обязателен
 * @desc Нужен ли выбор персонажа ?
 * @type boolean
 * @default false

 * @param inputNumber
 * @text Ввод количества
 * @desc Нужно ли вводить количество предмета ?
 * @type boolean
 * @default false

 * @param loseItem
 * @text Убрать предмет
 * @desc Убрать предмет из инвентаря после выполнения действия ?
 * @type boolean
 * @default false

 * @param categories
 * @text Категории предметов
 * @desc Категории предметов, для которых отображается действие
 * @type select[]
 * @option item
 * @option weapon
 * @option armor
 * @option keyItem
 * @default ["item","weapon","armor","keyItem"]

 * @param visible
 * @text Видимость действия
 * @desc Ваш JavaScript код, который отвечает за видимость действия. Запишите true/false в переменную 'visible'.
 * @type note
 * @default "// you can use variable 'user' and 'item' in this scope\nvisible = true;"

 * @param enabled
 * @text Доступность действия
 * @desc Ваш JavaScript код, который отвечает за доступность действия. Запишите true/false в переменную 'enabled'.
 * @type note
 * @default "// you can use variable 'user' and 'item' in this scope\nenabled = true;"

 * @param variables
 * @text Переменные
 * @desc Переменные, которые увеличиваются при выполнении действия
 * @type struct<ItemVariable>[]
 * @default []

 * @param se
 * @text Звуковой эффект
 * @desc Звуковой эффект после выполнения действия
 * @type struct<SE>
 * @default {"name":"","volume":"90","pitch":"100","pan":"0"}

*/

/*~struct~ItemVariable:

 * @param itemId
 * @text Item
 * @desc The item for which the action is performed
 * @type item
 * @default 0

 * @param variable
 * @text Variable
 * @desc The variable that increases when the action is performed
 * @type variable
 * @default 0

*/

/*~struct~ItemVariable:ru

 * @param itemId
 * @text Предмет
 * @desc Предмет, для которого выполняется действие
 * @type item
 * @default 0

 * @param variable
 * @text Variable
 * @desc Переменная, которая увеличивается при выполнении действия
 * @type variable
 * @default 0

*/

/*~struct~SE:

 * @param name
 * @text Filename
 * @desc Filename. Leave blank to not use.
 * @type file
 * @dir audio/se/
 * @require 1

 * @param volume
 * @text Volume
 * @desc Volume
 * @type number
 * @min 1
 * @max 100
 * @default 90

 * @param pitch
 * @text Pitch
 * @desc Pitch
 * @type number
 * @min 50
 * @max 150
 * @default 100

 * @param pan
 * @text Panorama
 * @desc Panorama
 * @type number
 * @min -100
 * @max 100
 * @default 0

*/

/*~struct~SE:ru

 * @param name
 * @text Файл
 * @desc Файл. Оставьте пустым, чтобы не использовать.
 * @type file
 * @dir audio/se/
 * @require 1

 * @param volume
 * @text Громкость
 * @desc Громкость
 * @type number
 * @min 0
 * @max 100
 * @default 90

 * @param pitch
 * @text Темп
 * @desc Темп
 * @type number
 * @min 50
 * @max 150
 * @default 100

 * @param pan
 * @text Панорама
 * @desc Панорама
 * @type number
 * @min -100
 * @max 100
 * @default 0

*/

'use strict';

var Imported = Imported || {};
Imported['DK_Item_Actions'] = '3.1.1';

//===========================================================================
// initialize parameters
//===========================================================================

const ItemActionsParams = (function() {

    function parse(string) {
        try {
            return JSON.parse(string, function(key, value) {
                if (typeof string === 'number' || typeof string === 'boolean') {
                    return string;
                }

                try {
                    if (Array.isArray(value)) {
                        return value.map(val => parse(val));
                    }

                    return parse(value);
                } catch (e) {
                    return value;
                }
            });
        } catch(e) {
            return string;
        }
    }

    const parameters = PluginManager.parameters('DK_Item_Actions');

    return Object.entries(parameters).reduce((acc, [key, value]) => {
        acc[key] = parse(value);

        return acc;
    }, {});

})();

//===========================================================================
// Scene_Item
//===========================================================================

const ItemActions_Scene_Item_create =
    Scene_Item.prototype.create;
Scene_Item.prototype.create = function() {
    ItemActions_Scene_Item_create.apply(this, arguments);
    this.createItemActionsWindow();
    this.createNumberWindow();
};

Scene_Item.prototype.createActorWindow = function() {
    if (Utils.RPGMAKER_NAME === 'MV') {
        this._actorWindow = new Window_ItemActor();
    } else {
        this._actorWindow = new Window_ItemActor(this.actorWindowRect());
    }

    this._actorWindow.setHandler('ok', this.onActorOk.bind(this));
    this._actorWindow.setHandler('cancel', this.onActorCancel.bind(this));
    this.addWindow(this._actorWindow);
};

Scene_Item.prototype.createItemActionsWindow = function() {
    this._itemActionsWindow = new Window_ItemActions(this.itemActionsWindowRect());
    this._itemActionsWindow.setHandler('ok', this.onItemActionOk.bind(this));
    this._itemActionsWindow.setHandler('cancel', this.onItemActionCancel.bind(this));
    this._itemActionsWindow.hide();
    this.addWindow(this._itemActionsWindow);
};

Scene_Item.prototype.itemActionsWindowRect = function() {
    const width = ItemActionsParams.windowWidth;
    const height = this.calcWindowHeight(ItemActionsParams.maxVisibleRows, true);
    const x = (Graphics.boxWidth - width) / 2;
    const y = (Graphics.boxHeight - height) / 2;

    return new Rectangle(x, y, width, height);
};

Scene_Item.prototype.createNumberWindow = function() {
    this._numberWindow = new Window_ItemNumber(this.numberWindowRect());
    this._numberWindow.setHandler('ok', this.onNumberOk.bind(this));
    this._numberWindow.setHandler('cancel', this.onNumberCancel.bind(this));
    this._numberWindow.hide();
    this.addWindow(this._numberWindow);
};

Scene_Item.prototype.numberWindowRect = function() {
    const wx = this._itemWindow.x;
    const wy = this._itemWindow.y;
    const ww = this._itemWindow.width;
    const wh = this._itemWindow.height;
    return new Rectangle(wx, wy, ww, wh);
};

Scene_Item.prototype.isSingleActor = function() {
    return this._actorWindow.maxItems() === 1;
};

Scene_Item.prototype.category = function() {
    return this._categoryWindow ?
        this._categoryWindow.currentSymbol() : null;
};

Scene_Item.prototype.itemAction = function() {
    return this._itemAction;
};

Scene_Item.prototype.number = function() {
    return this._numberWindow.number() || 1;
};

Scene_Item.prototype.onItemOk = function() {
    this._itemActionsWindow.setup(this.user(), this.item());
    this.showItemActionsWindow();
};

Scene_Item.prototype.onItemActionOk = function() {
    const action = this._itemActionsWindow.currentExt();

    this._itemActionsWindow.hide();
    this._itemAction = action;

    $gameParty.setLastItem(this.item());

    if (action.symbol === 'use') {
        this.determineItem();
    } else if (action.selectActor) {
        this.startActorSelection();
    } else if (action.inputNumber) {
        this.startNumberInput();
    } else {
        this.callAction(action);
    }
};

Scene_Item.prototype.onItemActionCancel = function() {
    this._itemActionsWindow.hide();
    this.activateItemWindow();
};

Scene_Item.prototype.onActorOk = function() {
    if (this._itemAction.inputNumber) {
        this.startNumberInput();
    } else {
        this.callAction(this._itemAction);
    }
};

Scene_Item.prototype.onActorCancel = function() {
    this.hideActorWindow();
    this.showItemActionsWindow();
    this._itemAction = null;
};

Scene_Item.prototype.determineItem = function() {
    const action = new Game_Action(this.user());
    const item = this.item();

    action.setItemObject(item);

    if (action.isForFriend()) {
        this.startActorSelection();
    } else {
        this.callAction(this._itemAction);
    }
};

Scene_Item.prototype.useItem = function() {
    this.user().useItem(this.item());
    this.applyItem();
};

Scene_Item.prototype.onNumberOk = function() {
    this.callAction(this._itemAction);
};

Scene_Item.prototype.onNumberCancel = function() {
    this._numberWindow.hide();

    if (this._itemAction.selectActor) {
        this.showActorWindow();
    } else {
        this.showItemActionsWindow();
    }
};

Scene_Item.prototype.showItemActionsWindow = function() {
    this._itemActionsWindow.show();
    this._itemActionsWindow.activate();
};

Scene_Item.prototype.startActorSelection = function() {
    this.showActorWindow();
    this._actorWindow.setup(this.user(), this.item(), this.itemAction());
    this._actorWindow.select(0);

    if (this.isSingleActor()) {
        this._actorWindow.processOk();
    }
};

Scene_Item.prototype.startNumberInput = function() {
    const item = this.item();
    const itemAmount = $gameParty.numItems(item);

    this._itemActionsWindow.hide();
    this._actorWindow.hide();
    this._numberWindow.setup(this.user(), item, this.itemAction(), this.actor(), itemAmount);
    this._numberWindow.show();
    this._numberWindow.activate();

    if (itemAmount === 1) {
        this._numberWindow.processOk();
    }
};

Scene_Item.prototype.hideNumberWindow = function() {
    this._numberWindow.hide();
    this._numberWindow.resetNumber();
};

Scene_Item.prototype.refreshCategoryWindow = function() {
    if (this._categoryWindow) {
        this._categoryWindow.refresh();
    }
};

Scene_Item.prototype.refreshActorWindow = function() {
    this._actorWindow.refresh();
};

Scene_Item.prototype.refreshCurrentItem = function() {
    this._itemWindow.redrawCurrentItem();
};

Scene_Item.prototype.canEvalAction = function(action) {
    if (action.symbol === 'use') {
        return this.canUse();
    }

    return true;
};

Scene_Item.prototype.callAction = function(action) {
    if (this.canEvalAction(action)) {
        this.evalAction(action);
        this.increaseVariable(action);
        this.loseItem(action);
        this.playActionSe(action);
        this._onActionSuccess(action);
        this.onActionSuccess(action);
        this._itemAction = null;

        return true;
    } else {
        this._onActionFailure(action);
        this.onActionFailure(action);
        SoundManager.playBuzzer();

        return false;
    }
};

Scene_Item.prototype.evalAction = function(action) {
    if (action.symbol === 'use') {
        this.useItem();
    } else if (action.handler) {
        // for eval scope
        const user = this.user();
        const item = this.item();
        const actor = this.actor();
        const number = this.number();

        eval(action.handler);
    }
};

Scene_Item.prototype.increaseVariable = function(action) {
    const itemId = this.item().id;
    const data = action.variables ?
        action.variables.find(data => data.itemId === itemId) : null;

    if (data && data.variable > 0) {
        $gameVariables.setValue(data.variable, $gameVariables.value(data.variable) + this.number());
    }
};

Scene_Item.prototype.loseItem = function(action) {
    if (action.loseItem) {
        $gameParty.loseItem(this.item(), this.number());
    }
};

Scene_Item.prototype.playActionSe = function(action) {
    if (action.symbol === 'use') {
        this.playSeForItem();
    } else if (action.se && action.se.name) {
        AudioManager.playSe(action.se);
    } else {
        SoundManager.playOk();
    }
};

Scene_Item.prototype._onActionSuccess = function(action) {
    this.checkCommonEvent();
    this.checkGameover();
    this.hideActorWindow();
    this.hideNumberWindow();
    this.refreshCategoryWindow();
    this.refreshActorWindow();
    this.refreshCurrentItem();
    this.activateItemWindow();
};

Scene_Item.prototype.onActionSuccess = function(action) {
    // to be overridden by plugins
};

Scene_Item.prototype._onActionFailure = function(action) {
    this.hideActorWindow();
    this.hideNumberWindow();
};

Scene_Item.prototype.onActionFailure = function(action) {
    if (action.symbol === 'use') {
        if (this.isSingleActor()) {
            this.showItemActionsWindow();
        } else {
            this.showActorWindow();
        }
    }
};

if (Utils.RPGMAKER_NAME === 'MV') {

    Scene_Item.prototype.showActorWindow = function() {
        this.showSubWindow(this._actorWindow);
    };

    Scene_Item.prototype.hideActorWindow = function() {
        this._actorWindow.hide();
        this._actorWindow.deactivate();
    };

    Scene_Item.prototype.calcWindowHeight = function(numLines = 1, selectable = false) {
        if (selectable) {
            return Window_Selectable.prototype.fittingHeight(numLines);
        } else {
            return Window_Base.prototype.fittingHeight(numLines);
        }
    };

}

//===========================================================================
// Window_ItemList
//===========================================================================

const ItemActions_Window_ItemList_isEnabled =
    Window_ItemList.prototype.isEnabled;
Window_ItemList.prototype.isEnabled = function(item) {
    if (SceneManager._scene instanceof Scene_Item) {
        return true;
    }

    return ItemActions_Window_ItemList_isEnabled.apply(this, arguments);
};

//===========================================================================
// Window_ItemActions
//===========================================================================

class Window_ItemActions extends Window_Command {

    initialize(rect) {
        if (Utils.RPGMAKER_NAME === 'MV') {
            this._windowWidth = rect.width;
            this._windowHeight = rect.height;

            super.initialize(rect.x, rect.y);
        } else {
            super.initialize(rect);
        }
    }

    windowWidth() {
        return this._windowWidth;
    }

    windowHeight() {
        return this._windowHeight;
    }

    setup(user, item) {
        this._item = item;
        this._user = user;

        this.refresh();

        const newHeight = this.fittingHeight(Math.min(ItemActionsParams.maxVisibleRows, this._list.length), true);

        if (this._height !== newHeight) {
            const height = newHeight;
            const width = this._width;
            const x = (Graphics.boxWidth - width) / 2;
            const y = (Graphics.boxHeight - height) / 2;

            this.move(x, y, width, height);
            this.createContents();
            this.refresh();
        }

        this.select(0);

        if (Utils.RPGMAKER_NAME === 'MZ') {
            this.scrollTo(0, 0);
        }
    }

    makeCommandList() {
        if (!this._item) {
            return;
        }

        this.addActionCommands('top');
        this.addOriginalCommands();
        this.addActionCommands('bottom');
        this.addCommand(TextManager.cancel, 'cancel');
    }

    addAction(name, data) {
        const action = {
            ...data,
            categories: data.categories || ['item', 'weapon', 'armor', 'keyItem'],
        };

        if (this.checkItemCategories(action) && this.checkVisibleActions(action) && this.isActionVisible(action)) {
            this.addCommand(name, 'ok', this.checkEnabledActions(action) && this.isActionEnabled(action), action);
        }
    }

    addActionCommands(position) {
        ItemActionsParams.actions.forEach((action) => {
            if (action.position === position) {
                this.addAction(action.name, action);
            }
        });
    }

    addOriginalCommands() {
        // to be overridden by plugins
    }

    checkItemCategories(action) {
        if (!action.categories) {
            return true;
        }

        const item = this._item;

        if (DataManager.isItem(item)) {
            if (item.itypeId === 2) {
                return action.categories.includes('keyItem');
            }

            return action.categories.includes('item');
        } else if (DataManager.isWeapon(item)) {
            return action.categories.includes('weapon');
        } else if (DataManager.isArmor(item)) {
            return action.categories.includes('armor');
        }

        return false;
    }

    checkVisibleActions(action) {
        const hiddenActions = this._item.meta['hide_item_actions'] ?
            this._item.meta['hide_item_actions'].split(',').map(symbol => symbol.trim()) : [];

        return !hiddenActions.includes(action.symbol);
    }

    checkEnabledActions(action) {
        const disabledActions = this._item.meta['disable_item_actions'] ?
            this._item.meta['disable_item_actions'].split(',').map(symbol => symbol.trim()) : [];

        return !disabledActions.includes(action.symbol);
    }

    evalActionVisible(action) {
        // for eval scope
        const user = this._user;
        const item = this._item;
        let visible = true;

        eval(action.visible);

        return visible;
    }

    evalActionEnabled(action) {
        // for eval scope
        const user = this._user;
        const item = this._item;
        let enabled = true;

        eval(action.enabled);

        return enabled;
    }

    isActionVisible(action) {
        if (action.visible) {
            return this.evalActionVisible(action);
        }

        return true;
    }

    isActionEnabled(action) {
        if (action.symbol === 'use') {
            if (!this._user.canUse(this._item)) {
                return false;
            }

            const action = new Game_Action(this._user);

            action.setItemObject(this._item);

            if (action.isForFriend() || action.isForAll()) {
                return $gameParty.members().some(target => action.testApply(target));
            }
        }

        if (action.enabled) {
            return this.evalActionEnabled(action);
        }

        return true;
    }

}

//===========================================================================
// Window_ItemActor
//===========================================================================

class Window_ItemActor extends Window_MenuActor {

    setup(user, item, action) {
        this._user = user;
        this._item = item;
        this._action = action;
        this.refresh();
    }

    canUseAction(index) {
        if (this._action.symbol === 'use') {
            const action = new Game_Action(this._user);
            const actor = this.actor(index);

            action.setItemObject(this._item);

            if (action.isForFriend()) {
                return action.testApply(actor);
            }
        }

        return true;
    }

    processOk() {
        if (this.canUseAction(this.index())) {
            super.processOk();
        } else {
            this.playBuzzerSound();
        }
    }

    drawItemImage(index) {
        const actor = this.actor(index);
        const rect = this.itemRect(index);
        const width = Utils.RPGMAKER_NAME === 'MV' ?
            Window_Base._faceWidth : ImageManager.faceWidth;
        const height = Utils.RPGMAKER_NAME === 'MV' ?
            Window_Base._faceHeight : rect.height - 2;
        const isEnabled = !!this._action && this.canUseAction(index);

        this.changePaintOpacity(isEnabled);
        this.drawActorFace(actor, rect.x + 1, rect.y + 1, width, height);
        this.changePaintOpacity(true);
    }

}

if (Utils.RPGMAKER_NAME === 'MV') {

    Window_ItemActor.prototype.actor = function(index) {
        return $gameParty.members()[index];
    };

}

//===========================================================================
// Window_ItemNumber
//===========================================================================

class Window_ItemNumber extends Window_ShopNumber {

    initialize(rect) {
        if (Utils.RPGMAKER_NAME === 'MV') {
            this._windowWidth = rect.width;

            super.initialize(rect.x, rect.y, rect.height);
        } else {
            super.initialize(rect);
        }
    }

    windowWidth() {
        return this._windowWidth;
    }

    resetNumber() {
        this._number = 1;
    }

    setup(user, item, action, actor, max) {
        this._user = user;
        this._action = action;
        this._actor = actor;
        super.setup(item, max, 0);
    }

    drawTotalPrice() {
        if (this._price > 0) {
            super.drawTotalPrice();
        }
    }

}
