// Path: src/main/java/derpatiel/progressivediff/api/DifficultyControl.java
// public abstract class DifficultyControl {
//     public abstract int getChangeForSpawn(SpawnEventDetails details);
//     public abstract String getIdentifier();
// }
// 
// Path: src/main/java/derpatiel/progressivediff/MultiplePlayerCombineType.java
// public enum MultiplePlayerCombineType {
//     AVERAGE,
//     MIN,
//     MAX,
//     SUM,
//     CLOSEST,
//     ;
// 
//     public static String getValidValuesString() {
//         StringBuilder builder = new StringBuilder("[ ");
//         builder.append(Joiner.on(", ").join(values()));
//         builder.append(" ]");
//         return builder.toString();
//     }
// }
// 
// Path: src/main/java/derpatiel/progressivediff/SpawnEventDetails.java
// public class SpawnEventDetails {
// 
//     public EntityLiving entity;
//     public boolean fromSpawner=false;
// }
// 
// Path: src/main/java/derpatiel/progressivediff/util/LOG.java
// public class LOG {
// 
// 	public static void log(Level logLevel, Object object){
// 		FMLLog.log(ProgressiveDifficulty.MODID, logLevel, String.valueOf(object));
// 	}
// 	
// 	public static void all(Object obj){ log(Level.ALL,obj);	}
// 	public static void debug(Object obj){ log(Level.DEBUG,obj);}
// 	public static void error(Object obj){ log(Level.ERROR,obj);}
// 	public static void fatal(Object obj){ log(Level.FATAL,obj);}
// 	public static void info(Object obj){ log(Level.INFO,obj);}
// 	public static void off(Object obj){ log(Level.OFF,obj);}
// 	public static void trace(Object obj){ log(Level.TRACE,obj);}
// 	public static void warn(Object obj){ log(Level.WARN,obj);}
// 
// 	public static void infoLogEvent(String eventName, World worldIn, BlockPos pos) {
// 		LOG.info("EVENT: "+eventName+", happened on the "+(worldIn.isRemote ? "client" : "server")+ ", a tile entity is "+(worldIn.getTileEntity(pos)==null ? "NOT " : "" )+ "present.");	
// 	}
// 	
// }
// 
// Path: src/main/java/derpatiel/progressivediff/util/PlayerAreaStatAccumulator.java
// public class PlayerAreaStatAccumulator {
// 
//     public static int getStatForPlayersInArea(MultiplePlayerCombineType type, EntityLiving entity, int radius, Function<EntityPlayerMP,Integer> function){
//         List<EntityPlayerMP> playersInRange = entity.getEntityWorld().getEntitiesWithinAABB(EntityPlayerMP.class, entity.getEntityBoundingBox().grow(radius));
//         int counter = 0;
//         if(playersInRange.size()>0) {
//             switch (type) {
//                 case AVERAGE:
//                     int avgSum = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         avgSum += count;
//                     }
//                     counter = avgSum / playersInRange.size();
//                     break;
//                 case CLOSEST:
//                     EntityPlayerMP closestPlayer = (EntityPlayerMP) entity.getEntityWorld().getClosestPlayerToEntity(entity, 128.0d);
//                     counter = function.apply(closestPlayer);
//                     break;
//                 case MAX:
//                     int max = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         if (count > max) {
//                             max = count;
//                         }
//                     }
//                     counter = max;
//                     break;
//                 case MIN:
//                     int min = Integer.MAX_VALUE;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         if (count < min) {
//                             min = count;
//                         }
//                     }
//                     counter = min;
//                     break;
//                 case SUM:
//                     int sum = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         sum += count;
//                     }
//                     counter = sum;
//                     break;
//             }
//         }
//         return counter;
//     }
// 
//     public static int getStatForPlayersInArea(MultiplePlayerCombineType type, StatBase stat, EntityLiving entity, int radius) {
//         return getStatForPlayersInArea(type,entity,radius, (player)->{
//             try {
//                 return player.getStatFile().readStat(stat);
//             }catch(Exception e){
//                 //catch a case where the stat is not defined
//                 return 0;
//             }
//         });
//     }
// }


