/*:
 * @target MZ
 * @author Aerosys
 * @plugindesc [Tier 3] [Version 1.1.2] [MV & MZ]
 * @base MK_FurnitureSystem
 * 
 * 
 * @command openBuildMenu
 * @text Open Build Menu
 * 
 * @arg category
 * @text Category (optional)
 * @desc when not given, all learned Blueprints are displayed
 * 
 * 
 * @param buildSelectWindow
 * @text Build Select Window
 * 
 * @param buildSelectWindowColumns
 * @parent buildSelectWindow
 * @text Number of Columns
 * @type number
 * @default 5
 * 
 * @param buildSelectWindowRows
 * @parent buildSelectWindow
 * @text Number of Rows
 * @type number
 * @default 1
 * 
 * @param buildSelectWindowPosition
 * @parent buildSelectWindow
 * @text Position
 * @type select
 * @option Top
 * @option Middle
 * @option Bottom
 * @default Bottom
 * 
 * @param variantIcon
 * @parent buildSelectWindow
 * @text Variant Icon
 * @type icon
 * @default 87
 * 
 * @param rotatableIcon
 * @parent buildSelectWindow
 * @text Rotatable Icon
 * @type icon
 * @default 75
 * 
 * @param a
 * @text _
 * 
 * @param materialCostsWindow
 * @text Material Costs Window
 * 
 * @param showMaterialCostsWindow
 * @parent materialCostsWindow
 * @text show?
 * @type boolean
 * @default true
 * @desc When you have no build costs in your game at all, you may want to hide this window entirely
 * 
 * @param materialCostsWindowRows
 * @parent materialCostsWindow
 * @text Number of Rows
 * @type number
 * @default 3
 * 
 * @param goldIcon
 * @parent materialCostsWindow
 * @text Gold Icon
 * @type icon
 * @default 314
 * 
 * @param staminaIcon
 * @parent materialCostsWindow
 * @text Stamina Icon
 * @type icon
 * @default 164
 * 
 * @param staminaText
 * @parent materialCostsWindow
 * @text Stamina Text
 * @default Stamina
 * 
 * @param leftSideText
 * @parent materialCostsWindow
 * @text Text (left side)
 * @default \I[%1] %2:
 * @desc Use %1 for Item Icon, %2 for Item Name
 * 
 * @param rightSideText
 * @parent materialCostsWindow
 * @text Text (right side)
 * @default \C[%1]%2 / %3
 * @desc Use %1 for Text Color, %2 for required quantity, %3 for quantity in Inventory
 * 
 * @param textWhenNoCosts
 * @parent materialCostsWindow
 * @text Text (No costs)
 * @default \C[3]Unlimited
 * @desc You may use Text Codes
 * 
 * @param textColors
 * @parent materialCostsWindow
 * @text Text Colors
 * 
 * @param textColorWhenOk
 * @parent textColors
 * @text When items in inventory
 * @type color
 * @default 3
 * 
 * @param textColorWhenNotOk
 * @parent textColors
 * @text When item is missing
 * @type color
 * @default 10
 * 
 * 
 * @param b
 * @text _
 * 
 * @param descriptionWindow
 * @text Blueprint Description Window
 * 
 * @param showDescriptionWindow
 * @parent descriptionWindow
 * @text show?
 * @type boolean
 * @default true
 * 
 * @param blueprintDescriptionWindowDrawEval
 * @parent descriptionWindow
 * @text JS: Draw
 * @type note
 * @default "const text = arguments[0];\nconst nLines = text.split('\\n').length;\nconst textHeight = nLines.clamp(1, 3) * this.lineHeight();\nconst y1 = this.contentsHeight() / 2 - textHeight / 2;\n\n(text).split('\\n').forEach((line, i) => {\n    this.drawTextEx(\n        line,\n        0,\n        i * this.lineHeight() + y1,\n        this.contentsWidth(),\n    )\n});"
 * 
 * 
 * @param c
 * @text _
 * 
 * @param commonEvents
 * @text Common Events
 * @type struct<CommonEvents>
 * @desc All Common Events are optional
 */

/*~struct~CommonEvents:
 *
 * @param onOpenMenuCommonEventId
 * @text Open Build Menu
 * @type common_event
 * 
 * @param onCloseMenuCommonEventId
 * @text Close Build Menu
 * @type common_event
 */


