// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      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.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.CharacterItemManager;
import engine.objects.Item;
import engine.objects.NPC;
import engine.objects.PlayerCharacter;
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 vendor;
        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

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

        // Oops?

        if (vendor == null)
            return true;

        // Process Request

        switch (msg.actionType) {

            case PRODUCE:

                // Create new work order

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

                // Submit workOder to begin rolling items

                int validation_result = ForgeManager.submit(workOrder);

                // workOrder could not be completed
                // Report back to the user the reason why

                if (validation_result != 0) {
                    ErrorPopupMsg.sendErrorPopup(player, validation_result);
                    return true;
                }

                break;
            case JUNK:
                junkItem(msg.itemUUID, vendor, origin);
                break;
            case RECYCLE:
                recycleItem(msg.items, vendor, origin);
                msg.actionType = mbEnums.ProductionActionType.TAKE;
                dispatch = Dispatch.borrow(player, msg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                break;
            case COMPLETE:
                completeItem(msg.itemUUID, vendor);
                break;
            case DEPOSIT:
                depositItem(msg.itemUUID, vendor, origin);
                break;
            case SETPRICE:
                setItemPrice(msg.itemUUID, msg.itemPrice, vendor, origin);
                break;
            case TAKE:
                takeItem(msg.items, vendor, origin);
                dispatch = Dispatch.borrow(player, msg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                break;
        }
        return true;
    }

    private static void completeItem(int itemUUID, NPC vendor) {

        Item virtualItem = ForgeManager.inMemoryItemLookup.get(itemUUID);
        WorkOrder workOrder = ForgeManager.itemWorkOrderLookup.get(virtualItem);

        // Persist item and add to vendor inventory

        Item completedItem = DbManager.ItemQueries.PERSIST(virtualItem);
        vendor.charItemManager.addItemToInventory(completedItem);

        ItemProductionMsg outMsg1 = new ItemProductionMsg(vendor.building, vendor, completedItem, mbEnums.ProductionActionType.DEPOSIT, true);
        DispatchMessage.dispatchMsgToInterestArea(vendor, outMsg1, mbEnums.DispatchChannel.SECONDARY, 700, false, false);
        ItemProductionMsg outMsg2 = new ItemProductionMsg(vendor.building, vendor, completedItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT, true);
        DispatchMessage.dispatchMsgToInterestArea(vendor, outMsg2, mbEnums.DispatchChannel.SECONDARY, 700, false, false);

        // Remove from collections

        ItemProductionMsg outMsg = new ItemProductionMsg(vendor.building, vendor, virtualItem, mbEnums.ProductionActionType.CONFIRM_SETPRICE, true);
        DispatchMessage.dispatchMsgToInterestArea(vendor, outMsg, mbEnums.DispatchChannel.SECONDARY, 700, false, false);

        workOrder.cooking.remove(virtualItem);
        ForgeManager.itemWorkOrderLookup.remove(virtualItem);


    }

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

        Item targetItem;
        ItemProductionMsg outMsg;
        Dispatch dispatch;

        PlayerCharacter player = origin.getPlayerCharacter();

        if (player == null)
            return;

        targetItem = Item.getFromCache(itemUUID);

        if (targetItem == null)
            return;

        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, true);
            dispatch = Dispatch.borrow(player, outMsg);
            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

            outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.SETPRICE, 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, true);
                dispatch = Dispatch.borrow(player, outMsg);
                DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

                outMsg = new ItemProductionMsg(vendor.getBuilding(), vendor, targetItem, mbEnums.ProductionActionType.CONFIRM_DEPOSIT, 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;

                    targetItem = Item.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, true);

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

                // Refund a portion of the gold

                vendor.getBuilding().transferGold(totalValue, false);

            } 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) {

        Item targetItem;
        ManageNPCMsg outMsg;
        Dispatch dispatch;

        if (origin.sellLock.tryLock()) {

            try {

                targetItem = ForgeManager.inMemoryItemLookup.get(itemUUID);

                PlayerCharacter player = origin.getPlayerCharacter();

                if (player == null)
                    return;

                //  junk nothing?

                if (targetItem == null)
                    return;

                // Cannot junk items without a forge!

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

                // Delete the item

                ForgeManager.inMemoryItemLookup.remove(targetItem.objectUUID);
                WorkOrder workOrder = ForgeManager.itemWorkOrderLookup.get(targetItem);
                workOrder.cooking.remove(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()) {

            targetItem = Item.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();

    }

}