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

package engine.objects;

import engine.Enum;
import engine.math.Vector3fImmutable;
import org.json.simple.JSONArray;
import org.json.simple.JSONObject;
import org.pmw.tinylog.Logger;

import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;

public class ItemTemplate {

    // Global template lookup
    public static HashMap<Integer, ItemTemplate> itemTemplates = new HashMap<>();

    // Template Properties
    public String obj_name;
    public boolean obj_pickable;
    public Vector3fImmutable obj_scale;
    public Vector3fImmutable obj_forward_vector;
    public Vector3fImmutable obj_default_alignment;
    public int obj_render_object;
    public int obj_icon;
    public float combat_health_current;
    public float combat_health_full;
    public HashMap<String, Float> combat_attack_resist = new HashMap<>();
    public Enum.ItemType item_type;
    public int item_eq_slots_value;
    public boolean item_eq_slots_type;
    public EnumSet<Enum.ItemEquipSlotType> item_eq_slots_or = EnumSet.noneOf(Enum.ItemEquipSlotType.class);
    public EnumSet<Enum.ItemEquipSlotType> item_eq_slots_and = EnumSet.noneOf(Enum.ItemEquipSlotType.class);
    public int item_value;
    public int item_wt;
    public float item_passive_defense_mod;
    public String item_base_name;
    public String item_dsc;
    public int item_render_object_female;
    public float item_health_full;
    public int item_parry_anim_id;
    public EnumSet<Enum.CharacterSkills> item_skill_used = EnumSet.noneOf(Enum.CharacterSkills.class);
    public EnumSet<Enum.CharacterSkills> item_skill_mastery_used = EnumSet.noneOf(Enum.CharacterSkills.class);
    // Armor specific fields
    public float item_bulk_factor;
    public int item_defense_rating;
    // Weapon specific fields
    public float item_weapon_wepspeed;
    public float item_weapon_max_range;
    public int item_weapon_projectile_id;
    public float item_weapon_projectile_speed;
    public int item_weapon_combat_idle_anim;
    public HashMap<Enum.SourceType, int[]> item_weapon_damage = new HashMap<>();
    public ArrayList<int[]> weapon_attack_anim_right = new ArrayList();
    public ArrayList<int[]> weapon_attack_anim_left = new ArrayList();
    public Enum.AttributeType item_primary_attr = Enum.AttributeType.None;
    public Enum.AttributeType item_secondary_attr = Enum.AttributeType.None;
    public EnumSet<Enum.ItemFlags> item_flags = EnumSet.noneOf(Enum.ItemFlags.class);
    public EnumSet<Enum.ItemUseFlags> item_use_flags = EnumSet.noneOf(Enum.ItemUseFlags.class);
    public int item_initial_charges;
    public HashMap<Enum.CharacterSkills, Integer> item_skill_required = new HashMap<>();
    public EnumSet<Enum.MonsterType> item_race_req = EnumSet.noneOf(Enum.MonsterType.class);
    public EnumSet<Enum.MonsterType> item_race_res = EnumSet.noneOf(Enum.MonsterType.class);

    public EnumSet<Enum.ClassType> item_class_req = EnumSet.noneOf(Enum.ClassType.class);
    public EnumSet<Enum.ClassType> item_class_res = EnumSet.noneOf(Enum.ClassType.class);
    public EnumSet<Enum.DisciplineType> item_disc_req = EnumSet.noneOf(Enum.DisciplineType.class);
    public EnumSet<Enum.DisciplineType> item_disc_res = EnumSet.noneOf(Enum.DisciplineType.class);
    public int item_level_req;
    public Enum.SexType item_sex_req;
    public HashMap<String, int[]> item_user_power_action = new HashMap<>();
    public static HashMap<String, Integer> item_power_grant = new HashMap<>();
    public HashMap<String, int[]> item_power_action = new HashMap<>();

