package net.wayward_realms.waywardmonsters;

import net.wayward_realms.waywardlib.character.CharacterPlugin;
import net.wayward_realms.waywardlib.classes.ClassesPlugin;
import net.wayward_realms.waywardlib.util.math.MathUtils;
import net.wayward_realms.waywardlib.util.serialisation.SerialisableLocation;
import org.bukkit.Location;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

public class EntityLevelManager {

    private WaywardMonsters plugin;

    public EntityLevelManager(WaywardMonsters plugin) {
        this.plugin = plugin;
    }

    public void addZeroPoint(Location location) {
        File zeroPointsFile = new File(plugin.getDataFolder(), "zero-points.yml");
        YamlConfiguration zeroPointsConfig = YamlConfiguration.loadConfiguration(zeroPointsFile);
        List<SerialisableLocation> zeroPoints = (List<SerialisableLocation>) zeroPointsConfig.getList("locations");
        zeroPoints.add(new SerialisableLocation(location));
        zeroPointsConfig.set("locations", zeroPoints);
        try {
            zeroPointsConfig.save(zeroPointsFile);
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }

    public void removeZeroPoint(Location location) {
        File zeroPointsFile = new File(plugin.getDataFolder(), "zero-points.yml");
        YamlConfiguration zeroPointsConfig = YamlConfiguration.loadConfiguration(zeroPointsFile);
        List<SerialisableLocation> zeroPoints = (List<SerialisableLocation>) zeroPointsConfig.getList("locations");
        zeroPoints.remove(new SerialisableLocation(location));
        zeroPointsConfig.set("locations", zeroPoints);
        try {
            zeroPointsConfig.save(zeroPointsFile);
        } catch (IOException exception) {
            exception.printStackTrace();
        }
    }

    public Collection<Location> getZeroPoints() {
        File zeroPointsFile = new File(plugin.getDataFolder(), "zero-points.yml");
        YamlConfiguration zeroPointsConfig = YamlConfiguration.loadConfiguration(zeroPointsFile);
        List<SerialisableLocation> zeroPoints = (List<SerialisableLocation>) zeroPointsConfig.getList("locations");
        List<Location> locations = new ArrayList<>();
        for (SerialisableLocation location : zeroPoints) {
            locations.add(location.toLocation());
        }
        return locations;
    }

    public int getEntityLevel(Entity entity) {
        if (entity instanceof Player) {
            RegisteredServiceProvider<CharacterPlugin> characterPluginProvider = plugin.getServer().getServicesManager().getRegistration(CharacterPlugin.class);
            if (characterPluginProvider != null) {
                CharacterPlugin characterPlugin = characterPluginProvider.getProvider();
                RegisteredServiceProvider<ClassesPlugin> classesPluginProvider = plugin.getServer().getServicesManager().getRegistration(ClassesPlugin.class);
                if (classesPluginProvider != null) {
                    ClassesPlugin classesPlugin = classesPluginProvider.getProvider();
                    return classesPlugin.getLevel(characterPlugin.getActiveCharacter((Player) entity));
                }
            }
        }
        Collection<Location> zeroPoints = getZeroPoints();
        if (zeroPoints.size() == 0) return 1;
        int minimumLevel = -1;
        for (Location zeroPoint : zeroPoints) {
            if (zeroPoint.getWorld().equals(entity.getWorld())) {
                int possibleLevel = (int) Math.floor((MathUtils.fastsqrt(entity.getLocation().distanceSquared(zeroPoint)) / plugin.getConfig().getDouble("mob-level-scale", 32D)));
                minimumLevel = possibleLevel < minimumLevel || minimumLevel == -1 ? possibleLevel : minimumLevel;
            }
        }
        return Math.max(minimumLevel - 1, 0);
    }

    public int getEntityStatValue(Entity entity) {
        return (int) Math.round(((200D * (double) getEntityLevel(entity)) / 100D) + 5D);
    }

}

@@ -38,16 +38,143 @@
 sters;%0A%0A
+import com.google.common.cache.Cache;%0Aimport com.google.common.cache.CacheBuilder;%0Aimport com.google.common.cache.CacheLoader;%0A
 import n
@@ -221,32 +221,32 @@
 haracterPlugin;%0A
-
 import net.waywa
@@ -756,16 +756,102 @@
 il.List;
+%0Aimport java.util.concurrent.ExecutionException;%0Aimport java.util.concurrent.TimeUnit;
 %0A%0Apublic
@@ -1002,23 +1002,1057 @@
 plugin;%0A
-
     %7D%0A%0A
+    private Cache%3CEntity, Integer%3E levelCache = CacheBuilder.newBuilder().%0A            maximumSize(100).%0A            expireAfterAccess(3, TimeUnit.MINUTES).%0A            build(new CacheLoader%3CEntity, Integer%3E() %7B%0A                public Integer load(Entity entity) %7B%0A                    Collection%3CLocation%3E zeroPoints = getZeroPoints();%0A                    if (zeroPoints.size() == 0) return 1;%0A                    int minimumLevel = -1;%0A                    for (Location zeroPoint : zeroPoints) %7B%0A                        if (zeroPoint.getWorld().equals(entity.getWorld())) %7B%0A                            int possibleLevel = (int) Math.floor((MathUtils.fastsqrt(entity.getLocation().distanceSquared(zeroPoint)) / plugin.getConfig().getDouble(%22mob-level-scale%22, 32D)));%0A                            minimumLevel = possibleLevel %3C minimumLevel %7C%7C minimumLevel == -1 ? possibleLevel : minimumLevel;%0A                        %7D%0A                    %7D%0A                    return Math.max(minimumLevel - 1, 0);%0A                %7D%0A            %7D);%0A%0A
     publ
@@ -4701,612 +4701,173 @@
     
-Collection%3CLocation%3E zeroPoints = getZeroPoints();%0A        if (zeroPoints.size() == 0) return 1;%0A        int minimumLevel = -1;%0A        for (Location zeroPoint : zeroPoints) %7B%0A            if (zeroPoint.getWorld().equals(entity.getWorld())) %7B%0A                int possibleLevel = (int) Math.floor((MathUtils.fastsqrt(entity.getLocation().distanceSquared(zeroPoint)) / plugin.getConfig().getDouble(%22mob-level-scale%22, 32D)));%0A                minimumLevel = possibleLevel %3C minimumLevel %7C%7C minimumLevel == -1 ? possibleLevel : minimumLevel;%0A            %7D%0A        %7D%0A        return Math.max(minimumLevel - 1, 0)
+try %7B%0A            return levelCache.get(entity);%0A        %7D catch (ExecutionException exception) %7B%0A            exception.printStackTrace();%0A        %7D%0A        return 0
 ;%0A  

Add entity level caching