// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com


package engine.objects;

import engine.Enum;
import engine.Enum.ItemContainerType;
import engine.Enum.ItemType;
import engine.Enum.OwnerType;
import engine.gameManager.*;
import engine.loot.ModTableEntry;
import engine.loot.ModTypeTableEntry;
import engine.net.ItemProductionManager;
import engine.net.ItemQueue;
import engine.net.client.ClientConnection;
import engine.net.client.msg.ErrorPopupMsg;
import engine.powers.EffectsBase;
import engine.server.MBServerStatics;
import org.joda.time.DateTime;
import org.pmw.tinylog.Logger;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

public class ItemFactory {

    public static void fillInventory(PlayerCharacter pc, int templateID, int count) {

        if (pc == null)
            return;

        int max = 20;
        CharacterItemManager itemManager = pc.charItemManager;

        ItemTemplate template = ItemTemplate.templates.get(templateID);

        if (count > max)
            count = max;

        ClientConnection cc = pc.getClientConnection();

        if (itemManager == null || template == null || cc == null)
            return;

        boolean worked;

        for (int i = 0; i < count; i++) {
            worked = false;

            if (!itemManager.hasRoomInventory(template.item_wt)) {
                if (pc != null)
                    ChatManager.chatSystemInfo(pc, "You can not carry any more of that item.");
                break;
            }

            Item item = new Item(templateID);
            item.ownerID = pc.getObjectUUID();
            item.ownerType = OwnerType.PlayerCharacter;
            item.containerType = ItemContainerType.INVENTORY;

            try {
                item = DbManager.ItemQueries.PERSIST(item);
                worked = true;
            } catch (Exception e) {
                Logger.error(e);
            }
            if (worked) {
                itemManager.addItemToInventory(item);
            }
        }
        itemManager.updateInventory();
    }

    public static Item fillForge(NPC npc, PlayerCharacter pc, int itemsToRoll, int templateID, int pToken, int sToken, String customName) {

        String prefixString = "";
        String suffixString = "";
        if (npc == null)
            return null;

        boolean useWarehouse = false;

        ItemTemplate template = ItemTemplate.templates.get(templateID);

        if (template == null)
            return null;

        Building forge = npc.getBuilding();

        if (forge == null)
            return null;

        if (!npc.charItemManager.hasRoomInventory(template.item_wt)) {
            if (pc != null)
                ErrorPopupMsg.sendErrorPopup(pc, 21);
            return null;
        }

        Zone zone = npc.getBuilding().getParentZone();

        if (zone == null)
            return null;

        City city = City.getCity(zone.playerCityUUID);

        if (city == null)
            return null;

        MobLoot ml = null;
        city.transactionLock.writeLock().lock();

        try {
            Warehouse cityWarehouse = city.warehouse;

            if (cityWarehouse != null && forge.assetIsProtected())
                useWarehouse = true;
            // ROLL BANE SCROLL.

            if (templateID > 910010 && templateID < 910019) {
                ConcurrentHashMap<Enum.ResourceType, Integer> resources = cityWarehouse.resources;


                int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, template.item_value);
                int overdraft = BuildingManager.GetOverdraft(forge, template.item_value);

                if (overdraft > 0 && !useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + " " + template.item_base_name);
                    return null;
                }

                if (overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + " " + template.item_base_name);
                    return null;
                }

                if (overdraft > resources.get(Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + " " + template.item_base_name);
                    return null;
                }

