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


package engine.net.client.handlers;

import engine.exception.MsgSendException;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.gameManager.ForgeManager;
import engine.loot.WorkOrder;
import engine.mbEnums;
import engine.mbEnums.GameObjectType;
import engine.mbEnums.ItemType;
import engine.net.Dispatch;
import engine.net.DispatchMessage;
import engine.net.client.ClientConnection;
import engine.net.client.msg.ClientNetMsg;
import engine.net.client.msg.ErrorPopupMsg;
import engine.net.client.msg.ItemProductionMsg;
import engine.net.client.msg.ManageNPCMsg;
import engine.objects.*;
import org.pmw.tinylog.Logger;

import java.util.HashMap;

/*
 * @Summary: Processes application protocol message which modifies
 * hireling inventory through rolling, junking or depositing.
 */

public class ItemProductionMsgHandler extends AbstractClientMsgHandler {

    public ItemProductionMsgHandler() {
        super(ItemProductionMsg.class);
    }

    @Override
    protected boolean _handleNetMsg(ClientNetMsg baseMsg, ClientConnection origin) throws MsgSendException {

        // Member variable declaration

        PlayerCharacter player;
        NPC vendorNPC;
        ItemProductionMsg msg;
        Dispatch dispatch;

        // Member variable assignment

        msg = (ItemProductionMsg) baseMsg;
        player = origin.getPlayerCharacter();

        if (player == null)
            return true;

        // Grab reference to vendor we are interacting with

        vendorNPC = (NPC) DbManager.getObject(mbEnums.GameObjectType.NPC, msg.npcUUID);

        // Oops?

        if (vendorNPC == null)
            return true;

        // Process Request

        switch (msg.actionType) {

            case PRODUCE:
                boolean isRandom = false;

                if (msg.unknown03 != 0 && msg.pToken == 0 && msg.sToken == 0)
                    isRandom = true;

                // Create new work order

                WorkOrder workOrder = new WorkOrder();
                workOrder.total_to_produce = msg.total_to_produce;
                workOrder.vendor = vendorNPC;
                workOrder.templateID = msg.templateID;
                workOrder.prefixToken = msg.pToken;
                workOrder.suffixToken = msg.sToken;
                workOrder.item_name_override = msg.name;
                workOrder.multiple_slot_request = msg.size;

                // Validate vendor can roll this item

                if (ForgeManager.validate(player, workOrder) == false)
                    return true;

                // Start rolling the item(s)

                ForgeManager.submit(workOrder);

                //Create Multiple Item Function.. Fill all empty slots

                if (msg.size > 0) {
                    int emptySlots = vendorNPC.getRank() - vendorNPC.getRolling().size();

                    if (emptySlots > 0)
                        for (int i = 0; i < emptySlots; i++)
                            vendorNPC.produceItem(player.getObjectUUID(), msg.total_to_produce, isRandom, msg.pToken, msg.sToken, msg.name, msg.templateID);
                } else
                    vendorNPC.produceItem(player.getObjectUUID(), msg.total_to_produce, isRandom, msg.pToken, msg.sToken, msg.name, msg.templateID);
                break;
            case JUNK:
                junkItem(msg.templateID, vendorNPC, origin);
                break;
            case RECYCLE:
                recycleItem(msg.items, vendorNPC, origin);
                msg.actionType = mbEnums.ProductionActionType.TAKE;
                dispatch = Dispatch.borrow(player, msg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                break;
            case COMPLETE:
                vendorNPC.completeItem(msg.templateID);
                break;
            case DEPOSIT:
                depositItem(msg.templateID, vendorNPC, origin);
                break;
            case SETPRICE:
                setItemPrice(msg.itemType, msg.templateID, msg.itemPrice, vendorNPC, origin);
                break;
            case TAKE:
                takeItem(msg.items, vendorNPC, origin);
                dispatch = Dispatch.borrow(player, msg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                break;
        }
        return true;
    }

    private static void setItemPrice(int itemType, int itemUUID, int itemPrice, NPC vendor, ClientConnection origin) {

        Item targetItem;
        ItemProductionMsg outMsg;
        Dispatch dispatch;

        PlayerCharacter player = origin.getPlayerCharacter();

        if (player == null)
            return;

        if (itemType == GameObjectType.Item.ordinal())
            targetItem = Item.getFromCache(itemUUID);
        else if (itemType == GameObjectType.MobLoot.ordinal())
            targetItem = MobLoot.getFromCache(itemUUID);
        else
            targetItem = null;

        if (targetItem == null)
            return;

        if (targetItem.getObjectType() == GameObjectType.Item) {
            if (!DbManager.ItemQueries.UPDATE_VALUE(targetItem, itemPrice)) {
                ChatManager.chatInfoError(origin.getPlayerCharacter(), "Failed to set price! Contact CCR For help.");
                return;
            }
            targetItem.setValue(itemPrice);
            outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.DEPOSIT.ordinal(), true);
            dispatch = Dispatch.borrow(player, outMsg);
            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

            outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.SETPRICE.ordinal(), true);
            dispatch = Dispatch.borrow(player, outMsg);
            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
        } else if (targetItem.getObjectType() == GameObjectType.MobLoot) {
            MobLoot mobLoot = (MobLoot) targetItem;
            if (!DbManager.NPCQueries.UPDATE_ITEM_PRICE(mobLoot.getObjectUUID(), vendor.getObjectUUID(), itemPrice)) {
                ChatManager.chatInfoError(origin.getPlayerCharacter(), "Failed to set price! Contact CCR For help.");
                return;
            }
            targetItem.setValue(itemPrice);
            outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.DEPOSIT.ordinal(), true);
            dispatch = Dispatch.borrow(player, outMsg);
            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

            outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.SETPRICE.ordinal(), true);
            dispatch = Dispatch.borrow(player, outMsg);
            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
        }
    }

    // Method sets the price on an item in the vendor inventory

    private static void depositItem(int itemUUID, NPC vendor, ClientConnection origin) {

        Item targetItem;
        ItemProductionMsg outMsg;
        CharacterItemManager itemMan;
        Dispatch dispatch;

        PlayerCharacter player = origin.getPlayerCharacter();

        if (player == null)
            return;

        if (origin.sellLock.tryLock()) {
            try {
                targetItem = Item.getFromCache(itemUUID);

                if (targetItem == null)
                    return;

                if (targetItem.template.item_type.equals(ItemType.GOLD))
                    return;

                if (!vendor.charItemManager.hasRoomInventory(targetItem.template.item_wt)) {
                    ErrorPopupMsg.sendErrorPopup(player, 21);
                    return;
                }

                itemMan = origin.getPlayerCharacter().charItemManager;

                if (itemMan == null)
                    return;

                if (vendor.charItemManager.getInventoryWeight() > 500) {
                    ErrorPopupMsg.sendErrorPopup(player, 21);
                    return;
                }

                if (!targetItem.validForInventory(origin, player, itemMan)) {
                    ErrorPopupMsg.sendErrorPopup(player, 19);
                    return;
                }

                // Transfer item from player to vendor's inventory

                if (!itemMan.sellToNPC(targetItem, vendor)) {
                    ErrorPopupMsg.sendErrorPopup(player, 109);
                    return;
                }

                outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.DEPOSIT.ordinal(), true);
                dispatch = Dispatch.borrow(player, outMsg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

                outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT.ordinal(), true);
                dispatch = Dispatch.borrow(player, outMsg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

                origin.getPlayerCharacter().charItemManager.updateInventory();
            } catch (Exception e) {
                Logger.error(e);
            } finally {
                origin.sellLock.unlock();
            }
        }

    }

    // Method completes an item that has been previously rolled
    // adding it to the NPC's inventory

    private static void recycleItem(HashMap<Integer, Integer> itemList, NPC vendor, ClientConnection origin) {

        Item targetItem;
        ItemProductionMsg outMsg;
        int totalValue = 0;
        Dispatch dispatch;

        if (vendor.getBuilding() == null)
            return;

        PlayerCharacter player = origin.getPlayerCharacter();

        if (player == null)
            return;

        if (itemList == null)
            return;

        if (origin.sellLock.tryLock()) {
            try {


                for (int itemUUID : itemList.keySet()) {
                    int itemValue = 0;

                    int type = itemList.get(itemUUID);

                    if (type == GameObjectType.Item.ordinal())
                        targetItem = Item.getFromCache(itemUUID);
                    else
                        targetItem = MobLoot.getFromCache(itemUUID);

                    if (targetItem == null)
                        continue;

                    if (targetItem.template.item_type.equals(ItemType.GOLD))
                        return;

                    if (!vendor.charItemManager.doesCharOwnThisItem(targetItem.getObjectUUID()))
                        continue;
                    if (vendor.charItemManager.inventoryContains(targetItem) == false)
                        continue;

                    itemValue = targetItem.template.item_value;

                    if (vendor.getBuilding().getStrongboxValue() + itemValue > vendor.getBuilding().getMaxGold()) {
                        ErrorPopupMsg.sendErrorPopup(player, 201);
                        break;
                    }

                    switch (targetItem.template.item_type) {
                        case EMPLOYMENTCONTRACT:
                        case CHARTER:
                        case DEED:
                        case REALMCHARTER:
                        case SCROLL:
                        case POTION:
                            continue;
                    }
                    totalValue += itemValue;
                    vendor.charItemManager.recycle(targetItem);

                    outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.TAKE.ordinal(), true);

                    dispatch = Dispatch.borrow(origin.getPlayerCharacter(), outMsg);
                    DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                }

                // Refund a portion of the gold

                if (!vendor.getBuilding().transferGold(totalValue, false))
                    return;

            } catch (Exception e) {
                Logger.error(e);
            } finally {
                origin.sellLock.unlock();
            }
        }
    }

    // Method handles recycling of an item

    private static void junkItem(int itemUUID, NPC vendor, ClientConnection origin) {

        MobLoot targetItem;
        ManageNPCMsg outMsg;
        Dispatch dispatch;

        if (origin.sellLock.tryLock()) {
            try {
                targetItem = MobLoot.getFromCache(itemUUID);

                PlayerCharacter player = origin.getPlayerCharacter();

                if (player == null)
                    return;

                //  junk nothing?

                if (targetItem == null)
                    return;

                if (!vendor.charItemManager.forgeContains(targetItem, vendor))
                    return;

                // Cannot junk items without a forge!

                if (vendor.getBuilding() == null)
                    return;

                // Delete the item and cancel any pending rolling timer jobs

                targetItem.recycle(vendor);
                vendor.removeItemFromForge(targetItem);

                // Refresh vendor's inventory to client

                outMsg = new ManageNPCMsg(vendor);
                outMsg.setMessageType(1);
                dispatch = Dispatch.borrow(player, outMsg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
            } finally {
                origin.sellLock.unlock();
            }
        }

    }

    private static void takeItem(HashMap<Integer, Integer> itemList, NPC vendor, ClientConnection origin) {

        Item targetItem;
        PlayerCharacter player = origin.getPlayerCharacter();

        if (player == null)
            return;

        for (int itemUUID : itemList.keySet()) {

            int type = itemList.get(itemUUID);
            if (type == GameObjectType.Item.ordinal()) {
                targetItem = Item.getFromCache(itemUUID);

            } else
                targetItem = MobLoot.getFromCache(itemUUID);

            if (targetItem == null)
                return;

            if (targetItem.template.item_type.equals(ItemType.GOLD))
                return;
            if (vendor.charItemManager.inventoryContains(targetItem) == false)
                return;

            if (player.charItemManager.hasRoomInventory(targetItem.template.item_wt) == false)
                return;

            player.charItemManager.buyFromNPC(targetItem, vendor);
        }

        player.charItemManager.updateInventory();

    }

}