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


package engine.net.client.msg;

import engine.Enum;
import engine.Enum.ProtectionState;
import engine.exception.MsgSendException;
import engine.gameManager.ChatManager;
import engine.gameManager.DbManager;
import engine.gameManager.PowersManager;
import engine.gameManager.SessionManager;
import engine.net.*;
import engine.net.client.ClientConnection;
import engine.net.client.Protocol;
import engine.objects.*;
import engine.powers.PowersBase;
import engine.server.MBServerStatics;
import org.pmw.tinylog.Logger;

import java.util.concurrent.ConcurrentHashMap;


public class TrainMsg extends ClientNetMsg {

    private int npcType;
    private int npcID;
    private int unknown01 = 1;
    private int trainCost01; //why two trainer costs?
    private int trainCost02; //why two trainer costs?
    private boolean isSkill; //true: skill; false: power
    private int token;
    private boolean unknown02 = true;
    private String ok = "";
    private int unknown03 = 0;

    /**
     * This is the general purpose constructor.
     */
    public TrainMsg() {
        super(Protocol.TRAINSKILL);
    }

    /**
     * This constructor is used by NetMsgFactory. It attempts to deserialize the
     * ByteBuffer into a message. If a BufferUnderflow occurs (based on reading
     * past the limit) then this constructor Throws that Exception to the
     * caller.
     */
    public TrainMsg(AbstractConnection origin, ByteBufferReader reader) {
        super(Protocol.TRAINSKILL, origin, reader);
    }

    public static void train(TrainMsg msg, ClientConnection origin) throws MsgSendException {
        origin.getPlayerCharacter().recalculate();
        PlayerCharacter playerCharacter = SessionManager.getPlayerCharacter(origin);
        Dispatch dispatch;

        if (playerCharacter == null)
            return;

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

        if (npc == null)
            return;

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

                if (gold == null)
                    return;

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

                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;

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

                    if (sk == null)
                        return;

                    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;
                    }

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

                    profitCost += .5f;
                    if (profitCost > playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems())
                        return;
                    Building b = npc.getBuilding();
                    if (b != null && b.getProtectionState().equals(ProtectionState.NPC))
                        b = null;

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

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

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


                    //attempt to train
                    if (sk.train(playerCharacter)) {
                        playerCharacter.getCharItemManager().buyFromNPC(b, (int) profitCost, (int) (profitCost - cost));

                        dispatch = Dispatch.borrow(playerCharacter, msg);
                        DispatchMessage.dispatchMsgDispatch(dispatch, Enum.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, Enum.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;

                    if (pb.isWeaponPower)
                        return;
                    CharacterPower cp = null;
                    if (powers.containsKey(token))
                        cp = powers.get(token);
                    if (cp == null)
                        return;

                    //attempt to train
                    float cost = (int) cp.getTrainingCost(playerCharacter, npc);
                    float profitCost = cost * npc.getSellPercent(playerCharacter);
                    profitCost += .5f;
                    if (profitCost > playerCharacter.getCharItemManager().getGoldInventory().getNumOfItems()) {
                        //	ChatManager.chatSystemError(pc, "You do not have enough gold to train this skill.");
                        return;
                    }

                    Building b = npc.getBuilding();

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

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

                        if (!playerCharacter.getCharItemManager().buyFromNPC(b, (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, Enum.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, Enum.DispatchChannel.SECONDARY);
                        }
                    }
                }
            } catch (Exception e) {
                Logger.error(e);
            } finally {
                origin.trainLock.unlock();
            }


        }


    }

    public static float getTrainPercent(NPC npc) {
        return 0f;
    }

    /**
     * Serializes the subclass specific items to the supplied NetMsgWriter.
     */
    @Override
    protected void _serialize(ByteBufferWriter writer) {
        writer.putInt(this.npcType);
        writer.putInt(this.npcID);
        writer.putInt(this.unknown01);
        writer.putInt(trainCost01);
        writer.putInt(trainCost02);
        writer.put((this.isSkill == true) ? (byte) 0x01 : (byte) 0x00);
        writer.putInt(this.token);
        writer.put((this.unknown02 == true) ? (byte) 0x01 : (byte) 0x00);
        writer.putString(this.ok);
        writer.putInt(this.unknown03);
    }

    /**
     * Deserializes the subclass specific items from the supplied NetMsgReader.
     */
    @Override
    protected void _deserialize(ByteBufferReader reader) {
        this.npcType = reader.getInt();
        this.npcID = reader.getInt();
        this.unknown01 = reader.getInt();
        this.trainCost01 = reader.getInt();
        this.trainCost02 = reader.getInt();
        this.isSkill = (reader.get() == (byte) 0x01) ? true : false;
        this.token = reader.getInt();
        this.unknown02 = (reader.get() == (byte) 0x01) ? true : false;
        this.ok = reader.getString();
        this.unknown03 = reader.getInt();
    }

    public int getToken() {
        return this.token;
    }

    public boolean isSkill() {
        return this.isSkill;
    }

    public int getNpcType() {
        return this.npcType;
    }

    public int getNpcID() {
        return this.npcID;
    }
}