                if (!forge.transferGold(-buildingWithdraw, false)) {
                    overdraft += buildingWithdraw;

                    if (!useWarehouse) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + template.item_base_name);
                        return null;
                    } else {
                        if (overdraft > resources.get(Enum.ResourceType.GOLD)) {
                            ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + template.item_base_name);
                            return null;
                        }
                    }
                }

                if (overdraft > 0)
                    if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GOLD, overdraft, true)) {
                        Logger.error("Warehouse for city " + cityWarehouse.city.getName() + " Failed to Create Item." + template.item_base_name);
                        return null;
                    }

                ml = new MobLoot(npc, template, false);

                ml.containerType = Enum.ItemContainerType.FORGE;
                ml.setValue(0);
                ml.loadEnchantments();

                float time;
                float rank = npc.getBuilding().getRank() - 1;
                float rate = (float) (2.5 * rank);
                time = (20 - rate);
                time *= MBServerStatics.ONE_MINUTE;

                if (ml.templateID > 910010 && ml.templateID < 910019) {
                    rank = ml.templateID - 910010;
                    time = rank * 60 * 60 * 3 * 1000;

                }

                // No job is submitted, as object's upgradetime field
                // is used to determin whether or not an object has
                // compelted rolling.  The game object exists previously
                // to this, not when 'compelte' is pressed.
                long upgradeTime = System.currentTimeMillis() + (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()));

                DateTime dateTime = new DateTime();
                dateTime = dateTime.withMillis(upgradeTime);
                ml.setDateToUpgrade(upgradeTime);

                npc.addItemToForge(ml);

                int playerID = 0;

                if (pc != null)
                    playerID = pc.getObjectUUID();
                DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(ml.getObjectUUID(), npc.getObjectUUID(), ml.templateID, dateTime, "", "", "", false, playerID);
                ProducedItem pi = new ProducedItem(ml.getObjectUUID(), npc.getObjectUUID(), ml.templateID, dateTime, false, "", "", "", playerID);
                pi.setProducedItemID(ml.getObjectUUID());
                pi.setAmount(itemsToRoll);
                pi.setRandom(false);

                ItemQueue produced = ItemQueue.borrow(pi, (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue())));
                ItemProductionManager.send(produced);

                return ml;
            }

            int galvorAmount = 0;
            int wormwoodAmount = 0;
            int prefixCost = 0;
            int suffixCost = 0;

            if (template.item_type.equals(ItemType.WEAPON) && template.item_skill_required.isEmpty() == false &&
                    template.item_skill_required.values().iterator().next() == 110) {
                switch (template.item_skill_used) {
                    case "Bow":
                    case "Crossbow":
                    case "Spear":
                    case "Pole Arm":
                    case "Staff":
                        wormwoodAmount = 20;
                        break;
                    case "Axe":
                    case "Dagger":
                    case "Sword":
                    case "Hammer":
                    case "Unarmed Combat":

                        if (ItemTemplate.isTwoHanded(template))
                            galvorAmount = 20;
                        else
                            galvorAmount = 10;
                        break;
                }
            }

            if (galvorAmount > 0 || wormwoodAmount > 0)
                if (!useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "This item requires resources to roll! Please make sure the forge is protected to access the warehouse." + template.item_base_name);
                    return null;

                }

            if (galvorAmount > 0) {
                if (Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GALVOR)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + template.item_base_name);
                    return null;
                }

                if (cityWarehouse.resources.get(Enum.ResourceType.GALVOR) < galvorAmount) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + template.item_base_name);
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.WORMWOOD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Wormwood is locked." + template.item_base_name);
                    return null;
                }

                if (cityWarehouse.resources.get(Enum.ResourceType.WORMWOOD) < wormwoodAmount) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Wormwood in warehouse to roll this item." + template.item_base_name);
                    return null;
                }
            }
            ConcurrentHashMap<Enum.ResourceType, Integer> suffixResourceCosts = null;
            ConcurrentHashMap<Enum.ResourceType, Integer> prefixResourceCosts = null;

            EffectsBase prefix = null;

            if (pToken != 0) {

                if (!useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Forge cannot access warehouse! Check to make sure forge is protected." + template.item_base_name);
                    return null;
                }
                prefix = PowersManager.getEffectByToken(pToken);
                if (prefix == null)
                    return null;
                EffectsBase prefixValue = PowersManager.getEffectByIDString(prefix.getIDString() + 'A');
                if (prefixValue == null)
                    return null;

                int baseCost = template.item_value;
                int effectCost = (int) prefixValue.getValue();
                int total = baseCost * 10 + effectCost;

                prefixCost = effectCost;
                int overdraft = BuildingManager.GetOverdraft(forge, total);

                if (overdraft > 0 && !useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                    return null;
                }

                if (overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                    return null;
                }

                if (overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                    return null;
                }
                prefixResourceCosts = prefix.getResourcesForEffect();

                for (Enum.ResourceType ibResources : prefixResourceCosts.keySet()) {
                    int warehouseAmount = cityWarehouse.resources.get(ibResources);
                    int creationAmount = prefixResourceCosts.get(ibResources);

                    if (warehouseAmount < creationAmount)
                        return null;
                }
            }
            EffectsBase suffix = null;

            if (sToken != 0) {

                if (!useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Forge cannot access warehouse! Check to make sure forge is protected." + template.item_base_name);
                    return null;
                }
                suffix = PowersManager.getEffectByToken(sToken);
                if (suffix == null)
                    return null;
                EffectsBase suffixValue = PowersManager.getEffectByIDString(suffix.getIDString() + 'A');
                if (suffixValue == null)
                    return null;
                suffixResourceCosts = suffix.getResourcesForEffect();
                int baseCost = template.item_value;
                int effectCost = (int) suffixValue.getValue();
                suffixCost = effectCost;
                int total = baseCost * 10 + effectCost;


                //	int buildingWithdraw = Building.GetWithdrawAmountForRolling(forge, total);
                int overdraft = BuildingManager.GetOverdraft(forge, total);

                if (overdraft > 0 && !useWarehouse) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                    return null;
                }

                if (overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                    return null;
                }

                if (overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                    return null;
                }


                for (Enum.ResourceType ibResources : suffixResourceCosts.keySet()) {
                    int warehouseAmount = cityWarehouse.resources.get(ibResources);
                    int creationAmount = suffixResourceCosts.get(ibResources);

                    if (warehouseAmount < creationAmount)
                        return null;
                }
            }

            //Check if Total suffix and prefix costs + itemCost can be withdrawn.

            int costToCreate = suffixCost + prefixCost + template.item_value;
            int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, costToCreate);

            int overdraft = BuildingManager.GetOverdraft(forge, costToCreate);

            if (overdraft > 0 && !useWarehouse) {
                if (pc != null)
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                return null;
            }

            if (overdraft > 0 && useWarehouse && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                if (pc != null)
                    ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                return null;
            }

            if (useWarehouse && overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {
                if (pc != null)
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                return null;
            }

            //		if (pc != null){
            //			ChatManager.chatGuildInfo(pc.getGuild(), "Building withdraw = " + buildingWithdraw);
            //			ChatManager.chatGuildInfo(pc.getGuild(), "Warehouse overdraft withdraw = " + overdraft);
            //
            //			ChatManager.chatGuildInfo(pc.getGuild(), "total withdraw = " + (overdraft + buildingWithdraw));
            //		}

            if (!forge.transferGold(-buildingWithdraw, false)) {
                overdraft += buildingWithdraw;

                if (!useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + template.item_base_name);
                    return null;
                } else {
                    if (overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + template.item_base_name);
                        return null;
                    }
                }
            }

            if (overdraft > 0 && useWarehouse)
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GOLD, overdraft, true)) {
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + " Failed to Create Item." + template.item_base_name);
                    return null;
                }


            if (prefix != null) {

                if (!useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Cannot Resource Roll without access to the warehouse! Make sure the forge is currently protected." + template.item_base_name);
                    return null;
                }


                for (Enum.ResourceType ibResources : prefixResourceCosts.keySet()) {

                    int creationAmount = prefixResourceCosts.get(ibResources);

                    if (Warehouse.isResourceLocked(cityWarehouse, ibResources) == true)
                        return null;

                    int oldAmount = cityWarehouse.resources.get(ibResources);
                    int amount = creationAmount;

                    if (oldAmount < amount)
                        amount = oldAmount;

                    if (!Warehouse.withdraw(cityWarehouse, npc, ibResources, amount, true)) {
                        //ChatManager.chatGuildError(pc, "Failed to create Item");
                        Logger.error("Warehouse for city " + cityWarehouse.city.getName() + " Failed to Create Item." + template.item_base_name);
                        return null;
                    }
                }
            }

            if (suffix != null) {

                for (Enum.ResourceType ibResources : suffixResourceCosts.keySet()) {
                    int creationAmount = suffixResourceCosts.get(ibResources);

                    if (Warehouse.isResourceLocked(cityWarehouse, ibResources) == true) {
                        ChatManager.chatSystemError(pc, "Resource is locked!" + template.item_base_name);
                        return null;
                    }

                    int oldAmount = cityWarehouse.resources.get(ibResources);
                    int amount = creationAmount;

                    if (oldAmount < amount)
                        amount = oldAmount;

                    if (!Warehouse.withdraw(cityWarehouse, npc, ibResources, amount, true)) {
                        //ChatManager.chatGuildError(pc, "Failed to create Item");
                        Logger.error("Warehouse for city " + cityWarehouse.city.getName() + " Failed to Create Item." + template.item_base_name);
                        return null;
                    }
                }
            }

            if (prefix == null && suffix == null) {

                int baseCost = template.item_value;
                int total = (int) (baseCost + baseCost * (float) .10);

                buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, total);

                overdraft = BuildingManager.GetOverdraft(forge, total);

                if (overdraft > 0 && !useWarehouse) {

                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                    return null;
                }

                if (overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {

                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                    return null;
                }

                if (useWarehouse && overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {

                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                    return null;
                }
            }

            if (!forge.transferGold(-buildingWithdraw, false)) {
                overdraft += buildingWithdraw;

                if (!useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + template.item_base_name);
                    return null;
                } else {
                    if (overdraft > cityWarehouse.resources.get(Enum.ResourceType.GOLD)) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + template.item_base_name);
                        return null;
                    }
                }
            }

            if (overdraft > 0)
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GOLD, overdraft, true)) {
                    //ChatManager.chatGuildError(pc, "Failed to create Item");
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + " Failed to Create Item." + template.item_base_name);
                    return null;
                }

            //	ChatManager.chatGuildInfo(pc, "Gold Cost = " + total);

            if (galvorAmount > 0) {
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GALVOR, galvorAmount, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!" + template.item_base_name);
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.WORMWOOD, wormwoodAmount, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse!" + template.item_base_name);
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                    return null;
                }
            }

            ml = new MobLoot(npc, template, false);

            ml.containerType = Enum.ItemContainerType.FORGE;

            if (customName.isEmpty() == false)
                ml.name = customName;

            if (prefix != null) {
                ml.addPermanentEnchantment(prefix.getIDString(), 0, 0, true);
                ml.setPrefix(prefix.getIDString());
                prefixString = prefix.getIDString();
            }

            if (suffix != null) {
                ml.addPermanentEnchantment(suffix.getIDString(), 0, 0, false);
                ml.setSuffix(suffix.getIDString());
                suffixString = suffix.getIDString();
            }

            ml.loadEnchantments();
            //set value to 0 so magicvalue can be recalculated in getValue.
            ml.setValue(0);

            float time;
            float rank = npc.getBuilding().getRank() - 1;
            float rate = (float) (2.5 * rank);
            time = (20 - rate);
            time *= MBServerStatics.ONE_MINUTE;

            if (ml.templateID > 910010 && ml.templateID < 910019) {
                rank = ml.templateID - 910010;
                time = rank * 60 * 60 * 3 * 1000;
            }


            // No job is submitted, as object's upgradetime field
            // is used to determin whether or not an object has
            // compelted rolling.  The game object exists previously
            // to this, not when 'compelte' is pressed.
            long upgradeTime = System.currentTimeMillis() + (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()));

            DateTime dateTime = new DateTime();
            dateTime = dateTime.withMillis(upgradeTime);
            ml.setDateToUpgrade(upgradeTime);

            npc.addItemToForge(ml);
            int playerID = 0;

            if (pc != null)
                playerID = pc.getObjectUUID();

            DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(ml.getObjectUUID(), npc.getObjectUUID(), ml.templateID, dateTime, prefixString, suffixString, ml.name, false, playerID);
            ProducedItem pi = new ProducedItem(npc.getRolling().size(), npc.getObjectUUID(), ml.templateID, dateTime, false, prefixString, suffixString, ml.name, playerID);
            pi.setProducedItemID(ml.getObjectUUID());
            pi.setAmount(itemsToRoll);

            ItemQueue produced = ItemQueue.borrow(pi, (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue())));
            ItemProductionManager.send(produced);
        } catch (Exception e) {
            Logger.error(e);
        } finally {
            city.transactionLock.writeLock().unlock();
        }


        //		npc.addItemToForge(item);
        return ml;

    }


    public static Item randomRoll(NPC vendor, PlayerCharacter playerCharacter, int itemsToRoll, int templateID) {
        byte itemModTable;
        int prefixMod = 0;
        int suffixMod = 0;
        String prefix = "";
        String suffix = "";

        ModTableEntry prefixEntry = null;
        ModTableEntry suffixEntry = null;

        ItemTemplate template = ItemTemplate.templates.get(templateID);

        if (template == null)
            return null;

        if (!vendor.charItemManager.hasRoomInventory(template.item_wt)) {

            if (playerCharacter != null)
                ChatManager.chatSystemInfo(playerCharacter, vendor.getName() + " " + vendor.getContract().getName() + " Inventory is full.");

            return null;
        }

        itemModTable = (byte) template.modTable;

        if (!vendor.getItemModTable().contains(itemModTable)) {

            if (playerCharacter != null)
                ErrorPopupMsg.sendErrorPopup(playerCharacter, 59);

            return null;
        }

        for (byte temp : vendor.getItemModTable()) {

            if (itemModTable != temp)
                continue;

            prefixMod = vendor.getModTypeTable().get(vendor.getItemModTable().indexOf(temp));
            suffixMod = vendor.getModSuffixTable().get(vendor.getItemModTable().indexOf(temp));
        }

        if (prefixMod == 0 && suffixMod == 0) {
            Logger.info("Failed to find modTables for item " + template.item_base_name);
            return null;
        }

        // Roll on the tables for this vendor

        ModTypeTableEntry prefixTypeTable = ModTypeTableEntry.rollTable(prefixMod, ThreadLocalRandom.current().nextInt(1, 100 + 1));
        ModTypeTableEntry suffixTypeTable = ModTypeTableEntry.rollTable(suffixMod, ThreadLocalRandom.current().nextInt(1, 100 + 1));

        // Sanity check.

        if (prefixTypeTable == null || suffixTypeTable == null)
            return null;

        int rollPrefix = ThreadLocalRandom.current().nextInt(1, 100 + 1);

        if (rollPrefix < 80) {

            int randomPrefix = LootManager.TableRoll(vendor.getLevel(), false);
            prefixEntry = ModTableEntry.rollTable(prefixTypeTable.modTableID, randomPrefix);

            if (prefixEntry != null)
                prefix = prefixEntry.action;

        }

        int rollSuffix = ThreadLocalRandom.current().nextInt(1, 100 + 1);

        // Always have at least one mod on a magic rolled item.
        // Suffix will be our backup plan.

        if (rollSuffix < 80 || prefixEntry == null) {

            int randomSuffix = LootManager.TableRoll(vendor.getLevel(), false);
            suffixEntry = ModTableEntry.rollTable(suffixTypeTable.modTableID, randomSuffix);

            if (suffixEntry != null)
                suffix = suffixEntry.action;
        }

        MobLoot toRoll = ItemFactory.produceRandomRoll(vendor, playerCharacter, prefix, suffix, templateID);

        if (toRoll == null)
            return null;

        toRoll.setValue(0);

        float time;
        float rank = vendor.getBuilding().getRank() - 1;
        float rate = (float) (2.5 * rank);
        time = (20 - rate);
        time *= MBServerStatics.ONE_MINUTE;

        // Bane circles

        if (toRoll.templateID > 910010 && toRoll.templateID < 910019) {
            rank = toRoll.templateID - 910010;
            time = rank * 60 * 60 * 3 * 1000;
        }

        // No job is submitted, as object's upgradetime field
        // is used to determine whether an object has
        // completed rolling.  The game object exists previously
        // to this, not when 'complete' is pressed.

        long upgradeTime = System.currentTimeMillis() + (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue()));

        DateTime dateTime = new DateTime();
        dateTime = dateTime.withMillis(upgradeTime);
        toRoll.setDateToUpgrade(upgradeTime);

        int playerID = 0;

        if (playerCharacter != null)
            playerID = playerCharacter.getObjectUUID();

        DbManager.NPCQueries.ADD_TO_PRODUCTION_LIST(toRoll.getObjectUUID(), vendor.getObjectUUID(), toRoll.templateID, dateTime, prefix, suffix, toRoll.name, true, playerID);

        ProducedItem pi = new ProducedItem(toRoll.getObjectUUID(), vendor.getObjectUUID(), toRoll.templateID, dateTime, true, prefix, suffix, toRoll.name, playerID);
        pi.setProducedItemID(toRoll.getObjectUUID());
        pi.setAmount(itemsToRoll);
        ItemQueue produced = ItemQueue.borrow(pi, (long) (time * Float.parseFloat(ConfigManager.MB_PRODUCTION_RATE.getValue())));
        ItemProductionManager.send(produced);

        return toRoll;
    }

    public static MobLoot produceRandomRoll(NPC npc, PlayerCharacter pc, String prefixString, String suffixString, int templateID) {

        boolean useWarehouse = false;

        if (npc == null)
            return null;

        ItemTemplate template = ItemTemplate.templates.get(templateID);

        Building forge = npc.getBuilding();

        if (forge == null)
            return null;

        Zone zone = npc.getBuilding().getParentZone();

        if (zone == null)
            return null;

        City city = City.getCity(zone.playerCityUUID);

        if (city == null)
            return null;

        MobLoot ml = null;
        city.transactionLock.writeLock().lock();

        try {


            Warehouse cityWarehouse = city.warehouse;

            if (cityWarehouse != null && forge.assetIsProtected())
                useWarehouse = true;

            ConcurrentHashMap<Enum.ResourceType, Integer> resources = null;

            if (useWarehouse)
                resources = cityWarehouse.resources;

            int galvorAmount = 0;
            int wormwoodAmount = 0;

            if (template.item_type.equals(ItemType.WEAPON) && template.item_skill_required.isEmpty() == false &&
                    template.item_skill_required.values().iterator().next() == 110) {
                switch (template.item_skill_used) {
                    case "Bow":
                    case "Crossbow":
                    case "Spear":
                    case "Pole Arm":
                    case "Staff":
                        wormwoodAmount = 22;
                        break;
                    case "Axe":
                    case "Dagger":
                    case "Sword":
                    case "Hammer":
                    case "Unarmed Combat":

                        if (ItemTemplate.isTwoHanded(template))
                            galvorAmount = 22;
                        else
                            galvorAmount = 11;
                        break;
                }
            }

            //Cant roll 110% weapons that require resources if not allowed to use warehouse.
            if (galvorAmount > 0 || wormwoodAmount > 0)
                if (!useWarehouse)
                    return null;

            if (galvorAmount > 0) {
                if (Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GALVOR)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + template.item_base_name);
                    return null;
                }

                if (cityWarehouse.resources.get(Enum.ResourceType.GALVOR) < galvorAmount) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + template.item_base_name);
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.WORMWOOD)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + template.item_base_name);
                    return null;
                }

                if (cityWarehouse.resources.get(Enum.ResourceType.WORMWOOD) < wormwoodAmount) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + template.item_base_name);
                    return null;
                }
            }

            EffectsBase prefix = null;

            if (!prefixString.isEmpty()) {
                prefix = PowersManager.getEffectByIDString(prefixString);
                if (prefix == null)
                    return null;
            }

            int baseCost = template.item_value;
            int total = (int) (baseCost + baseCost * .10);

            EffectsBase suffix = null;

            if (!suffixString.isEmpty()) {
                suffix = PowersManager.getEffectByIDString(suffixString);

                if (suffix == null)
                    return null;
            }

            //calculate gold costs and remove from the warehouse
            if (prefix != null || suffix != null) {
                int costToCreate = (int) (template.item_value + template.item_value * .10f);
                int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, costToCreate);
                int overdraft = BuildingManager.GetOverdraft(forge, costToCreate);

                if (overdraft > 0 && !useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                    return null;
                }

                if (useWarehouse && overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                    return null;
                }

                if (useWarehouse && overdraft > resources.get(Enum.ResourceType.GOLD)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                    return null;
                }

                if (!forge.transferGold(-buildingWithdraw, false)) {
                    overdraft += buildingWithdraw;

                    if (!useWarehouse) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + template.item_base_name);
                        return null;
                    } else {
                        if (overdraft > resources.get(Enum.ResourceType.GOLD)) {
                            ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + template.item_base_name);
                            return null;
                        }
                    }
                }

                // there was an overdraft, withdraw the rest from warehouse.
                if (overdraft > 0) {
                    if (pc != null) {
                        if (!Warehouse.withdraw(cityWarehouse, pc, Enum.ResourceType.GOLD, overdraft, false, true)) {
                            Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                            return null;
                        }
                    } else {
                        if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GOLD, overdraft, true)) {
                            Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                            return null;
                        }
                    }
                }
            }

            if (prefix == null && suffix == null) {

                int buildingWithdraw = BuildingManager.GetWithdrawAmountForRolling(forge, total);
                int overdraft = BuildingManager.GetOverdraft(forge, total);

                if (overdraft > 0 && !useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough gold in building strongbox." + template.item_base_name);
                    return null;
                }

                if (useWarehouse && overdraft > 0 && Warehouse.isResourceLocked(cityWarehouse, Enum.ResourceType.GOLD)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + template.item_base_name);
                    return null;
                }

                if (useWarehouse && overdraft > resources.get(Enum.ResourceType.GOLD)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + template.item_base_name);
                    return null;
                }

                if (!forge.transferGold(-buildingWithdraw, false)) {
                    overdraft += buildingWithdraw;

                    if (!useWarehouse) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + template.item_base_name);
                        return null;
                    } else {
                        if (overdraft > resources.get(Enum.ResourceType.GOLD)) {
                            ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + template.item_base_name);
                            return null;
                        }
                    }
                }

                if (overdraft > 0 && useWarehouse) {

                    if (pc != null) {
                        if (!Warehouse.withdraw(cityWarehouse, pc, Enum.ResourceType.GOLD, overdraft, false, true)) {
                            Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                            return null;
                        }
                    } else {
                        if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GOLD, overdraft, true)) {
                            Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                            return null;
                        }
                    }
                }
            }

            if (galvorAmount > 0 && useWarehouse) {
                //ChatManager.chatGuildInfo(pc, "Withdrawing " + galvorAmount + " galvor from warehouse");
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.GALVOR, galvorAmount, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!" + template.item_base_name);
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");
                    return null;
                }
            }

            if (wormwoodAmount > 0 && useWarehouse) {
                //ChatManager.chatGuildInfo(pc, "Withdrawing " + wormwoodAmount + " wormwood from warehouse");
                if (!Warehouse.withdraw(cityWarehouse, npc, Enum.ResourceType.WORMWOOD, wormwoodAmount, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse for " + template.item_base_name);
                    Logger.error("Warehouse for city " + cityWarehouse.city.getName() + "Failed to Withdrawl ");

                    return null;
                }
            }

            ml = new MobLoot(npc, template, false);

            ml.containerType = Enum.ItemContainerType.FORGE;

            if (prefix != null) {
                ml.addPermanentEnchantment(prefix.getIDString(), 0, 0, true);
                ml.setPrefix(prefix.getIDString());
            }

            if (suffix != null) {
                ml.addPermanentEnchantment(suffix.getIDString(), 0, 0, false);
                ml.setSuffix(suffix.getIDString());
            }

            ml.loadEnchantments();


            ml.setValue(0);
            ml.setRandom(true);
            npc.addItemToForge(ml);
        } catch (Exception e) {
            Logger.error(e);
        } finally {
            city.transactionLock.writeLock().unlock();
        }
        return ml;

    }
}
