// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      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.ArrayList;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadLocalRandom;

public class ItemFactory {

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

        if (pc == null)
            return;

        int max = 20;
        CharacterItemManager itemManager = pc.getCharItemManager();
        ItemBase ib = ItemBase.getItemBase(objectID);
        if (count > max)
            count = max;

        ClientConnection cc = pc.getClientConnection();

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

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

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

            Item item = new Item(ib, pc.getObjectUUID(), OwnerType.PlayerCharacter, (byte) 0, (byte) 0,
                    (short) 1, (short) 1, true, false, ItemContainerType.INVENTORY, (byte) 0,
                    new ArrayList<>(), "");
            try {
                item = DbManager.ItemQueries.ADD_ITEM(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 itemID, int pToken, int sToken, String customName) {

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

        boolean useWarehouse = false;

        ItemBase ib = ItemBase.getItemBase(itemID);

        if (ib == null)
            return null;

        Building forge = npc.getBuilding();

        if (forge == null)
            return null;


        if (!npc.getCharItemManager().hasRoomInventory(ib.getWeight())) {
            if (pc != null)
                ErrorPopupMsg.sendErrorPopup(pc, 21);
            return null;
        }

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

        if (zone == null)
            return null;

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

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

        try {
            Warehouse cityWarehouse = city.getWarehouse();

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

            if (ib.getUUID() > 910010 && ib.getUUID() < 910019) {
                ConcurrentHashMap<ItemBase, Integer> resources = cityWarehouse.getResources();


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

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

                if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + " " + ib.getName());
                    return null;
                }

                if (overdraft > resources.get(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + " " + ib.getName());
                    return null;
                }

                //All checks passed, lets withdraw from building first.

                //			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." + ib.getName());
                        return null;
                    } else {
                        if (overdraft > resources.get(ItemBase.GOLD_ITEM_BASE)) {
                            ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + ib.getName());
                            return null;
                        }
                    }
                }

                if (overdraft > 0)
                    if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                        //ChatManager.chatGuildError(pc, "Failed to create Item");
                        Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item." + ib.getName());
                        return null;
                    }


                ml = new MobLoot(npc, ib, 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.getItemBase().getUUID() > 910010 && ml.getItemBase().getUUID() < 910019) {
                    rank = ml.getItemBaseID() - 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.getItemBaseID(), dateTime, "", "", "", false, playerID);
                ProducedItem pi = new ProducedItem(ml.getObjectUUID(), npc.getObjectUUID(), ml.getItemBaseID(), 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 (ib.getType() == ItemType.WEAPON && ib.getPercentRequired() == 110) {
                switch (ib.getSkillRequired()) {
                    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 (ib.isTwoHanded())
                            galvorAmount = 20;
                        else
                            galvorAmount = 10;
                        break;
                }
            }

            ItemBase galvor = ItemBase.getItemBase(1580017);
            ItemBase wormwood = ItemBase.getItemBase(1580018);

            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." + ib.getName());
                    return null;

                }

            if (galvorAmount > 0) {
                if (cityWarehouse.isResourceLocked(galvor)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + ib.getName());
                    return null;
                }

                if (cityWarehouse.getResources().get(galvor) < galvorAmount) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + ib.getName());
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (cityWarehouse.isResourceLocked(wormwood)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Wormwood is locked." + ib.getName());
                    return null;
                }

                if (cityWarehouse.getResources().get(wormwood) < wormwoodAmount) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Wormwood in warehouse to roll this item." + ib.getName());
                    return null;
                }
            }
            ConcurrentHashMap<ItemBase, Integer> suffixResourceCosts = null;
            ConcurrentHashMap<ItemBase, 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." + ib.getName());
                    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 = ib.getBaseValue();
                int effectCost = (int) prefixValue.getValue();
                int total = baseCost * 10 + effectCost;

                prefixCost = effectCost;
                int buildingWithdraw = BuildingManager.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." + ib.getName());
                    return null;
                }

                if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + ib.getName());
                    return null;
                }

                if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + ib.getName());
                    return null;
                }
                prefixResourceCosts = prefix.getResourcesForEffect();
                for (ItemBase ibResources : prefixResourceCosts.keySet()) {
                    int warehouseAmount = cityWarehouse.getResources().get(ibResources);
                    int creationAmount = prefixResourceCosts.get(ibResources);
                    //ChatManager.chatInfoError(pc, "Prefix : " + ibResources.getName() + " / " + creationAmount);
                    if (warehouseAmount < creationAmount) {
                        //ChatManager.chatInfoError(pc, "You need at least " + creationAmount + " " + ibResources.getName() + " to Create this item.");
                        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." + ib.getName());
                    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 = ib.getBaseValue();
                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." + ib.getName());
                    return null;
                }

                if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + ib.getName());
                    return null;
                }

                if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + ib.getName());
                    return null;
                }


                for (ItemBase ibResources : suffixResourceCosts.keySet()) {
                    int warehouseAmount = cityWarehouse.getResources().get(ibResources);
                    int creationAmount = suffixResourceCosts.get(ibResources);
                    if (warehouseAmount < creationAmount) {
                        //					if (pc != null)
                        //						ChatManager.chatInfoError(pc, "You need at least " + creationAmount + " " + ibResources.getName() + " to Create this item.");
                        return null;
                    }


                }

            }


            //Check if Total suffix and prefix costs + itemCost can be withdrawn.
            int costToCreate = suffixCost + prefixCost + (ib.getBaseValue());
            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." + ib.getName());
                return null;
            }

            if (overdraft > 0 && useWarehouse && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                if (pc != null)
                    ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + ib.getName());
                return null;
            }

            if (useWarehouse && overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {
                if (pc != null)
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + ib.getName());
                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." + ib.getName());
                    return null;
                } else {
                    if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + ib.getName());
                        return null;
                    }
                }
            }

            if (overdraft > 0 && useWarehouse)
                if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                    //ChatManager.chatGuildError(pc, "Failed to create Item");
                    Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item." + ib.getName());
                    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." + ib.getName());
                    return null;
                }


                for (ItemBase ibResources : prefixResourceCosts.keySet()) {

                    int creationAmount = prefixResourceCosts.get(ibResources);

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

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

                    if (oldAmount < amount)
                        amount = oldAmount;

                    if (!cityWarehouse.withdraw(npc, ibResources, amount, false, true)) {
                        //ChatManager.chatGuildError(pc, "Failed to create Item");
                        Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item." + ib.getName());
                        return null;
                    }
                }
            }

            if (suffix != null) {

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

                    if (cityWarehouse.isResourceLocked(ibResources) == true) {
                        ChatManager.chatSystemError(pc, ibResources.getName() + " is locked!" + ib.getName());
                        return null;
                    }

                    int oldAmount = cityWarehouse.getResources().get(ibResources);
                    int amount = creationAmount;
                    if (oldAmount < amount)
                        amount = oldAmount;
                    if (!cityWarehouse.withdraw(npc, ibResources, amount, false, true)) {
                        //ChatManager.chatGuildError(pc, "Failed to create Item");
                        Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item." + ib.getName());
                        return null;
                    }
                }
            }

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

                int baseCost = ib.getBaseValue();
                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." + ib.getName());
                    return null;
                }

                if (overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {

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

                if (useWarehouse && overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {

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

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

                if (!useWarehouse) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Building does not have enough gold to produce this item." + ib.getName());
                    return null;
                } else {
                    if (overdraft > cityWarehouse.getResources().get(ItemBase.GOLD_ITEM_BASE)) {
                        ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse to produce this item." + ib.getName());
                        return null;
                    }
                }
            }

            if (overdraft > 0)
                if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                    //ChatManager.chatGuildError(pc, "Failed to create Item");
                    Logger.error("Warehouse With UID of " + cityWarehouse.getUID() + " Failed to Create Item." + ib.getName());
                    return null;
                }

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

            if (galvorAmount > 0) {
                if (!cityWarehouse.withdraw(npc, galvor, galvorAmount, false, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!" + ib.getName());
                    Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (!cityWarehouse.withdraw(npc, wormwood, wormwoodAmount, false, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse!" + ib.getName());
                    Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                    return null;
                }
            }

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

            ml.containerType = Enum.ItemContainerType.FORGE;
            ml.setName(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.getItemBase().getUUID() > 910010 && ml.getItemBase().getUUID() < 910019) {
                rank = ml.getItemBaseID() - 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.getItemBaseID(), dateTime, prefixString, suffixString, ml.getCustomName(), false, playerID);
            ProducedItem pi = new ProducedItem(npc.getRolling().size(), npc.getObjectUUID(), ml.getItemBaseID(), dateTime, false, prefixString, suffixString, ml.getCustomName(), 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 itemBaseID) {
        byte itemModTable;
        int prefixMod = 0;
        int suffixMod = 0;
        String prefix = "";
        String suffix = "";

        ModTableEntry prefixEntry = null;
        ModTableEntry suffixEntry = null;

        ItemBase ib = ItemBase.getItemBase(itemBaseID);

        if (ib == null)
            return null;

        if (!vendor.getCharItemManager().hasRoomInventory(ib.getWeight())) {

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

            return null;
        }

        itemModTable = (byte) ib.getModTable();

        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 " + ib.getName());
            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, itemBaseID);

        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;

        if (toRoll.getItemBase().getUUID() > 910010 && toRoll.getItemBase().getUUID() < 910019) {
            rank = toRoll.getItemBaseID() - 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.getItemBaseID(), dateTime, prefix, suffix, toRoll.getCustomName(), true, playerID);

        ProducedItem pi = new ProducedItem(toRoll.getObjectUUID(), vendor.getObjectUUID(), toRoll.getItemBaseID(), dateTime, true, prefix, suffix, toRoll.getCustomName(), 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 itemID) {

        boolean useWarehouse = false;

        if (npc == null)
            return null;

        ItemBase ib = ItemBase.getItemBase(itemID);

        if (ib == null)
            return null;

        Building forge = npc.getBuilding();

        if (forge == null)
            return null;

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

        if (zone == null)
            return null;

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

        if (city == null)
            return null;

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

        try {


            Warehouse cityWarehouse = city.getWarehouse();

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

            ConcurrentHashMap<ItemBase, Integer> resources = null;

            if (useWarehouse)
                resources = cityWarehouse.getResources();

            int galvorAmount = 0;
            int wormwoodAmount = 0;

            if (ib.getType() == ItemType.WEAPON && ib.getPercentRequired() == 110) {
                switch (ib.getSkillRequired()) {
                    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 (ib.isTwoHanded())
                            galvorAmount = 22;
                        else
                            galvorAmount = 11;
                        break;
                }
            }

            ItemBase galvor = ItemBase.getItemBase(1580017);
            ItemBase wormwood = ItemBase.getItemBase(1580018);

            //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 (cityWarehouse.isResourceLocked(galvor)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + ib.getName());
                    return null;
                }

                if (cityWarehouse.getResources().get(galvor) < galvorAmount) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + ib.getName());
                    return null;
                }
            }

            if (wormwoodAmount > 0) {
                if (cityWarehouse.isResourceLocked(wormwood)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Galvor is locked." + ib.getName());
                    return null;
                }

                if (cityWarehouse.getResources().get(wormwood) < wormwoodAmount) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Galvor in warehouse to roll this item." + ib.getName());
                    return null;
                }
            }

            EffectsBase prefix = null;

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

            ItemBase goldIB = ItemBase.getGoldItemBase();

            int baseCost = ib.getBaseValue();
            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) (ib.getBaseValue() + ib.getBaseValue() * .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." + ib.getName());
                    return null;
                }

                if (useWarehouse && overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + ib.getName());
                    return null;
                }

                if (useWarehouse && overdraft > resources.get(goldIB)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + ib.getName());
                    return null;
                }

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

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

                // there was an overdraft, withdraw the rest from warehouse.
                if (overdraft > 0) {
                    if (pc != null) {
                        if (!cityWarehouse.withdraw(pc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                            Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                            return null;
                        }
                    } else {
                        if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                            Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "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." + ib.getName());
                    return null;
                }

                if (useWarehouse && overdraft > 0 && cityWarehouse.isResourceLocked(ItemBase.GOLD_ITEM_BASE)) {
                    if (pc != null)
                        ErrorPopupMsg.sendErrorMsg(pc, "Warehouse gold is barred! Overdraft cannot be withdrawn from warehouse." + ib.getName());
                    return null;
                }

                if (useWarehouse && overdraft > resources.get(goldIB)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Not enough Gold in Warehouse for overdraft." + ib.getName());
                    return null;
                }

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

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

                if (overdraft > 0 && useWarehouse) {

                    if (pc != null) {
                        if (!cityWarehouse.withdraw(pc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                            Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                            return null;
                        }
                    } else {
                        if (!cityWarehouse.withdraw(npc, ItemBase.GOLD_ITEM_BASE, overdraft, false, true)) {
                            Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                            return null;
                        }
                    }
                }
            }

            if (galvorAmount > 0 && useWarehouse) {
                //ChatManager.chatGuildInfo(pc, "Withdrawing " + galvorAmount + " galvor from warehouse");
                if (!cityWarehouse.withdraw(npc, galvor, galvorAmount, false, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Galvor from warehouse!" + ib.getName());
                    Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");
                    return null;
                }
            }

            if (wormwoodAmount > 0 && useWarehouse) {
                //ChatManager.chatGuildInfo(pc, "Withdrawing " + wormwoodAmount + " wormwood from warehouse");
                if (!cityWarehouse.withdraw(npc, wormwood, wormwoodAmount, false, true)) {
                    ErrorPopupMsg.sendErrorMsg(pc, "Failed to withdraw Wormwood from warehouse for " + ib.getName());
                    Logger.error("Warehouse with UID of" + cityWarehouse.getObjectUUID() + "Failed to Withdrawl ");

                    return null;
                }
            }

            ml = new MobLoot(npc, ib, 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;

    }
}