package engine.gameManager;

import engine.mbEnums;
import engine.objects.*;
import engine.powers.EffectsBase;
import engine.powers.poweractions.AbstractPowerAction;
import org.pmw.tinylog.Logger;

import java.util.EnumSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
// • ▌ ▄ ·.  ▄▄▄·  ▄▄ • ▪   ▄▄· ▄▄▄▄·  ▄▄▄·  ▐▄▄▄  ▄▄▄ .
// ·██ ▐███▪▐█ ▀█ ▐█ ▀ ▪██ ▐█ ▌▪▐█ ▀█▪▐█ ▀█ •█▌ ▐█▐▌·
// ▐█ ▌▐▌▐█·▄█▀▀█ ▄█ ▀█▄▐█·██ ▄▄▐█▀▀█▄▄█▀▀█ ▐█▐ ▐▌▐▀▀▀
// ██ ██▌▐█▌▐█ ▪▐▌▐█▄▪▐█▐█▌▐███▌██▄▪▐█▐█ ▪▐▌██▐ █▌▐█▄▄▌
// ▀▀  █▪▀▀▀ ▀  ▀ ·▀▀▀▀ ▀▀▀·▀▀▀ ·▀▀▀▀  ▀  ▀ ▀▀  █▪ ▀▀▀
//      Magicbane Emulator Project © 2013 - 2022
//                www.magicbane.com

public enum ItemManager {
    ITEMMANAGER;

    public static final AtomicInteger lastNegativeID = new AtomicInteger(0);

    public static Boolean ValidRace(Item item, mbEnums.MonsterType race) {

        if (item.template.item_race_req.isEmpty() && item.template.item_race_res.isEmpty())
            return true;

        if (item.template.item_race_req.isEmpty() == false)
            if (item.template.item_race_req.contains(race))
                return true;

        if (item.template.item_race_res.isEmpty() == false)
            if (item.template.item_class_res.contains(race) == false)
                return true;

        return false;
    }

    public static Boolean ValidClass(Item item, mbEnums.ClassType base, mbEnums.ClassType profession) {

        // Early exit if no entry

        if (item.template.item_class_req.isEmpty() && item.template.item_class_res.isEmpty())
            return true;

        if (item.template.item_class_req.isEmpty() == false)
            if (item.template.item_class_req.contains(base) || item.template.item_class_req.contains(profession))
                return true;

        if (item.template.item_class_res.isEmpty() == false)
            if (item.template.item_class_res.contains(base) == false && item.template.item_class_res.contains(profession) == false)
                return true;

        return false;
    }

    public static Boolean ValidDiscipline(Item item, EnumSet<mbEnums.DisciplineType> discs) {

        // Early exit if no entry

        if (item.template.item_disc_req.isEmpty() && item.template.item_disc_res.isEmpty())
            return true;

        EnumSet<mbEnums.DisciplineType> workSet = EnumSet.copyOf(discs);

        if (item.template.item_disc_req.isEmpty() == false) {

            workSet.retainAll(item.template.item_disc_req);

            if (workSet.isEmpty() == false)
                return true;
        }

        if (item.template.item_disc_res.isEmpty() == false) {

            workSet.retainAll(item.template.item_disc_res);

            if (workSet.isEmpty() == false)
                return true;
        }

        return false;
    }

    public static Boolean canCharacterEquip(Item item, AbstractCharacter character) {
        return ValidRace(item, character.absRace) && ValidClass(item, character.absBaseClass, character.absPromotionClass) && ValidDiscipline(item, character.absDisciplines);
    }

    public static boolean validForSkills(Item item, ConcurrentHashMap<String, CharacterSkill> skills) {

        CharacterSkill characterSkill;

        if (item.template.item_skill_required.isEmpty())
            return true;

        for (String skillRequired : item.template.item_skill_required.keySet()) {

            int required_value = item.template.item_skill_required.get(skillRequired);
            characterSkill = skills.get(skillRequired);

            if (characterSkill == null)
                return false;

            if (characterSkill.getModifiedAmountBeforeMods() > required_value)
                return true;

        }

        return false;
    }

