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

package engine.net.client.handlers;

import engine.mbEnums;
import engine.exception.MsgSendException;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.gameManager.PowersManager;
import engine.gameManager.SessionManager;
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.TrainMsg;
import engine.net.client.msg.TrainerInfoMsg;
import engine.objects.*;
import engine.powers.PowersBase;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;

import java.util.concurrent.ConcurrentHashMap;

public class TrainMsgHandler extends AbstractClientMsgHandler {

    public TrainMsgHandler() {
        super(TrainMsg.class);
    }

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

        TrainMsg msg = (TrainMsg) baseMsg;
        origin.getPlayerCharacter().recalculate();
        PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin);
        Dispatch dispatch;

        if (playerCharacter == null)
            return true;

        NPC npc = NPC.getFromCache(msg.npcID);

        if (npc == null)
            return true;

        if (origin.trainLock.tryLock()) {
            try {
                Item gold = playerCharacter.charItemManager.getGoldInventory();

                if (gold == null)
                    return true;

                if (!gold.validForInventory(origin, playerCharacter, playerCharacter.charItemManager))
                    return true;

                boolean canTrain = false;
                if (msg.isSkill) {

                    //Get skill
                    SkillsBase sb = DbManager.SkillsBaseQueries.GET_BASE_BY_TOKEN(msg.token);
                    ConcurrentHashMap<String, CharacterSkill> skills = playerCharacter.getSkills();

                    if (sb == null || skills == null)
                        return true;

                    CharacterSkill sk = skills.get(sb.getName());

                    if (sk == null)
                        return true;

                    if (sk.getSkillsBase().getToken() == 40661438) {
                        int maxValue = 15;


                        if (MaxSkills.MaxSkillsSet.get(252647) != null)
                            for (MaxSkills maxSkills : MaxSkills.MaxSkillsSet.get(252647)) {
                                if (maxSkills.getSkillToken() != sk.getToken())
                                    continue;

                                if (maxSkills.getSkillLevel() > npc.getLevel())
                                    continue;
                                maxValue += maxSkills.getMaxSkillPercent();
                            }
                        if (maxValue > sk.getModifiedAmountBeforeMods()) {
                            canTrain = true;
                        }

                    }
                    if (canTrain == false)
                        if (npc.getContract() != null && npc.getContract().getExtraRune() != 0) {
                            int maxValue = 15;


                            if (MaxSkills.MaxSkillsSet.get(npc.getContract().getExtraRune()) != null)
                                for (MaxSkills maxSkills : MaxSkills.MaxSkillsSet.get(npc.getContract().getExtraRune())) {
                                    if (maxSkills.getSkillToken() != sk.getToken())
                                        continue;

                                    if (maxSkills.getSkillLevel() > npc.getLevel())
                                        continue;
                                    maxValue += maxSkills.getMaxSkillPercent();
                                }
                            if (maxValue > sk.getModifiedAmountBeforeMods()) {
                                canTrain = true;
                            }


                        }
                    if (canTrain == false) {
                        int maxValue = 15;
                        if (MaxSkills.MaxSkillsSet.get(npc.getContractID()) != null)
                            for (MaxSkills maxSkills : MaxSkills.MaxSkillsSet.get(npc.getContractID())) {
                                if (maxSkills.getSkillToken() != sk.getToken())
                                    continue;

                                if (maxSkills.getSkillLevel() > npc.getLevel())
                                    continue;
                                maxValue += maxSkills.getMaxSkillPercent();
                            }
                        if (maxValue > sk.getModifiedAmountBeforeMods()) {
                            canTrain = true;
                        }
                    }

                    if (canTrain == false) {
                        int maxValue = 15;
                        if (MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID()) != null)
                            for (MaxSkills maxSkills : MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID())) {
                                if (maxSkills.getSkillToken() != sk.getToken())
                                    continue;

                                if (maxSkills.getSkillLevel() > npc.getLevel())
                                    continue;
                                maxValue += maxSkills.getMaxSkillPercent();
                            }
                        if (maxValue > sk.getModifiedAmountBeforeMods()) {
                            canTrain = true;
                        }
                    }

                    if (canTrain == false) {
                        int maxValue = 15;
                        if (MaxSkills.MaxSkillsSet.get(npc.extraRune2) != null)
                            for (MaxSkills maxSkills : MaxSkills.MaxSkillsSet.get(npc.getContract().getClassID())) {
                                if (maxSkills.getSkillToken() != sk.getToken())
                                    continue;

                                if (maxSkills.getSkillLevel() > npc.getLevel())
                                    continue;
                                maxValue += maxSkills.getMaxSkillPercent();
                            }
                        if (maxValue > sk.getModifiedAmountBeforeMods()) {
                            canTrain = true;
                        }
                    }

                    if (canTrain == false) {
                        ChatManager.chatSystemError(playerCharacter, "NPC cannot train that skill any higher");
                        return true;
                    }

                    float cost = sk.getTrainingCost(playerCharacter, npc);
                    float profitCost = cost * npc.getSellPercent(playerCharacter);

                    profitCost += .5f;

                    if (profitCost > playerCharacter.charItemManager.getGoldInventory().getNumOfItems())
                        return true;

                    Building building = npc.getBuilding();

                    if (building != null && building.getProtectionState().equals(mbEnums.ProtectionState.NPC))
                        building = null;

                    if (building != null && building.getStrongboxValue() + (profitCost - cost) > building.getMaxGold()) {
                        ErrorPopupMsg.sendErrorPopup(playerCharacter, 206);
                        return true;
                    }

                    if (playerCharacter.charItemManager.getGoldInventory().getNumOfItems() - profitCost < 0)
                        return true;

                    if (playerCharacter.charItemManager.getGoldInventory().getNumOfItems() - profitCost > MBServerStatics.PLAYER_GOLD_LIMIT)
                        return true;

                    //attempt to train

                    if (sk.train(playerCharacter)) {
                        playerCharacter.charItemManager.buyFromNPC(building, (int) profitCost, (int) (profitCost - cost));

                        dispatch = Dispatch.borrow(playerCharacter, msg);
                        DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

                        //update trainer window

                        if (npc != null) {
                            TrainerInfoMsg tim = new TrainerInfoMsg(msg.npcType, msg.npcID, npc.getSellPercent(playerCharacter));
                            tim.setTrainPercent(npc.getSellPercent(playerCharacter));
                            dispatch = Dispatch.borrow(playerCharacter, tim);
                            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                        }
                    }

                } else {
                    //Get Power
                    int token = msg.token;

                    if (MBServerStatics.POWERS_DEBUG) {
                        ChatManager.chatSayInfo(playerCharacter, "Training Power: " +
                                Integer.toHexString(msg.token) + " (" + msg.token + ')');
                        System.out.println("Training Power: " +
                                Integer.toHexString(msg.token) + " (" + msg.token + ')');
                    }

                    PowersBase pb = PowersManager.getPowerByToken(token);
                    ConcurrentHashMap<Integer, CharacterPower> powers = playerCharacter.getPowers();

                    if (pb == null || powers == null)
                        return true;

                    if (pb.isWeaponPower)
                        return true;

                    CharacterPower cp = null;

                    if (powers.containsKey(token))
                        cp = powers.get(token);

                    if (cp == null)
                        return true;

                    //attempt to train
                    float cost = (int) cp.getTrainingCost(playerCharacter, npc);
                    float profitCost = cost * npc.getSellPercent(playerCharacter);

                    profitCost += .5f;

                    if (profitCost > playerCharacter.charItemManager.getGoldInventory().getNumOfItems())
                        return true;

                    Building building = npc.getBuilding();

                    if (building != null && building.getProtectionState().equals(mbEnums.ProtectionState.NPC))
                        building = null;

                    if (building != null && building.getStrongboxValue() + (profitCost - cost) > building.getMaxGold()) {
                        ErrorPopupMsg.sendErrorPopup(playerCharacter, 206);
                        return true;
                    }

                    if (cp.train(playerCharacter)) {

                        if (!playerCharacter.charItemManager.buyFromNPC(building, (int) profitCost, (int) (profitCost - cost)))
                            ChatManager.chatSystemError(playerCharacter, "Failed to Withdrawl gold from inventory. Contact CCR");

                        //train succeeded

                        dispatch = Dispatch.borrow(playerCharacter, msg);
                        DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);

                        //update trainer window

                        if (npc != null) {
                            TrainerInfoMsg tim = new TrainerInfoMsg(msg.npcType, msg.npcID, npc.getSellPercent(playerCharacter));
                            tim.setTrainPercent(npc.getSellPercent(playerCharacter));
                            dispatch = Dispatch.borrow(playerCharacter, tim);
                            DispatchMessage.dispatchMsgDispatch(dispatch, mbEnums.DispatchChannel.SECONDARY);
                        }
                    }
                }
            } catch (Exception e) {
                Logger.error(e);
            } finally {
                origin.trainLock.unlock();
            }

        }

        return true;
    }

}