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


package engine.objects;

import engine.Enum;
import engine.Enum.GameObjectType;
import engine.Enum.ItemType;
import engine.gameManager.DbManager;
import org.pmw.tinylog.Logger;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

public class ItemBase {

    public static ItemBase GOLD_ITEM_BASE = null;
    public static int GOLD_BASE_ID = 7;
    public static ArrayList<Integer> AnniversaryGifts = new ArrayList<>();
    public static HashMap<Integer, ItemBase> _itemBaseByUUID = new HashMap<>();
    // Internal cache
    private static final HashMap<Integer, Integer> itemHashIDMap = new HashMap<>();
    private static final HashMap<String, Integer> _IDsByNames = new HashMap<>();
    private static final ArrayList<ItemBase> _resourceList = new ArrayList<>();
    private final int uuid;

//requirements/restrictions
    private final ItemType type;
    private int vendorType;
    private final int modTable;
    private final int useID;
    private int hashID;
    private final byte useAmount;

    // Armor and weapon related values
    private final String skillRequired;
    private final short percentRequired;
    private final float blockMod;
    private final short defense;
    private final float dexPenalty;
    private final float speed;
    private final float range;
    private final short minDamage;
    private final short maxDamage;
    private final String mastery;
    private final engine.Enum.SourceType damageType;
    private final boolean twoHanded;
    private boolean isConsumable;

    // Item stat modifiers
    private final HashMap<Integer, Integer> bakedInStats = new HashMap<>();
    private final HashMap<Integer, Integer> usedStats = new HashMap<>();
    private final float parryBonus;
    private final boolean isStrBased;
    private ArrayList<Integer> animations = new ArrayList<>();
    private ArrayList<Integer> offHandAnimations = new ArrayList<>();

    /**
     * ResultSet Constructor
     */
    public ItemBase(ResultSet rs) throws SQLException {

        this.uuid = rs.getInt("ID");

        this.type = ItemType.valueOf(rs.getString("Type"));
        this.useID = rs.getInt("useID");
        this.vendorType = rs.getInt("vendorType");
        this.useAmount = rs.getByte("useAmount");
        this.modTable = rs.getInt("modTable");
        this.hashID = rs.getInt("itemHashID");

        this.isConsumable = false;
        this.skillRequired = rs.getString("skillRequired");
        this.percentRequired = rs.getShort("percentRequired");
        this.blockMod = rs.getFloat("blockMod");
        this.defense = rs.getShort("defense");
        this.dexPenalty = rs.getFloat("dexPenalty");
        this.parryBonus = rs.getFloat("parryBonus");
        this.isStrBased = (rs.getInt("isStrBased") == 1);
        this.speed = rs.getFloat("speed");
        this.range = rs.getFloat("range");
        this.minDamage = rs.getShort("minDamage");
        this.maxDamage = rs.getShort("maxDamage");

        this.mastery = rs.getString("mastery");
        damageType = Enum.SourceType.valueOf(rs.getString("damageType").toUpperCase());

        this.twoHanded = (rs.getInt("twoHanded") == 1);

        switch (this.type) {
            case RUNE:
            case SCROLL:
            case COMMANDROD:
            case POTION:
            case TEARS:
            case GUILDCHARTER:
            case DEED:
            case CONTRACT:
            case WATERBUCKET:
            case REALMCHARTER:
            case GIFT:
                this.isConsumable = true;
                break;
            case OFFERING:
                this.isConsumable = true;
                Boon.HandleBoonListsForItemBase(uuid);
                break;
        }

        try {
            DbManager.ItemBaseQueries.LOAD_ANIMATIONS(this);
        } catch (Exception e) {
            Logger.error(e.getMessage());
        }
        initBakedInStats();
        initializeHashes();

    }

    public static void addToCache(ItemBase itemBase) {

        _itemBaseByUUID.put(itemBase.uuid, itemBase);

        if (itemBase.type.equals(ItemType.RESOURCE))
            _resourceList.add(itemBase);

        ItemTemplate template = ItemTemplate.itemTemplates.get(itemBase.uuid);

        if (template == null)
            Logger.error("Null template for: " + itemBase.uuid);
        else
            _IDsByNames.put(template.item_base_name.toLowerCase().replace(" ", "_"), itemBase.uuid);
    }

    public static HashMap<Integer, Integer> getItemHashIDMap() {
        return itemHashIDMap;
    }

    /*
     * Database
     */
    public static ItemBase getItemBase(int uuid) {

        return _itemBaseByUUID.get(uuid);
    }

    /**
     * Get the ItemBase instance for Gold.
     *
     * @return ItemBase for Gold
     */
    public static ItemBase getGoldItemBase() {
        if (ItemBase.GOLD_ITEM_BASE == null)
            ItemBase.GOLD_ITEM_BASE = getItemBase(7);
        return ItemBase.GOLD_ITEM_BASE;
    }

    public static int getIDByName(String name) {
        if (ItemBase._IDsByNames.containsKey(name))
            return ItemBase._IDsByNames.get(name);
        return 0;
    }