import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import derpatiel.progressivediff.api.DifficultyControl;
import derpatiel.progressivediff.MultiplePlayerCombineType;
import derpatiel.progressivediff.SpawnEventDetails;
import derpatiel.progressivediff.util.LOG;
import derpatiel.progressivediff.util.PlayerAreaStatAccumulator;
import net.minecraft.advancements.*;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
    public AdvancementControl(Map<String,Integer> addedDifficultyAchievementMap, MultiplePlayerCombineType combineType){
        this.addedDifficultyAchievementMap = addedDifficultyAchievementMap;
        this.type = combineType;
    }

    @Override
    public int getChangeForSpawn(SpawnEventDetails details) {
        int decidedPoints = PlayerAreaStatAccumulator.getStatForPlayersInArea(type,details.entity,128,(player)->getAdvancementPointsForPlayer(player));
        return decidedPoints;
    }

    private int getAdvancementPointsForPlayer(EntityPlayerMP player) {
        int sum=0;
        PlayerAdvancements playerProgress = player.getAdvancements();
        AdvancementManager advancementManager = player.getServerWorld().getAdvancementManager();
        for(String advancementId : addedDifficultyAchievementMap.keySet()){
                Advancement advancement = advancementManager.getAdvancement(new ResourceLocation(advancementId));
                if(playerProgress.getProgress(advancement).isDone()){
                    sum += addedDifficultyAchievementMap.get(advancementId);
                }
            }
        return sum;
    }

    @Override
    public String getIdentifier() {
        return IDENTIFIER;
    }

    public static Function<Configuration,List<DifficultyControl>> getFromConfig = config -> {
// Path: src/main/java/derpatiel/progressivediff/api/DifficultyControl.java
// public abstract class DifficultyControl {
//     public abstract int getChangeForSpawn(SpawnEventDetails details);
//     public abstract String getIdentifier();
// }
// 
// Path: src/main/java/derpatiel/progressivediff/MultiplePlayerCombineType.java
// public enum MultiplePlayerCombineType {
//     AVERAGE,
//     MIN,
//     MAX,
//     SUM,
//     CLOSEST,
//     ;
// 
//     public static String getValidValuesString() {
//         StringBuilder builder = new StringBuilder("[ ");
//         builder.append(Joiner.on(", ").join(values()));
//         builder.append(" ]");
//         return builder.toString();
//     }
// }
// 
// Path: src/main/java/derpatiel/progressivediff/SpawnEventDetails.java
// public class SpawnEventDetails {
// 
//     public EntityLiving entity;
//     public boolean fromSpawner=false;
// }
// 
// Path: src/main/java/derpatiel/progressivediff/util/LOG.java
// public class LOG {
// 
// 	public static void log(Level logLevel, Object object){
// 		FMLLog.log(ProgressiveDifficulty.MODID, logLevel, String.valueOf(object));
// 	}
// 	
// 	public static void all(Object obj){ log(Level.ALL,obj);	}
// 	public static void debug(Object obj){ log(Level.DEBUG,obj);}
// 	public static void error(Object obj){ log(Level.ERROR,obj);}
// 	public static void fatal(Object obj){ log(Level.FATAL,obj);}
// 	public static void info(Object obj){ log(Level.INFO,obj);}
// 	public static void off(Object obj){ log(Level.OFF,obj);}
// 	public static void trace(Object obj){ log(Level.TRACE,obj);}
// 	public static void warn(Object obj){ log(Level.WARN,obj);}
// 
// 	public static void infoLogEvent(String eventName, World worldIn, BlockPos pos) {
// 		LOG.info("EVENT: "+eventName+", happened on the "+(worldIn.isRemote ? "client" : "server")+ ", a tile entity is "+(worldIn.getTileEntity(pos)==null ? "NOT " : "" )+ "present.");	
// 	}
// 	
// }
// 
// Path: src/main/java/derpatiel/progressivediff/util/PlayerAreaStatAccumulator.java
// public class PlayerAreaStatAccumulator {
// 
//     public static int getStatForPlayersInArea(MultiplePlayerCombineType type, EntityLiving entity, int radius, Function<EntityPlayerMP,Integer> function){
//         List<EntityPlayerMP> playersInRange = entity.getEntityWorld().getEntitiesWithinAABB(EntityPlayerMP.class, entity.getEntityBoundingBox().grow(radius));
//         int counter = 0;
//         if(playersInRange.size()>0) {
//             switch (type) {
//                 case AVERAGE:
//                     int avgSum = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         avgSum += count;
//                     }
//                     counter = avgSum / playersInRange.size();
//                     break;
//                 case CLOSEST:
//                     EntityPlayerMP closestPlayer = (EntityPlayerMP) entity.getEntityWorld().getClosestPlayerToEntity(entity, 128.0d);
//                     counter = function.apply(closestPlayer);
//                     break;
//                 case MAX:
//                     int max = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         if (count > max) {
//                             max = count;
//                         }
//                     }
//                     counter = max;
//                     break;
//                 case MIN:
//                     int min = Integer.MAX_VALUE;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         if (count < min) {
//                             min = count;
//                         }
//                     }
//                     counter = min;
//                     break;
//                 case SUM:
//                     int sum = 0;
//                     for (EntityPlayerMP player : playersInRange) {
//                         int count = function.apply(player);
//                         sum += count;
//                     }
//                     counter = sum;
//                     break;
//             }
//         }
//         return counter;
//     }
// 
//     public static int getStatForPlayersInArea(MultiplePlayerCombineType type, StatBase stat, EntityLiving entity, int radius) {
//         return getStatForPlayersInArea(type,entity,radius, (player)->{
//             try {
//                 return player.getStatFile().readStat(stat);
//             }catch(Exception e){
//                 //catch a case where the stat is not defined
//                 return 0;
//             }
//         });
//     }
// }


// Path: src/main/java/derpatiel/progressivediff/controls/AdvancementControl.java
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import derpatiel.progressivediff.api.DifficultyControl;
import derpatiel.progressivediff.MultiplePlayerCombineType;
import derpatiel.progressivediff.SpawnEventDetails;
import derpatiel.progressivediff.util.LOG;
import derpatiel.progressivediff.util.PlayerAreaStatAccumulator;
import net.minecraft.advancements.*;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.common.config.Configuration;
import net.minecraftforge.common.config.Property;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

    public AdvancementControl(Map<String,Integer> addedDifficultyAchievementMap, MultiplePlayerCombineType combineType){
        this.addedDifficultyAchievementMap = addedDifficultyAchievementMap;
        this.type = combineType;
    }

    @Override
    public int getChangeForSpawn(SpawnEventDetails details) {
        int decidedPoints = PlayerAreaStatAccumulator.getStatForPlayersInArea(type,details.entity,128,(player)->getAdvancementPointsForPlayer(player));
        return decidedPoints;
    }

    private int getAdvancementPointsForPlayer(EntityPlayerMP player) {
        int sum=0;
        PlayerAdvancements playerProgress = player.getAdvancements();
        AdvancementManager advancementManager = player.getServerWorld().getAdvancementManager();
        for(String advancementId : addedDifficultyAchievementMap.keySet()){
                Advancement advancement = advancementManager.getAdvancement(new ResourceLocation(advancementId));
                if(playerProgress.getProgress(advancement).isDone()){
                    sum += addedDifficultyAchievementMap.get(advancementId);
                }
            }
        return sum;
    }

    @Override
    public String getIdentifier() {
        return IDENTIFIER;
    }

    public static Function<Configuration,List<DifficultyControl>> getFromConfig = config -> {
        List<DifficultyControl> returns = Lists.newArrayList();