(function() {


const PLUGIN_NAME = 'MK_FurnitureMenu';

const reject = (reason) => {
    const message = (
        'An Error has occurred in the Plugin %1: %2 ' +
        'If the problem persists, contact the Plugin Creator.'
    ).format(PLUGIN_NAME, reason);
    throw Error(message);
}

if (!PluginManager._parameters[PLUGIN_NAME.toLowerCase()]) {
    reject((
        "Please check that this plugin's filename is \"%1.js\". " +
        "Subdirectories (e.g.: js/plugins/xy/thisPlugin.js) are not allowed."
    ).format(PLUGIN_NAME));
}

const structure = (serialized, parameterName) => {
    if (!serialized) {
        reject((
            "The Plugin Parameter \"%1\" is missing. " +
            "Please check it in the Plugin Manager. It may help to re-install this Plugin (i.e.: remove, re-add)."
        ).format(parameterName));
    }
    try {
        return JSON.parse(serialized);
    
    } catch (e) {
        reject((
            "The Plugin Parameter \"%1\" is corrupted. " +
            "Please check it in the Plugin Manager. It may help to re-install this Plugin (i.e.: remove, re-add)."
        ).format(parameterName));
    }
}

const customFunction = (body, parameterName) => {
    if (!body) {
        reject((
            'The Plugin Parameter "%1" is missing. ' +
            'Please check it in the Plugin Manager. It may help to re-install this Plugin (i.e.: remove, re-add).'
        ).format(parameterName));
    }
    try {
        return new Function(JSON.parse(body));
    
    } catch (e) {
        reject((
            'The Plugin Parameter "%1" contains an error and could not be interpreted. ' +
            'Please check it in the Plugin Manager. It may also help to re-install this Plugin (i.e.: remove, re-add). ' +
            'Cause: %2'
        ).format(parameterName, e));
    }
}

const requirePlugin = (plugin, pluginName) => {
    if (!plugin) reject('The required Plugin "%1" is missing. Please add it to the Plugin Manager.'.format(pluginName));
}

requirePlugin(MK.Furniture, 'MK_FurnitureSystem');


const params                            = PluginManager.parameters(PLUGIN_NAME);
const columns                           = Number(params.buildSelectWindowColumns) || 5;
const buildSelectWindowPosition         = (params.buildSelectWindowPosition || 'Bottom').toLowerCase();
const nRows                             = Number(params.buildSelectWindowRows) || 1;
const variantIcon                       = Number(params.variantIcon || 87);
const rotatableIcon                     = Number(params.rotatableIcon || 75);
const textColorWhenOk                   = Number(params.textColorWhenOk);
const textColorWhenNotOk                = Number(params.textColorWhenNotOk);
const showMaterialCostsWindow           = 'true' == params.showMaterialCostsWindow;
const materialCostsWindowRows           = Number(params.materialCostsWindowRows) || 3;
const showDescriptionWindow             = 'true' == params.showDescriptionWindow;
const blueprintDescriptionWindowDrawEval = customFunction(
                                                params.blueprintDescriptionWindowDrawEval,
                                                'Description Window Draw',
);
const commonEvents                      = params.commonEvents && structure(params.commonEvents, 'Common Events') || { };
const onOpenMenuCommonEventId           = Number(commonEvents.onOpenMenuCommonEventId);
const onCloseMenuCommonEventId          = Number(commonEvents.onCloseMenuCommonEventId);
const downscaleFactors                  = [1, 2, 3, 4];

MK.FurnitureMenu                    = { };
MK.FurnitureMenu.goldIcon           = Number(params.goldIcon);
MK.FurnitureMenu.staminaIcon        = Number(params.staminaIcon);
MK.FurnitureMenu.staminaText        = params.staminaText;
MK.FurnitureMenu.leftSideText       = params.leftSideText;
MK.FurnitureMenu.rightSideText      = params.rightSideText;
MK.FurnitureMenu.textWhenNoCosts    = params.textWhenNoCosts;


const imageCache = { };

function getDownscaledImage(folder, name, factor, f) {
    const key = '%1%2_%3'.format(folder, name, factor);
    
    if (imageCache[key]) {
        f(imageCache[key]);
    } else {
        const bitmap = ImageManager.loadBitmap(folder, name);
        bitmap.addLoadListener(() => {
            const downscaledBitmap = downscale(bitmap, factor);
            imageCache[key] = downscaledBitmap;

            f(downscaledBitmap);
        });
    }
}

function downscale(bitmap, factor) {
    if (factor == 1) return bitmap;
    
    const source = bitmap._canvas || bitmap._image;
    const toReturn = new Bitmap(bitmap.width / factor, bitmap.height / factor);

    toReturn.clearRect(0, 0, toReturn.width, toReturn.height);
    toReturn.context.globalCompositeOperation = 'source-over';

    toReturn.context.drawImage(
        source,
        0,
        0,
        bitmap.width,
        bitmap.height,
        0,
        0,
        bitmap.width / factor,
        bitmap.height / factor,
    );
    return toReturn;
}


// =====================================================================================
// Window Furniture Select
// =====================================================================================

function Window_FurnitureSelect() {
    this.initialize(...arguments);
}

Window_FurnitureSelect.prototype = Object.create(Window_Selectable.prototype);
Window_FurnitureSelect.prototype.constructor = Window_FurnitureSelect;

Window_FurnitureSelect.prototype.initialize = function(/*rectangle*/) {
    Window_Selectable.prototype.initialize.apply(this, arguments);

    this.createCancelButton();

    const tileset = $dataTilesets[$gameMap.tilesetId()];

    if (tileset) {
        tileset.tilesetNames.forEach((name, index) => {
            downscaleFactors.forEach(factor => {
                this['tilesetBitmaps' + factor] = this['tilesetBitmaps' + factor] || [ ];

                if (name) {
                    getDownscaledImage(
                        'img/tilesets/',
                        name,
                        factor,
                        (bitmap) => {
                            this['tilesetBitmaps' + factor][index] = bitmap;
                            this.refresh();
                        },
                    );
                } else {
                    this['tilesetBitmaps' + factor][index] = null;
                }
            });
        });
    }
}

Window_FurnitureSelect.prototype.setCategory = function(category) {
    if (this._category != category) {
        this._category = category;
        this.refresh();
    }
}

Window_FurnitureSelect.prototype.createCancelButton = function() {
    if (ConfigManager.touchUI) {
        this.updatePadding();
        
        this._cancelButton = new Sprite_Button('cancel');
        this._cancelButton.x = this.width - this._cancelButton.width;
        this._cancelButton.y = 0 - this._cancelButton.height - 2;

        this.addChild(this._cancelButton);
    }
}

Window_FurnitureSelect.prototype.refresh = function() {
    this._data = $gameParty.learnedFurniturePlans()
        .filter(blueprint => this.includes(blueprint))
        .map(blueprint => this.mapBlueprint(blueprint));
    
    Window_Selectable.prototype.refresh.call(this);
}

Window_FurnitureSelect.prototype.includes = function(blueprint) {
    return (
        blueprint &&
        (!this._category || this._category == blueprint.category)
    );
}

Window_FurnitureSelect.prototype.mapBlueprint = function(blueprint) {
    const result    = { };
    const assetInfo = MK.MapManipulation.getAssetInfo(blueprint.sourceMapName, blueprint.regionId);

    for (let attr in assetInfo) { result[attr] = assetInfo[attr] };
    for (let attr in blueprint) { result[attr] = blueprint[attr] };
    
    return result;
}

Window_FurnitureSelect.prototype.maxCols = function() {
    return columns;
}

Window_FurnitureSelect.prototype.maxItems = function() {
    return this._data ? this._data.length : 1;
}

Window_FurnitureSelect.prototype.itemHeight = function() {
    return this.itemWidth();
}

Window_FurnitureSelect.prototype.itemAt = function(index) {
    return this._data && this._data[index];
}

Window_FurnitureSelect.prototype.item = function() {
    return this.itemAt(this.index());
}

Window_FurnitureSelect.prototype.drawItem = function(index) {
    const asset = this.itemAt(index);
    const rectangle = this.itemRect(index);

    asset && this.performDraw(rectangle, asset);
}

Window_FurnitureSelect.prototype.performDraw = function(rectangle, asset) {
    const downscaleFactor = this.determineDownscaleFactor(rectangle, asset);
    const w = asset.width * $gameMap.tileWidth() / downscaleFactor;
    const h = asset.height * $gameMap.tileHeight() / downscaleFactor;
    
    const rectangle2 = new Rectangle(
        rectangle.width / 2 - w / 2 + rectangle.x,
        rectangle.height / 2 - h / 2 + rectangle.y,
        w,
        h,
    );

    this.drawEvent(rectangle2, asset, downscaleFactor);
    this.drawInfoIcons(rectangle, asset);
}

Window_FurnitureSelect.prototype.determineDownscaleFactor = function(rectangle, asset) {
    return downscaleFactors.find(f => 
        asset.width * $gameMap.tileWidth() / f < rectangle.width &&
        asset.height * $gameMap.tileHeight() / f < rectangle.height
    ) || Math.max(...downscaleFactors);
}

Window_FurnitureSelect.prototype.drawEvent = function(rectangle, asset, downscaleFactor) {
    const sourceMap = MK.MapManipulation.getSourceMap(asset.sourceMapName);
    if (!sourceMap) return;
    
    const event = sourceMap.events
        .filter(Boolean)
        .find(event => event.x == asset.x && event.y == asset.y);

    if (event) {
        const page              = event.pages[0];
        const characterIndex    = page.image.characterIndex;
        const characterName     = page.image.characterName;
        const direction         = page.image.direction;
        const pattern           = page.image.pattern;
        const tileId            = page.image.tileId;

        if (characterName) {
            getDownscaledImage('img/characters/', characterName, downscaleFactor,
                (bitmap) => {
                    const isBigCharacter    = ImageManager.isBigCharacter(characterName);
                    const isObjectCharacter = ImageManager.isObjectCharacter(characterName);
                    const pw                = bitmap.width / (isBigCharacter ? 3 : 12);
                    const ph                = bitmap.height / (isBigCharacter ? 4 : 8);

                    const characterBlockX   = isBigCharacter
                                                ? 0
                                                : Math.floor(characterIndex % 4) * 3;
                    const characterBlockY   = isBigCharacter
                                                ? 0
                                                : Math.floor(characterIndex / 4) * 4;
                    const characterPatternY = (direction - 2) / 2;
                    const sx                = (characterBlockX + pattern) * pw;
                    const sy                = (characterBlockY + characterPatternY) * ph;

                    const dx = (0.5 * $gameMap.tileWidth() - pw / 2) + rectangle.x;
                    const dy = $gameMap.tileHeight() - ph - (isObjectCharacter ? 0 : 6) + rectangle.y;

                    this.contents.blt(
                        bitmap,
                        sx,
                        sy,
                        pw,
                        ph,
                        dx,
                        dy,
                    );
                }
            )
        }
        if (tileId) {
            const pw = $gameMap.tileWidth() / downscaleFactor;
            const ph = $gameMap.tileHeight() / downscaleFactor;
            const sx = ((Math.floor(tileId / 128) % 2) * 8 + (tileId % 8)) * pw;
            const sy = (Math.floor((tileId % 256) / 8) % 16) * ph;
            const setNumber = Math.floor(tileId / 256) + 5;
            const bitmap = this['tilesetBitmaps' + downscaleFactor][setNumber];

            if (bitmap) {
                this.contents.blt(
                    bitmap,
                    sx,
                    sy,
                    pw,
                    ph,
                    rectangle.x,
                    rectangle.y,
                );
            }
        }
    }
}

Window_FurnitureSelect.prototype.blt = function(tilesetNumber, sx, sy, w, h, dx, dy, factor) {
    const container = this['tilesetBitmaps' + factor];
    const tilesetBitmap = container && container[tilesetNumber];

    if (tilesetBitmap) {
        this.contents.blt(
            tilesetBitmap,
            sx,
            sy,
            w,
            h,
            dx,
            dy,
        );
    } else {
        this.contents.clearRect(dx, dy, w, h);
    }
}

Window_FurnitureSelect.prototype.drawInfoIcons = function(rectangle, asset) {
    const hasVariants = this.hasVariants(asset);
    const isRotatable = this.isRotatable(asset);

    const iconWidth     = Utils.RPGMAKER_NAME == 'MZ' ? ImageManager.iconWidth : Window_Base._iconWidth;
    const iconHeight    = Utils.RPGMAKER_NAME == 'MZ' ? ImageManager.iconHeight : Window_Base._iconHeight;

    if (hasVariants && isRotatable) {
        this.drawIcon(variantIcon, rectangle.x, rectangle.y + rectangle.height - iconHeight);
        this.drawIcon(rotatableIcon, rectangle.x + iconWidth, rectangle.y + rectangle.height - iconHeight);
    }
    if (hasVariants && !isRotatable) {
        this.drawIcon(variantIcon, rectangle.x, rectangle.y + rectangle.height - iconHeight);
    }
    if (!hasVariants && isRotatable) {
        this.drawIcon(rotatableIcon, rectangle.x, rectangle.y + rectangle.height - iconHeight);
    }
}

Window_FurnitureSelect.prototype.hasVariants = function(asset) {
    const info = MK.MapManipulation.getAssetInfo(asset.sourceMapName, asset.regionId);
    return info && info.n > 1;
}

Window_FurnitureSelect.prototype.isRotatable = function(asset) {
    return asset.rotatable;
}

Window_FurnitureSelect.prototype.update = function() {
    Window_Selectable.prototype.update.call(this);

    if (this.index() !== this._lastIndex) {
        this._lastIndex = this.index();
        this._materialsWindow && this._materialsWindow.setBlueprint(this.item());
        this._descriptionWindow && this._descriptionWindow.setText(this.item() && this.item().description);
    }
}

Window_FurnitureSelect.prototype.isCurrentItemEnabled = function() {
    return this.isEnabled(this.item());
}

Window_FurnitureSelect.prototype.isEnabled = function(blueprint) {
    return blueprint && MK.Furniture.canBuild(blueprint.blueprintName);
}


// =====================================================================================
// Window Materials
// =====================================================================================

function Window_RequiredMaterials() {
    this.initialize(...arguments);
}

Window_RequiredMaterials.prototype = Object.create(Window_Base.prototype);
Window_RequiredMaterials.prototype.constructor = Window_RequiredMaterials;

Window_RequiredMaterials.prototype.setBlueprint = function(blueprint) {
    if (this.blueprint != blueprint) {
        this.blueprint = blueprint;

        this.refresh();
    }
}

Window_RequiredMaterials.prototype.refresh = function() {
    this.contents && this.contents.clear();
    this.contentsBack && this.contentsBack.clear();
    
    this.draw();
}

Window_RequiredMaterials.prototype.draw = function() {
    if (!this.blueprint) return;

    if (this.isUnlimited()) {
        this.drawUnlimited();
    } else {
        this.drawWithCosts();
    }
}

Window_RequiredMaterials.prototype.drawUnlimited = function() {
    const text = MK.FurnitureMenu.textWhenNoCosts || 'Unlimited';

    this.drawTextEx(
        text,
        this.contentsWidth() / 2 - this.getTextWidth(text) / 2,
        this.contentsHeight() / 2 - this.lineHeight() / 2,
    );
}

Window_RequiredMaterials.prototype.drawWithCosts = function() {
    const n = this.blueprint.requiredItems.length
                + (this.blueprint.gold ? 1 : 0)
                + (this.blueprint.staminaCost ? 1 : 0);
    
    const height    = n * this.lineHeight();
    const y1        = this.contentsHeight() / 2 - height / 2;
    
    this.blueprint.requiredItems.forEach((entry, index) => {
        const item = $dataItems[entry.itemId];
        if (!item) return;

        const requiredQuantity = Math.round(entry.quantity * $gameSystem.buildCostModifier());
        const availableQuantity = $gameParty.numItems(item);

        this.drawLine(
            index * this.lineHeight() + y1,
            item.iconIndex,
            item.name,
            requiredQuantity,
            availableQuantity,
        );
    });

    if (this.blueprint.gold) {
        const index = this.blueprint.requiredItems.length;
        const requiredQuantity = Math.round(this.blueprint.gold * $gameSystem.buildCostModifier());
        const availableQuantity = $gameParty.gold();

        this.drawLine(
            index * this.lineHeight() + y1,
            MK.FurnitureMenu.goldIcon,
            TextManager.currencyUnit,
            requiredQuantity,
            availableQuantity,
        );
    }

    if (this.blueprint.staminaCost) {
        const index             = this.blueprint.requiredItems.length + (this.blueprint.gold ? 1 : 0);
        const requiredQuantity  = Math.round(this.blueprint.staminaCost * $gameSystem.buildCostModifier());
        const availableQuantity = MK.Sandbox.currentStamina();

        this.drawLine(
            index * this.lineHeight() + y1,
            MK.FurnitureMenu.staminaIcon,
            MK.FurnitureMenu.staminaText || 'Stamina',
            requiredQuantity,
            availableQuantity,
        );
    }
}

Window_RequiredMaterials.prototype.drawLine = function(y, iconIndex, itemName, requiredQuantity, availableQuantity) {
    const text1 = MK.FurnitureMenu.leftSideText.format(iconIndex, itemName);
    const text2 = MK.FurnitureMenu.rightSideText.format(
        this.getTextColor(requiredQuantity <= availableQuantity),
        requiredQuantity,
        availableQuantity,
    );

    this.drawTextEx(text1, 5, y);
    this.drawTextEx(text2, this.contentsWidth() - this.getTextWidth(text2) - 10, y);
}

Window_RequiredMaterials.prototype.isUnlimited = function() {
    return !this.blueprint || (
        this.blueprint.requiredItems.every(entry =>
            Math.round(entry.quantity * $gameSystem.buildCostModifier()) <= 0
        ) &&
        Math.round(this.blueprint.gold * $gameSystem.buildCostModifier()) <= 0 &&
        Math.round(this.blueprint.staminaCost * $gameSystem.buildCostModifier()) <= 0
    );
}

Window_RequiredMaterials.prototype.getTextWidth = function(text) {
    return 'MZ' == Utils.RPGMAKER_NAME
        ? this.textSizeEx(text).width
        : this.drawTextEx(text, 0, this.contents.height);
}

Window_RequiredMaterials.prototype.getTextColor = function(ok) {
    return ok ? textColorWhenOk : textColorWhenNotOk;
}

const alias_GameParty_gainItem = Game_Party.prototype.gainItem;
Game_Party.prototype.gainItem = function(/* arguments */) {
    alias_GameParty_gainItem.apply(this, arguments);

    if (SceneManager._scene._furnitureRequiredMaterialsWindow)
        SceneManager._scene._furnitureRequiredMaterialsWindow.refresh();
}

const alias_GameParty_gainGold = Game_Party.prototype.gainGold;
Game_Party.prototype.gainGold = function(/* arguments */) {
    alias_GameParty_gainGold.apply(this, arguments);

    if (SceneManager._scene._furnitureRequiredMaterialsWindow)
        SceneManager._scene._furnitureRequiredMaterialsWindow.refresh();
}

const alias_GameVariables_setValue = Game_Variables.prototype.setValue;
Game_Variables.prototype.setValue = function(/* arguments */) {
    alias_GameVariables_setValue.apply(this, arguments);

    if (SceneManager._scene._furnitureRequiredMaterialsWindow)
        SceneManager._scene._furnitureRequiredMaterialsWindow.refresh();
}


// =====================================================================================
// Window Description
// =====================================================================================

function Window_BlueprintDescription() {
    this.initialize(...arguments);
}

Window_BlueprintDescription.prototype = Object.create(Window_Base.prototype);
Window_BlueprintDescription.prototype.constructor = Window_BlueprintDescription;

Window_BlueprintDescription.prototype.initialize = function(/* arguments */) {
    Window_Base.prototype.initialize.apply(this, arguments);

    this.refresh();
}

Window_BlueprintDescription.prototype.setText = function(text) {
    if (this._text != text) {
        this._text = text;
        this.refresh();
    }
}

Window_BlueprintDescription.prototype.refresh = function() {
    this.contents && this.contents.clear();
    this.contentsBack && this.contentsBack.clear();

    blueprintDescriptionWindowDrawEval.call(this, this._text || '');
}


// =====================================================================================
// MK
// =====================================================================================

MK.Furniture.openBuildMenu = function(category) {
    if (!MK.Furniture.canOpenBuildMenu()) return;
    
    const window = SceneManager._scene._furnitureSelectWindow;
    window.setCategory(category);
    window.refresh();
    window.show();
    window.activate();
    if (!window.item()) window.select(0);

    const materialsWindow = SceneManager._scene._furnitureRequiredMaterialsWindow;
    showMaterialCostsWindow && materialsWindow.refresh();
    showMaterialCostsWindow && materialsWindow.show();

    const descriptionWindow = SceneManager._scene._furnitureDescriptionWindow;
    showDescriptionWindow && descriptionWindow.refresh();
    showDescriptionWindow && descriptionWindow.show();
    
    $gameTemp.selectingFromFurnitureMenu = true;

    onOpenMenuCommonEventId && $gameTemp.reserveCommonEvent(onOpenMenuCommonEventId);
}

MK.Furniture.canOpenBuildMenu = function() {
    return (
        SceneManager._scene instanceof Scene_Map &&
        !MK.Furniture.isBuildMode() &&
        !SceneManager._scene._furnitureSelectWindow.isOpenAndActive()
    );
}


// =====================================================================================
// Scene Map
// =====================================================================================

const alias_SceneMap_createDisplayObjects = Scene_Map.prototype.createDisplayObjects;
Scene_Map.prototype.createDisplayObjects = function() {
    alias_SceneMap_createDisplayObjects.call(this);

    this.createFurnitureSelectWindow();
    this.createFurnitureRequiredMaterialsWindow();
    this.createFurnitureDescriptionWindow();

    this._furnitureSelectWindow._materialsWindow = this._furnitureRequiredMaterialsWindow;
    this._furnitureSelectWindow._descriptionWindow = this._furnitureDescriptionWindow;
}

const alias_SceneMap_callMenu = Scene_Map.prototype.callMenu;
Scene_Map.prototype.callMenu = function() {
    
    if ($gameTemp.selectingFromFurnitureMenu) {
        this.closeFurnitureMenu();
        this.menuCalling = false;
    } else {
        alias_SceneMap_callMenu.call(this);
    }
}

Scene_Map.prototype.createFurnitureSelectWindow = function() {
    const rectangle = this.furnitureSelectWindowRectangle();

    if ('MZ' == Utils.RPGMAKER_NAME) {
        this._furnitureSelectWindow = new Window_FurnitureSelect(rectangle);
    } else {
        this._furnitureSelectWindow = new Window_FurnitureSelect(
            rectangle.x,
            rectangle.y,
            rectangle.width,
            rectangle.height,
        );
    }
    this._furnitureSelectWindow.refresh();

    if ($gameTemp.selectingFromFurnitureMenu) {
        this._furnitureSelectWindow.activate();
    } else {
        this._furnitureSelectWindow.hide();
    }
    this._furnitureSelectWindow.setHandler('ok', this.onFurnitureOkay.bind(this));
    this._furnitureSelectWindow.setHandler('cancel', this.closeFurnitureMenu.bind(this));
    this.addChild(this._furnitureSelectWindow);
}

Scene_Map.prototype.createFurnitureRequiredMaterialsWindow = function() {
    const rectangle = this.furnitureRequiredMaterialsWindowRectangle();

    if ('MZ' == Utils.RPGMAKER_NAME) {
        this._furnitureRequiredMaterialsWindow = new Window_RequiredMaterials(rectangle);
    } else {
        this._furnitureRequiredMaterialsWindow = new Window_RequiredMaterials(
            rectangle.x,
            rectangle.y,
            rectangle.width,
            rectangle.height,
        );
    }
    if (!$gameTemp.selectingFromFurnitureMenu) {
        this._furnitureRequiredMaterialsWindow.hide();
    }
    this.addChild(this._furnitureRequiredMaterialsWindow);
}

Scene_Map.prototype.createFurnitureDescriptionWindow = function() {
    const rectangle = this.furnitureDescriptionWindowRectangle();

    if ('MZ' == Utils.RPGMAKER_NAME) {
        this._furnitureDescriptionWindow = new Window_BlueprintDescription(rectangle);
    } else {
        this._furnitureDescriptionWindow = new Window_BlueprintDescription(
            rectangle.x,
            rectangle.y,
            rectangle.width,
            rectangle.height,
        );
    }
    if (!$gameTemp.selectingFromFurnitureMenu) {
        this._furnitureDescriptionWindow.hide();
    }
    this.addChild(this._furnitureDescriptionWindow);
}

Scene_Map.prototype.onFurnitureOkay = function() {
    this._furnitureSelectWindow.hide();
    this._furnitureDescriptionWindow.hide();

    const blueprint = this._furnitureSelectWindow.item();
    blueprint && MK.Furniture.startBuildMode(blueprint.blueprintName);
    
    $gameTemp.selectingFromFurnitureMenu = false;
    $gameTemp.selectedFromFurnitureMenu = true;
}

Scene_Map.prototype.closeFurnitureMenu = function() {
    this._furnitureSelectWindow.deactivate();
    this._furnitureSelectWindow.hide();
    this._furnitureRequiredMaterialsWindow.hide();
    this._furnitureDescriptionWindow.hide();

    $gameTemp.selectingFromFurnitureMenu = false;
    onCloseMenuCommonEventId && $gameTemp.reserveCommonEvent(onCloseMenuCommonEventId);
}

// Rectangles

Scene_Map.prototype.furnitureSelectWindowRectangle = function() {
    return new Rectangle(
        this.furnitureSelectWindowLeft(),
        this.furnitureSelectWindowTop(),
        this.furnitureSelectWindowWidth(),
        this.furnitureSelectWindowHeight(),
    );
}

Scene_Map.prototype.furnitureSelectWindowLeft = function() {
    return 10;
}

Scene_Map.prototype.furnitureSelectWindowTop = function() {
    
    switch (buildSelectWindowPosition) {
        case 'top':
            return Utils.RPGMAKER_NAME == 'MZ' ? this.buttonAreaHeight() + 10 : 10;
        case 'middle':
            return Graphics.boxHeight / 2 - this.furnitureSelectWindowHeight() / 2;
        case 'bottom':
        default:
            if (!showMaterialCostsWindow && !showDescriptionWindow) {
                return Graphics.boxHeight - this.furnitureSelectWindowHeight() - this.furnitureSelectWindowLeft();
            }
            return this.furnitureRequiredMaterialsWindowTop() - this.furnitureSelectWindowHeight();
    }
}

Scene_Map.prototype.furnitureSelectWindowWidth = function() {
    return Graphics.boxWidth - 2 * this.furnitureSelectWindowLeft();
}

Scene_Map.prototype.furnitureSelectWindowHeight = function() {
    const width = this.furnitureSelectWindowWidth();
    const padding = 'MZ' == Utils.RPGMAKER_NAME
        ? $gameSystem.windowPadding()
        : Window_FurnitureSelect.prototype.standardPadding();
    
    return nRows * Math.ceil(width / columns) + 2 * (padding - 2);
}

Scene_Map.prototype.furnitureRequiredMaterialsWindowRectangle = function() {
    return new Rectangle(
        this.furnitureRequiredMaterialsWindowLeft(),
        this.furnitureRequiredMaterialsWindowTop(),
        this.furnitureRequiredMaterialsWindowWidth(),
        this.furnitureRequiredMaterialsWindowHeight(),
    );
}

Scene_Map.prototype.furnitureRequiredMaterialsWindowLeft = function() {
    return this.furnitureSelectWindowLeft();
}

Scene_Map.prototype.furnitureRequiredMaterialsWindowTop = function() {
    return Graphics.boxHeight - this.furnitureRequiredMaterialsWindowHeight() - this.furnitureSelectWindowLeft();
}

Scene_Map.prototype.furnitureRequiredMaterialsWindowWidth = function() {
    return Math.floor(this.furnitureSelectWindowWidth() * 0.4);
}

Scene_Map.prototype.furnitureRequiredMaterialsWindowHeight = function() {
    return 'MZ' == Utils.RPGMAKER_NAME
        ? this.calcWindowHeight(materialCostsWindowRows)
        : new Window_Base(0, 0, 0, 0).fittingHeight(materialCostsWindowRows);
}

Scene_Map.prototype.furnitureDescriptionWindowRectangle = function() {
    return new Rectangle(
        this.furnitureRequiredMaterialsWindowLeft() + this.furnitureRequiredMaterialsWindowWidth(),
        this.furnitureRequiredMaterialsWindowTop(),
        this.furnitureSelectWindowWidth() - this.furnitureRequiredMaterialsWindowWidth(),
        this.furnitureRequiredMaterialsWindowHeight(),
    );
}

const alias_GamePlayer_canMove = Game_Player.prototype.canMove;
Game_Player.prototype.canMove = function() {
    return (
        alias_GamePlayer_canMove.call(this) &&
        !$gameTemp.selectingFromFurnitureMenu
    );
}

const alias_Furniture_quitBuildMode = MK.Furniture.quitBuildMode;
MK.Furniture.quitBuildMode = function() {
    alias_Furniture_quitBuildMode.call(this);

    if ($gameTemp.selectedFromFurnitureMenu) {
        const category = SceneManager._scene._furnitureSelectWindow._category
        MK.Furniture.openBuildMenu(category);
        $gameTemp.selectedFromFurnitureMenu = false;
    }
}

const alias_Furniture_canEnableBuildMode = MK.Furniture.canEnableBuildMode;
MK.Furniture.canEnableBuildMode = function() {
    return (
        alias_Furniture_canEnableBuildMode.call(this) &&
        !SceneManager._scene._furnitureSelectWindow.isOpenAndActive()
    );
}

const alias_GameMap_isEventRunning = Game_Map.prototype.isEventRunning;
Game_Map.prototype.isEventRunning = function() {
    return (
        alias_GameMap_isEventRunning.call(this) ||
        (
            SceneManager._scene._furnitureSelectWindow &&
            SceneManager._scene._furnitureSelectWindow.isOpenAndActive()
        )
    );
}


if (PluginManager.registerCommand) {
    
    PluginManager.registerCommand(PLUGIN_NAME, 'openBuildMenu', args => {
        MK.Furniture.openBuildMenu(args.category);
    });
}


})();