    /**
     * @return the _itemBaseByUUID
     */
    public static HashMap<Integer, ItemBase> getUUIDCache() {
        return _itemBaseByUUID;
    }

    /**
     * @return the _resourceList
     */
    public static ArrayList<ItemBase> getResourceList() {
        return _resourceList;
    }

    public static void loadAllItemBases() {
        DbManager.ItemBaseQueries.LOAD_ALL_ITEMBASES();

        AnniversaryGifts.add(971000);
        AnniversaryGifts.add(971001);
        AnniversaryGifts.add(971002);
        AnniversaryGifts.add(971003);
        AnniversaryGifts.add(971004);
        AnniversaryGifts.add(971005);
        AnniversaryGifts.add(971006);
        AnniversaryGifts.add(971007);
        AnniversaryGifts.add(971008);
        AnniversaryGifts.add(971009);
        AnniversaryGifts.add(971010);
        AnniversaryGifts.add(5101000);
        AnniversaryGifts.add(5101020);
        AnniversaryGifts.add(5101100);
        AnniversaryGifts.add(5101120);
        AnniversaryGifts.add(5101040);
        AnniversaryGifts.add(5101140);
        AnniversaryGifts.add(5101060);
        AnniversaryGifts.add(5101080);

    }

    private void initBakedInStats() {
        DbManager.ItemBaseQueries.LOAD_BAKEDINSTATS(this);
    }

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

    public boolean isRune() {
        int ID = uuid;
        if (ID > 2499 && ID < 3050) //class, discipline runes
            return true;
        else
            return ID > 249999 && ID < 252137;
    }

    public boolean isDiscRune() {
        int ID = uuid;
        //class, discipline runes
        return ID > 2499 && ID < 3050;
    }

    public boolean isGlass() {
        int ID = uuid;
        return ID > 7000099 && ID < 7000281;
    }

    //returns powers tokens baked in to item
    public HashMap<Integer, Integer> getBakedInStats() {
        return this.bakedInStats;
    }

    //returns power tokens granted when using item, such as scrolls and potions
    public HashMap<Integer, Integer> getUsedStats() {
        return this.usedStats;
    }

    public final void initializeHashes() {
        itemHashIDMap.put(this.hashID, uuid);

    }

    public ItemType getType() {
        return this.type;
    }

    public int getUseID() {
        return this.useID;
    }

    public byte getUseAmount() {
        return this.useAmount;
    }

    public int getModTable() {
        return modTable;
    }

    public int getHashID() {
        return hashID;
    }

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

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

        if (abstractCharacter.getObjectType().equals(GameObjectType.PlayerCharacter)) {

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

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

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

            return item.template.item_value != 0 || Kit.IsNoobGear(item.getItemBase().uuid);
            //players can't wear 0 value items.

        }

        return true; //Mobiles and NPC's don't need to check equip
    }

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

        boolean validSlot = false;

        if (equipped == null)
            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 ((ItemTemplate.isTwoHanded(item)) &&
                ((slot == Enum.EquipSlotType.LHELD && equipped.get(Enum.EquipSlotType.RHELD) != null) ||
                        (slot == Enum.EquipSlotType.RHELD && equipped.get(Enum.EquipSlotType.LHELD) != null)))
            return false;

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

        return true;
    }

    /**
     * @return the uuid
     */
    public final int getUUID() {
        return uuid;
    }

    public boolean isTwoHanded() {
        return this.twoHanded;
    }

    /**
     * @return the skillRequired
     */
    public String getSkillRequired() {
        return skillRequired;
    }

    /**
     * @return the mastery
     */
    public String getMastery() {
        return mastery;
    }

    /**
     * @return the defense
     */
    public short getDefense() {
        return defense;
    }

    /**
     * @return the dexPenalty
     */
    public float getDexPenalty() {
        return dexPenalty;
    }

    /**
     * @return the speed
     */
    public float getSpeed() {
        return speed;
    }

    /**
     * @return the range
     */
    public float getRange() {
        return range;
    }

    /**
     * @return the isStrBased
     */
    public boolean isStrBased() {
        return isStrBased;
    }

    /**
     * @return the maxDamage
     */
    public short getMaxDamage() {
        return maxDamage;
    }

    /**
     * @return the minDamage
     */
    public short getMinDamage() {
        return minDamage;
    }

    /**
     * @return the damageType
     */
    public Enum.SourceType getDamageType() {
        return damageType;
    }

    public short getPercentRequired() {
        return percentRequired;
    }

    public ArrayList<Integer> getAnimations() {
        return animations;
    }

    public void setAnimations(ArrayList<Integer> animations) {
        this.animations = animations;
    }

    public ArrayList<Integer> getOffHandAnimations() {
        return offHandAnimations;
    }

    public void setOffHandAnimations(ArrayList<Integer> offHandAnimations) {
        this.offHandAnimations = offHandAnimations;
    }

}