    public ItemTemplate(JSONObject jsonObject) {

        try {
            // Reading a String

            obj_name = (String) jsonObject.get("obj_name");

            // Reading a boolean

            obj_pickable = (boolean) jsonObject.get("obj_pickable");

            // Reading floats from an array (note always check for empty arrays)

            JSONArray scaleData = (JSONArray) jsonObject.get("obj_scale");

            if (scaleData.isEmpty() == false)
                obj_scale = new Vector3fImmutable(((Double) scaleData.get(0)).floatValue(), ((Double) scaleData.get(1)).floatValue(),
                        ((Double) scaleData.get(2)).floatValue());

            JSONArray forwardVector = (JSONArray) jsonObject.get("obj_forward_vector");

            if (forwardVector.isEmpty() == false)
                obj_forward_vector = new Vector3fImmutable(((Double) forwardVector.get(0)).floatValue(), ((Double) forwardVector.get(1)).floatValue(),
                        ((Double) forwardVector.get(2)).floatValue());

            JSONArray defaultAlighment = (JSONArray) jsonObject.get("obj_default_alignment");

            if (defaultAlighment.isEmpty() == false)
                obj_default_alignment = new Vector3fImmutable(((Double) defaultAlighment.get(0)).floatValue(), ((Double) defaultAlighment.get(1)).floatValue(),
                        ((Double) defaultAlighment.get(2)).floatValue());

            // Reading an integer value

            obj_render_object = ((Long) jsonObject.get("obj_render_object")).intValue();
            obj_icon = ((Long) jsonObject.get("obj_icon")).intValue();

            // Reading float values

            combat_health_current = ((Double) jsonObject.get("combat_health_current")).floatValue();
            combat_health_full = ((Double) jsonObject.get("combat_health_full")).floatValue();

            // Reading a hashmap of floats

            JSONObject resist_json = (JSONObject) jsonObject.get("combat_attack_resist");

            for (Object key : resist_json.keySet()) {
                float resist = ((Double) resist_json.get(key)).floatValue();
                combat_attack_resist.put((String) key, resist);
            }

            // Parsing an enum

            item_type = Enum.ItemType.valueOf((String) jsonObject.get("item_type"));
            item_eq_slots_value = ((Long) jsonObject.get("item_eq_slots_value")).intValue();
            item_eq_slots_type = (boolean) jsonObject.get("item_eq_slots_type");

            // Parsing an enumset

            JSONArray eq_slots_or = (JSONArray) jsonObject.get("item_eq_slots_or");

            if (eq_slots_or.isEmpty() == false)
                for (Object o : eq_slots_or)
                    item_eq_slots_or.add(Enum.ItemEquipSlotType.valueOf((String) o));

            JSONArray eq_slots_and = (JSONArray) jsonObject.get("item_eq_slots_and");

            if (eq_slots_and.isEmpty() == false)
                for (Object o : eq_slots_and)
                    item_eq_slots_and.add(Enum.ItemEquipSlotType.valueOf((String) o));

            item_value = ((Long) jsonObject.get("item_value")).intValue();
            item_wt = ((Long) jsonObject.get("item_wt")).intValue();
            item_passive_defense_mod = ((Double) jsonObject.get("item_passive_defense_mod")).floatValue();
            item_base_name = (String) jsonObject.get("item_base_name");
            item_dsc = (String) jsonObject.get("item_dsc");
            item_render_object_female = ((Long) jsonObject.get("item_render_object_female")).intValue();
            item_health_full = ((Double) jsonObject.get("item_health_full")).floatValue();

            Object skill_used = jsonObject.get("item_skill_used");

            if (skill_used instanceof String) {
                String skilString = (String) skill_used;
                skilString = skilString.replaceAll("\\s", "");
                skilString = skilString.replaceAll(",", "");
                item_skill_used.add(Enum.CharacterSkills.valueOf(skilString));
            }

            Object mastery_used = jsonObject.get("item_skill_mastery_used");

            if (mastery_used instanceof String) {
                String masteryString = (String) mastery_used;
                masteryString = masteryString.replaceAll("\\s", "");
                masteryString = masteryString.replaceAll(",", "");
                item_skill_mastery_used.add(Enum.CharacterSkills.valueOf(masteryString));
            }

            item_parry_anim_id = ((Long) jsonObject.get("item_parry_anim_id")).intValue();

            // Fields only present for ARMOR

            if (item_type.equals(Enum.ItemType.ARMOR)) {
                item_bulk_factor = ((Double) jsonObject.get("item_bulk_factor")).floatValue();
                item_defense_rating = ((Long) jsonObject.get("item_defense_rating")).intValue();
            }

            // Fields only present for WEAPON

            if (item_type.equals(Enum.ItemType.WEAPON)) {

                JSONObject item_weapon = (JSONObject) jsonObject.get("item_weapon");
                item_weapon_wepspeed = ((Double) item_weapon.get("weapon_wepspeed")).floatValue();
                item_weapon_max_range = ((Double) item_weapon.get("weapon_max_range")).floatValue();
                item_weapon_projectile_id = ((Long) item_weapon.get("weapon_projectile_id")).intValue();
                item_weapon_projectile_speed = ((Double) item_weapon.get("weapon_projectile_speed")).floatValue();
                item_weapon_combat_idle_anim = ((Long) item_weapon.get("weapon_combat_idle_anim")).intValue();

                JSONArray weapon_damage = (JSONArray) item_weapon.get("weapon_damage");

                if (weapon_damage.isEmpty() == false)
                    for (Object o : weapon_damage) {
                        JSONObject damage_entry = (JSONObject) o;
                        Enum.SourceType sourceType = Enum.SourceType.valueOf(((String) damage_entry.get("damage_type")).toUpperCase());
                        int min = ((Long) damage_entry.get("damage_min")).intValue();
                        int max = ((Long) damage_entry.get("damage_max")).intValue();
                        int[] minMax = {min, max};
                        item_weapon_damage.put(sourceType, minMax);
                    }

                JSONArray attack_anim_right = (JSONArray) item_weapon.get("weapon_attack_anim_right");

                if (attack_anim_right.isEmpty() == false)
                    for (Object o : attack_anim_right) {
                        JSONArray animationEntry = (JSONArray) o;
                        int animation = ((Long) animationEntry.get(0)).intValue();
                        int duration = ((Long) animationEntry.get(1)).intValue();
                        weapon_attack_anim_right.add(new int[]{animation, duration});
                    }

                JSONArray attack_anim_left = (JSONArray) item_weapon.get("weapon_attack_anim_left");

                if (attack_anim_left.isEmpty() == false)
                    for (Object o : attack_anim_left) {
                        JSONArray animationEntry = (JSONArray) o;
                        int animation = ((Long) animationEntry.get(0)).intValue();
                        int duration = ((Long) animationEntry.get(1)).intValue();
                        weapon_attack_anim_left.add(new int[]{animation, duration});
                    }

                item_primary_attr = Enum.AttributeType.valueOf((String) jsonObject.get("item_primary_attr"));
                item_secondary_attr = Enum.AttributeType.valueOf((String) jsonObject.get("item_secondary_attr"));
            }

            JSONArray itemflags = (JSONArray) jsonObject.get("item_flags");

            if (itemflags.isEmpty() == false)
                for (Object o : itemflags) {
                    String flag = (String) o;
                    if (flag.equals("None") == false)
                        item_flags.add(Enum.ItemFlags.valueOf((String) o));
                }

            JSONArray itemUseflags = (JSONArray) jsonObject.get("item_use_flags");

            if (itemUseflags.isEmpty() == false)
                for (Object o : itemUseflags)
                    item_use_flags.add(Enum.ItemUseFlags.valueOf((String) o));

            item_initial_charges = ((Long) jsonObject.get("item_initial_charges")).intValue();

            JSONArray skill_required = (JSONArray) jsonObject.get("item_skill_req");

            if (skill_required.isEmpty() == false)
                for (Object o : skill_required) {
                    JSONObject skill_req = (JSONObject) o;
                    String skill_type = (String) skill_req.get("skill_type");
                    skill_type = skill_type.replaceAll("\\s", "");
                    skill_type = skill_type.replaceAll(",", "");
                    int skill_level = ((Long) skill_req.get("skill_level")).intValue();
                    item_skill_required.put(Enum.CharacterSkills.valueOf(skill_type), skill_level);
                }

            JSONObject race_required = (JSONObject) jsonObject.get("item_race_req");
            boolean restrict = ((Boolean) race_required.get("restrict"));
            JSONArray races = (JSONArray) race_required.get("races");

            if (races.isEmpty() == false)
                for (Object o : races) {
                    String race = (String) o;
                    race = race.replaceAll("\\s", "");
                    race = race.replaceAll(",", "");
                    race = race.replaceAll("-", "");

                    if (restrict)
                        item_race_res.add(Enum.MonsterType.valueOf(race));
                    else
                        item_race_req.add(Enum.MonsterType.valueOf(race));
                }

            JSONObject class_required = (JSONObject) jsonObject.get("item_class_req");
            restrict = ((Boolean) class_required.get("restrict"));
            JSONArray classes = (JSONArray) class_required.get("classes");

            if (classes.isEmpty() == false)
                for (Object o : classes) {
                    String classEntry = (String) o;
                    classEntry = classEntry.replaceAll("\\s", "");
                    classEntry = classEntry.replaceAll(",", "");

                    if (restrict)
                        item_class_res.add(Enum.ClassType.valueOf(classEntry));
                    else
                        item_class_req.add(Enum.ClassType.valueOf(classEntry));
                }

            JSONObject disc_required = (JSONObject) jsonObject.get("item_disc_req");
            restrict = ((Boolean) disc_required.get("restrict"));
            JSONArray discs = (JSONArray) disc_required.get("discs");

            if (discs.isEmpty() == false)
                for (Object o : discs) {
                    String disc = (String) o;
                    disc = disc.replaceAll("\\s", "");
                    disc = disc.replaceAll(",", "");

                    if (restrict)
                        item_disc_res.add(Enum.DisciplineType.valueOf(disc));
                    else
                        item_disc_req.add(Enum.DisciplineType.valueOf(disc));
                }

            item_level_req = ((Long) jsonObject.get("item_level_req")).intValue();
            item_sex_req = Enum.SexType.valueOf((String) jsonObject.get("item_sex_req"));

            JSONArray userPowerActions = (JSONArray) jsonObject.get("item_user_power_action");

            if (userPowerActions.isEmpty() == false)
                for (Object o : userPowerActions) {
                    JSONObject powerActionEntry = (JSONObject) o;
                    String power = (String) powerActionEntry.get("power");
                    JSONArray args = (JSONArray) powerActionEntry.get("arguments");
                    int[] arguments = {((Long) args.get(0)).intValue(), ((Long) args.get(1)).intValue()};
                    item_user_power_action.put(power, arguments);
                }

            JSONArray powerGrants = (JSONArray) jsonObject.get("item_power_grant");

            if (powerGrants.isEmpty() == false)
                for (Object o : powerGrants) {
                    JSONObject powerGrantEntry = (JSONObject) o;
                    String power_type = (String) powerGrantEntry.get("power_type");
                    int power_value = ((Long) powerGrantEntry.get("power_value")).intValue();
                    item_power_grant.put(power_type, power_value);
                }

            JSONArray item_power_actions = (JSONArray) jsonObject.get("item_power_action");

            if (item_power_actions.isEmpty() == false)
                for (Object o : item_power_actions) {
                    JSONObject powerActionEntry = (JSONObject) o;
                    String power = (String) powerActionEntry.get("power_type");
                    JSONArray power_actions = (JSONArray) powerActionEntry.get("power_actions");
                    JSONArray power_args = (JSONArray) power_actions.get(9);
                    int[] power_arguments = {((Long) power_args.get(0)).intValue(), ((Long) power_args.get(1)).intValue()};
                    item_power_action.put(power, power_arguments);
                }

        } catch (Exception e) {
            Logger.error(e);
        }

    }

}