    public static boolean isTwoHanded(Item item) {

        if (!item.template.item_type.equals(mbEnums.ItemType.WEAPON))
            return false;

        return item.template.item_eq_slots_and.contains(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
    }

    public static boolean isTwoHanded(ItemTemplate template) {

        if (!template.item_type.equals(mbEnums.ItemType.WEAPON))
            return false;

        return template.item_eq_slots_and.contains(EnumSet.of(mbEnums.EquipSlotType.LHELD, mbEnums.EquipSlotType.RHELD));
    }

    public static boolean isShield(Item item) {

        if (item.template.item_skill_required.containsKey("Block"))
            return true;

        return false;
    }

    public static boolean isShield(ItemTemplate template) {

        if (template.item_skill_required.containsKey("Block"))
            return true;

        return false;
    }

    public static boolean validForSlot(mbEnums.EquipSlotType slot, ConcurrentHashMap<mbEnums.EquipSlotType, Item> equipped, Item item) {

        boolean validSlot = false;

        if (equipped == null)
            return false;
        //Item not valid for slot

        if (item.template.item_eq_slots_or.contains(slot) == false)
            return false;

        // Slot is taken

        if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false)
            return false;

        // Two handed weapons take up two slots

        if ((isTwoHanded(item)) &&
                ((slot == mbEnums.EquipSlotType.LHELD && equipped.get(mbEnums.EquipSlotType.RHELD) != null) ||
                        (slot == mbEnums.EquipSlotType.RHELD && equipped.get(mbEnums.EquipSlotType.LHELD) != null)))
            return false;

        if (item.template.item_type.equals(mbEnums.ItemType.WEAPON))
            if (equipped.get(slot) != null && equipped.get(slot).equals(item) == false)
                return false;

        return true;
    }

    public static boolean canEquip(mbEnums.EquipSlotType slot, CharacterItemManager itemManager, AbstractCharacter abstractCharacter, Item item) {

        if (itemManager == null || abstractCharacter == null)
            return false;

        // Early exit for mobiles and NPCS.
        // Perhaps not needed now that mobs have skills.

        if (EnumSet.of(mbEnums.GameObjectType.NPC, mbEnums.GameObjectType.Mob).contains(abstractCharacter.getObjectType()))
            return false;

        if (!validForSlot(slot, itemManager.getEquipped(), item))
            return false;

        if (!validForSkills(item, abstractCharacter.getSkills()))
            return false;

        if (canCharacterEquip(item, abstractCharacter) == false)
            return false;

        //players can't wear 0 value items.

        return item.template.item_value != 0 || Kit.IsNoobGear(item.templateID);

    }

    public static boolean MakeItemForPlayer(int templateID, PlayerCharacter reciever, int amount) {

        boolean itemWorked = false;

        Item item = new Item(templateID);

        item.ownerID = reciever.getObjectUUID();
        item.ownerType = mbEnums.OwnerType.PlayerCharacter;
        item.containerType = mbEnums.ItemContainerType.INVENTORY;
        item.numberOfItems = amount;

        try {
            item = DbManager.ItemQueries.PERSIST(item);
            itemWorked = true;
        } catch (Exception e) {
            Logger.error(e);
        }

        if (!itemWorked)
            return false;

        reciever.charItemManager.addItemToInventory(item);
        reciever.charItemManager.updateInventory();

        return true;
    }

    public static void applyPrefixOrSuffix(Item item, int token) {

        EffectsBase effectsBase = PowersManager.getEffectByToken(token);
        AbstractPowerAction apa = PowersManager.getPowerActionByIDString(effectsBase.getIDString());

        if (apa == null)
            return;

        apa.applyEffectForItem(item, 0);
        item.getEffectNames().add(effectsBase.getIDString());
    }
}