package net.minecraft.world.level.biome;

import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilder;
import net.minecraft.util.WeighedRandom;
import java.util.Arrays;
import com.google.common.collect.Sets;
import org.apache.logging.log4j.LogManager;
import net.minecraft.resources.ResourceLocation;
import com.mojang.datafixers.kinds.Applicative;
import java.util.stream.Collector;
import java.util.Comparator;
import net.minecraft.util.StringRepresentable;
import com.mojang.datafixers.kinds.App;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.surfacebuilders.SurfaceBuilderConfiguration;
import net.minecraft.sounds.Music;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.network.chat.Component;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.FoliageColor;
import net.minecraft.world.level.GrassColor;
import java.util.Iterator;
import net.minecraft.ReportedException;
import net.minecraft.CrashReport;
import java.util.Random;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.core.SectionPos;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.LevelReader;
import net.minecraft.core.BlockPos;
import net.minecraft.util.Mth;
import java.util.function.Consumer;
import net.minecraft.world.level.levelgen.feature.Feature;
import java.util.stream.Stream;
import java.util.Collection;
import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import net.minecraft.Util;
import com.google.common.collect.Maps;
import com.google.common.collect.Lists;
import net.minecraft.world.level.levelgen.carver.CarverConfiguration;
import net.minecraft.world.level.levelgen.carver.WorldCarver;
import net.minecraft.core.Registry;
import it.unimi.dsi.fastutil.longs.Long2FloatLinkedOpenHashMap;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
import net.minecraft.world.level.levelgen.feature.StructureFeature;
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import java.util.List;
import net.minecraft.world.level.levelgen.GenerationStep;
import java.util.Map;
import net.minecraft.world.level.levelgen.surfacebuilders.ConfiguredSurfaceBuilder;
import javax.annotation.Nullable;
import net.minecraft.world.level.levelgen.synth.PerlinSimplexNoise;
import net.minecraft.core.IdMapper;
import java.util.Set;
import com.mojang.serialization.Codec;
import org.apache.logging.log4j.Logger;

public class Biome {
    public static final Logger LOGGER;
    public static final Codec<Biome> CODEC;
    public static final Set<Biome> EXPLORABLE_BIOMES;
    public static final IdMapper<Biome> MUTATED_BIOMES;
    protected static final PerlinSimplexNoise TEMPERATURE_NOISE;
    public static final PerlinSimplexNoise BIOME_INFO_NOISE;
    @Nullable
    protected String descriptionId;
    protected final float depth;
    protected final float scale;
    protected final float temperature;
    protected final float downfall;
    private final int skyColor;
    @Nullable
    protected final String parent;
    protected final ConfiguredSurfaceBuilder<?> surfaceBuilder;
    protected final BiomeCategory biomeCategory;
    protected final Precipitation precipitation;
    protected final BiomeSpecialEffects specialEffects;
    protected final Map<GenerationStep.Carving, List<ConfiguredWorldCarver<?>>> carvers;
    protected final Map<GenerationStep.Decoration, List<ConfiguredFeature<?, ?>>> features;
    protected final List<ConfiguredFeature<?, ?>> flowerFeatures;
    private final Map<StructureFeature<?>, ConfiguredStructureFeature<?, ?>> validFeatureStarts;
    private final Map<MobCategory, List<SpawnerData>> spawners;
    private final Map<EntityType<?>, MobSpawnCost> mobSpawnCosts;
    private final List<ClimateParameters> optimalParameters;
    private final ThreadLocal<Long2FloatLinkedOpenHashMap> temperatureCache;
    
    @Nullable
    public static Biome getMutatedVariant(final Biome bre) {
        return Biome.MUTATED_BIOMES.byId(Registry.BIOME.getId(bre));
    }
    
    public static <C extends CarverConfiguration> ConfiguredWorldCarver<C> makeCarver(final WorldCarver<C> cjh, final C cja) {
        return new ConfiguredWorldCarver<C>(cjh, cja);
    }
    
    protected Biome(final BiomeBuilder a) {
        this.flowerFeatures = Lists.newArrayList();
        this.mobSpawnCosts = Maps.newHashMap();
        final Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap2;
        this.temperatureCache = ThreadLocal.<Long2FloatLinkedOpenHashMap>withInitial(() -> Util.<Long2FloatLinkedOpenHashMap>make(() -> {
            long2FloatLinkedOpenHashMap2 = new Long2FloatLinkedOpenHashMap(1024, 0.25f) {
                protected void rehash(final int integer) {
                }
            };
            long2FloatLinkedOpenHashMap2.defaultReturnValue(Float.NaN);
            return long2FloatLinkedOpenHashMap2;
        }));
        if (a.surfaceBuilder == null || a.precipitation == null || a.biomeCategory == null || a.depth == null || a.scale == null || a.temperature == null || a.downfall == null || a.specialEffects == null) {
            throw new IllegalStateException("You are missing parameters to build a proper biome for " + this.getClass().getSimpleName() + "\n" + a);
        }
        this.surfaceBuilder = a.surfaceBuilder;
        this.precipitation = a.precipitation;
        this.biomeCategory = a.biomeCategory;
        this.depth = a.depth;
        this.scale = a.scale;
        this.temperature = a.temperature;
        this.downfall = a.downfall;
        this.skyColor = this.calculateSkyColor();
        this.parent = a.parent;
        this.optimalParameters = (List<ClimateParameters>)((a.optimalParameters != null) ? a.optimalParameters : ImmutableList.of());
        this.specialEffects = a.specialEffects;
        this.carvers = Maps.newHashMap();
        this.validFeatureStarts = Maps.newHashMap();
        this.features = Maps.newHashMap();
        for (final GenerationStep.Decoration b6 : GenerationStep.Decoration.values()) {
            this.features.put(b6, Lists.newArrayList());
        }
        this.spawners = Maps.newHashMap();
        for (final MobCategory apa6 : MobCategory.values()) {
            this.spawners.put(apa6, Lists.newArrayList());
        }
    }
    
    private Biome(final Precipitation f, final BiomeCategory b, final float float3, final float float4, final float float5, final float float6, final BiomeSpecialEffects bri, final int integer, final ConfiguredSurfaceBuilder<?> cvj, final Map<GenerationStep.Carving, List<ConfiguredWorldCarver<?>>> map10, final Map<GenerationStep.Decoration, List<ConfiguredFeature<?, ?>>> map11, final List<ConfiguredStructureFeature<?, ?>> list12, final Map<MobCategory, List<SpawnerData>> map13, final List<ClimateParameters> list14, final Optional<String> optional) {
        this.flowerFeatures = Lists.newArrayList();
        this.mobSpawnCosts = Maps.newHashMap();
        final Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap2;
        this.temperatureCache = ThreadLocal.<Long2FloatLinkedOpenHashMap>withInitial(() -> Util.<Long2FloatLinkedOpenHashMap>make(() -> {
            long2FloatLinkedOpenHashMap2 = new Long2FloatLinkedOpenHashMap(1024, 0.25f) {
                protected void rehash(final int integer) {
                }
            };
            long2FloatLinkedOpenHashMap2.defaultReturnValue(Float.NaN);
            return long2FloatLinkedOpenHashMap2;
        }));
        this.precipitation = f;
        this.biomeCategory = b;
        this.depth = float3;
        this.scale = float4;
        this.temperature = float5;
        this.downfall = float6;
        this.specialEffects = bri;
        this.skyColor = integer;
        this.surfaceBuilder = cvj;
        this.carvers = map10;
        this.features = map11;
        this.validFeatureStarts = list12.stream().collect(Collectors.toMap(ckc -> ckc.feature, Function.identity()));
        this.spawners = map13;
        this.optimalParameters = list14;
        this.parent = optional.orElse(null);
        map11.values().stream().flatMap(Collection::stream).filter(ckb -> ckb.feature == Feature.DECORATED_FLOWER).forEach(this.flowerFeatures::add);
    }
    
    public boolean isMutated() {
        return this.parent != null;
    }
    
    private int calculateSkyColor() {
        float float2 = this.temperature;
        float2 /= 3.0f;
        float2 = Mth.clamp(float2, -1.0f, 1.0f);
        return Mth.hsvToRgb(0.62222224f - float2 * 0.05f, 0.5f + float2 * 0.1f, 1.0f);
    }
    
    public int getSkyColor() {
        return this.skyColor;
    }
    
    protected void addSpawn(final MobCategory apa, final SpawnerData g) {
        this.spawners.get(apa).add(g);
    }
    
    protected void addMobCharge(final EntityType<?> aoq, final double double2, final double double3) {
        this.mobSpawnCosts.put(aoq, new MobSpawnCost(double3, double2));
    }
    
    public List<SpawnerData> getMobs(final MobCategory apa) {
        return this.spawners.get(apa);
    }
    
    @Nullable
    public MobSpawnCost getMobSpawnCost(final EntityType<?> aoq) {
        return this.mobSpawnCosts.get(aoq);
    }
    
    public Precipitation getPrecipitation() {
        return this.precipitation;
    }
    
    public boolean isHumid() {
        return this.getDownfall() > 0.85f;
    }
    
    public float getCreatureProbability() {
        return 0.1f;
    }
    
    protected float getTemperatureNoCache(final BlockPos fu) {
        if (fu.getY() > 64) {
            final float float3 = (float)(Biome.TEMPERATURE_NOISE.getValue(fu.getX() / 8.0f, fu.getZ() / 8.0f, false) * 4.0);
            return this.getTemperature() - (float3 + fu.getY() - 64.0f) * 0.05f / 30.0f;
        }
        return this.getTemperature();
    }
    
    public final float getTemperature(final BlockPos fu) {
        final long long3 = fu.asLong();
        final Long2FloatLinkedOpenHashMap long2FloatLinkedOpenHashMap5 = this.temperatureCache.get();
        final float float6 = long2FloatLinkedOpenHashMap5.get(long3);
        if (!Float.isNaN(float6)) {
            return float6;
        }
        final float float7 = this.getTemperatureNoCache(fu);
        if (long2FloatLinkedOpenHashMap5.size() == 1024) {
            long2FloatLinkedOpenHashMap5.removeFirstFloat();
        }
        long2FloatLinkedOpenHashMap5.put(long3, float7);
        return float7;
    }
    
    public boolean shouldFreeze(final LevelReader bqd, final BlockPos fu) {
        return this.shouldFreeze(bqd, fu, true);
    }
    
    public boolean shouldFreeze(final LevelReader bqd, final BlockPos fu, final boolean boolean3) {
        if (this.getTemperature(fu) >= 0.15f) {
            return false;
        }
        if (fu.getY() >= 0 && fu.getY() < 256 && bqd.getBrightness(LightLayer.BLOCK, fu) < 10) {
            final BlockState cfj5 = bqd.getBlockState(fu);
            final FluidState cxa6 = bqd.getFluidState(fu);
            if (cxa6.getType() == Fluids.WATER && cfj5.getBlock() instanceof LiquidBlock) {
                if (!boolean3) {
                    return true;
                }
                final boolean boolean4 = bqd.isWaterAt(fu.west()) && bqd.isWaterAt(fu.east()) && bqd.isWaterAt(fu.north()) && bqd.isWaterAt(fu.south());
                if (!boolean4) {
                    return true;
                }
            }
        }
        return false;
    }
    
    public boolean shouldSnow(final LevelReader bqd, final BlockPos fu) {
        if (this.getTemperature(fu) >= 0.15f) {
            return false;
        }
        if (fu.getY() >= 0 && fu.getY() < 256 && bqd.getBrightness(LightLayer.BLOCK, fu) < 10) {
            final BlockState cfj4 = bqd.getBlockState(fu);
            if (cfj4.isAir() && Blocks.SNOW.defaultBlockState().canSurvive(bqd, fu)) {
                return true;
            }
        }
        return false;
    }
    
    public void addFeature(final GenerationStep.Decoration b, final ConfiguredFeature<?, ?> ckb) {
        if (ckb.feature == Feature.DECORATED_FLOWER) {
            this.flowerFeatures.add(ckb);
        }
        this.features.get(b).add(ckb);
    }
    
    public <C extends CarverConfiguration> void addCarver(final GenerationStep.Carving a, final ConfiguredWorldCarver<C> cjc) {
        this.carvers.computeIfAbsent(a, a -> Lists.newArrayList()).add(cjc);
    }
    
    public List<ConfiguredWorldCarver<?>> getCarvers(final GenerationStep.Carving a) {
        return this.carvers.computeIfAbsent(a, a -> Lists.newArrayList());
    }
    
    public void addStructureStart(final ConfiguredStructureFeature<?, ?> ckc) {
        this.validFeatureStarts.put(ckc.feature, ckc);
    }
    
    public boolean isValidStart(final StructureFeature<?> cml) {
        return this.validFeatureStarts.containsKey(cml);
    }
    
    public Iterable<ConfiguredStructureFeature<?, ?>> structures() {
        return this.validFeatureStarts.values();
    }
    
    public ConfiguredStructureFeature<?, ?> withBiomeConfig(final ConfiguredStructureFeature<?, ?> ckc) {
        return this.validFeatureStarts.getOrDefault(ckc.feature, ckc);
    }
    
    public List<ConfiguredFeature<?, ?>> getFlowerFeatures() {
        return this.flowerFeatures;
    }
    
    public List<ConfiguredFeature<?, ?>> getFeaturesForStep(final GenerationStep.Decoration b) {
        return this.features.get(b);
    }
    
    public void generate(final GenerationStep.Decoration b, final StructureFeatureManager bqq, final ChunkGenerator cha, final WorldGenLevel bqu, final long long5, final WorldgenRandom ciy, final BlockPos fu) {
        int integer10 = 0;
        if (bqq.shouldGenerateFeatures()) {
            for (final StructureFeature<?> cml12 : Registry.STRUCTURE_FEATURE) {
                if (cml12.step() != b) {
                    continue;
                }
                ciy.setFeatureSeed(long5, integer10, b.ordinal());
                final int integer11 = fu.getX() >> 4;
                final int integer12 = fu.getZ() >> 4;
                final int integer13 = integer11 << 4;
                final int integer14 = integer12 << 4;
                try {
                    final int integer15;
                    final int integer16;
                    bqq.startsForFeature(SectionPos.of(fu), cml12).forEach(ctz -> ctz.placeInChunk(bqu, bqq, cha, ciy, new BoundingBox(integer15, integer16, integer15 + 15, integer16 + 15), new ChunkPos(integer11, integer12)));
                }
                catch (Exception exception17) {
                    final CrashReport j18 = CrashReport.forThrowable(exception17, "Feature placement");
                    j18.addCategory("Feature").setDetail("Id", Registry.STRUCTURE_FEATURE.getKey(cml12)).setDetail("Description", () -> cml12.toString());
                    throw new ReportedException(j18);
                }
                ++integer10;
            }
        }
        for (final ConfiguredFeature<?, ?> ckb12 : this.features.get(b)) {
            ciy.setFeatureSeed(long5, integer10, b.ordinal());
            try {
                ckb12.place(bqu, bqq, cha, ciy, fu);
            }
            catch (Exception exception18) {
                final CrashReport j19 = CrashReport.forThrowable(exception18, "Feature placement");
                j19.addCategory("Feature").setDetail("Id", Registry.FEATURE.getKey(ckb12.feature)).setDetail("Config", ckb12.config).setDetail("Description", () -> ckb12.feature.toString());
                throw new ReportedException(j19);
            }
            ++integer10;
        }
    }
    
    public int getFogColor() {
        return this.specialEffects.getFogColor();
    }
    
    public int getGrassColor(final double double1, final double double2) {
        final double double3 = Mth.clamp(this.getTemperature(), 0.0f, 1.0f);
        final double double4 = Mth.clamp(this.getDownfall(), 0.0f, 1.0f);
        return GrassColor.get(double3, double4);
    }
    
    public int getFoliageColor() {
        final double double2 = Mth.clamp(this.getTemperature(), 0.0f, 1.0f);
        final double double3 = Mth.clamp(this.getDownfall(), 0.0f, 1.0f);
        return FoliageColor.get(double2, double3);
    }
    
    public void buildSurfaceAt(final Random random, final ChunkAccess cgy, final int integer3, final int integer4, final int integer5, final double double6, final BlockState cfj7, final BlockState cfj8, final int integer9, final long long10) {
        this.surfaceBuilder.initNoise(long10);
        this.surfaceBuilder.apply(random, cgy, this, integer3, integer4, integer5, double6, cfj7, cfj8, integer9, long10);
    }
    
    public BiomeTempCategory getTemperatureCategory() {
        if (this.biomeCategory == BiomeCategory.OCEAN) {
            return BiomeTempCategory.OCEAN;
        }
        if (this.getTemperature() < 0.2) {
            return BiomeTempCategory.COLD;
        }
        if (this.getTemperature() < 1.0) {
            return BiomeTempCategory.MEDIUM;
        }
        return BiomeTempCategory.WARM;
    }
    
    public final float getDepth() {
        return this.depth;
    }
    
    public final float getDownfall() {
        return this.downfall;
    }
    
    public Component getName() {
        return new TranslatableComponent(this.getDescriptionId());
    }
    
    public String getDescriptionId() {
        if (this.descriptionId == null) {
            this.descriptionId = Util.makeDescriptionId("biome", Registry.BIOME.getKey(this));
        }
        return this.descriptionId;
    }
    
    public final float getScale() {
        return this.scale;
    }
    
    public final float getTemperature() {
        return this.temperature;
    }
    
    public BiomeSpecialEffects getSpecialEffects() {
        return this.specialEffects;
    }
    
    public final int getWaterColor() {
        return this.specialEffects.getWaterColor();
    }
    
    public final int getWaterFogColor() {
        return this.specialEffects.getWaterFogColor();
    }
    
    public Optional<AmbientParticleSettings> getAmbientParticle() {
        return this.specialEffects.getAmbientParticleSettings();
    }
    
    public Optional<SoundEvent> getAmbientLoop() {
        return this.specialEffects.getAmbientLoopSoundEvent();
    }
    
    public Optional<AmbientMoodSettings> getAmbientMood() {
        return this.specialEffects.getAmbientMoodSettings();
    }
    
    public Optional<AmbientAdditionsSettings> getAmbientAdditions() {
        return this.specialEffects.getAmbientAdditionsSettings();
    }
    
    public Optional<Music> getBackgroundMusic() {
        return this.specialEffects.getBackgroundMusic();
    }
    
    public final BiomeCategory getBiomeCategory() {
        return this.biomeCategory;
    }
    
    public ConfiguredSurfaceBuilder<?> getSurfaceBuilder() {
        return this.surfaceBuilder;
    }
    
    public SurfaceBuilderConfiguration getSurfaceBuilderConfig() {
        return (SurfaceBuilderConfiguration)this.surfaceBuilder.getSurfaceBuilderConfiguration();
    }
    
    public Stream<ClimateParameters> optimalParameters() {
        return this.optimalParameters.stream();
    }
    
    @Nullable
    public String getParent() {
        return this.parent;
    }
    
    static {
        LOGGER = LogManager.getLogger();
        CODEC = RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)Precipitation.CODEC.fieldOf("precipitation").forGetter(bre -> bre.precipitation), (App)BiomeCategory.CODEC.fieldOf("category").forGetter(bre -> bre.biomeCategory), (App)Codec.FLOAT.fieldOf("depth").forGetter(bre -> bre.depth), (App)Codec.FLOAT.fieldOf("scale").forGetter(bre -> bre.scale), (App)Codec.FLOAT.fieldOf("temperature").forGetter(bre -> bre.temperature), (App)Codec.FLOAT.fieldOf("downfall").forGetter(bre -> bre.downfall), (App)BiomeSpecialEffects.CODEC.fieldOf("effects").forGetter(bre -> bre.specialEffects), (App)Codec.INT.fieldOf("sky_color").forGetter(bre -> bre.skyColor), (App)ConfiguredSurfaceBuilder.CODEC.fieldOf("surface_builder").forGetter(bre -> bre.surfaceBuilder), (App)Codec.simpleMap((Codec)GenerationStep.Carving.CODEC, ConfiguredWorldCarver.CODEC.listOf().promotePartial((Consumer)Util.prefix("Carver: ", Biome.LOGGER::error)), StringRepresentable.keys(GenerationStep.Carving.values())).fieldOf("carvers").forGetter(bre -> bre.carvers), (App)Codec.simpleMap((Codec)GenerationStep.Decoration.CODEC, ConfiguredFeature.CODEC.listOf().promotePartial((Consumer)Util.prefix("Feature: ", Biome.LOGGER::error)), StringRepresentable.keys(GenerationStep.Decoration.values())).fieldOf("features").forGetter(bre -> bre.features), (App)ConfiguredStructureFeature.CODEC.listOf().promotePartial((Consumer)Util.prefix("Structure start: ", Biome.LOGGER::error)).fieldOf("starts").forGetter(bre -> bre.validFeatureStarts.values().stream().sorted(Comparator.comparing(ckc -> Registry.STRUCTURE_FEATURE.getKey(ckc.feature))).collect(Collectors.<ConfiguredStructureFeature<?, ?>>toList())), (App)Codec.simpleMap((Codec)MobCategory.CODEC, SpawnerData.CODEC.listOf().promotePartial((Consumer)Util.prefix("Spawn data: ", Biome.LOGGER::error)), StringRepresentable.keys(MobCategory.values())).fieldOf("spawners").forGetter(bre -> bre.spawners), (App)ClimateParameters.CODEC.listOf().fieldOf("climate_parameters").forGetter(bre -> bre.optimalParameters), (App)Codec.STRING.optionalFieldOf("parent").forGetter(bre -> Optional.<String>ofNullable(bre.parent))).apply(instance, Biome::new));
        EXPLORABLE_BIOMES = Sets.newHashSet();
        MUTATED_BIOMES = new IdMapper<Biome>();
        TEMPERATURE_NOISE = new PerlinSimplexNoise(new WorldgenRandom(1234L), ImmutableList.<Integer>of(0));
        BIOME_INFO_NOISE = new PerlinSimplexNoise(new WorldgenRandom(2345L), ImmutableList.<Integer>of(0));
    }
    
    public enum BiomeTempCategory {
        OCEAN("ocean"), 
        COLD("cold"), 
        MEDIUM("medium"), 
        WARM("warm");
        
        private static final Map<String, BiomeTempCategory> BY_NAME;
        private final String name;
        
        private BiomeTempCategory(final String string3) {
            this.name = string3;
        }
        
        public String getName() {
            return this.name;
        }
        
        static {
            BY_NAME = Arrays.<BiomeTempCategory>stream(values()).collect(Collectors.toMap(BiomeTempCategory::getName, c -> c));
        }
    }
    
    public enum BiomeCategory implements StringRepresentable {
        NONE("none"), 
        TAIGA("taiga"), 
        EXTREME_HILLS("extreme_hills"), 
        JUNGLE("jungle"), 
        MESA("mesa"), 
        PLAINS("plains"), 
        SAVANNA("savanna"), 
        ICY("icy"), 
        THEEND("the_end"), 
        BEACH("beach"), 
        FOREST("forest"), 
        OCEAN("ocean"), 
        DESERT("desert"), 
        RIVER("river"), 
        SWAMP("swamp"), 
        MUSHROOM("mushroom"), 
        NETHER("nether");
        
        public static final Codec<BiomeCategory> CODEC;
        private static final Map<String, BiomeCategory> BY_NAME;
        private final String name;
        
        private BiomeCategory(final String string3) {
            this.name = string3;
        }
        
        public String getName() {
            return this.name;
        }
        
        public static BiomeCategory byName(final String string) {
            return BiomeCategory.BY_NAME.get(string);
        }
        
        @Override
        public String getSerializedName() {
            return this.name;
        }
        
        static {
            CODEC = StringRepresentable.<BiomeCategory>fromEnum(BiomeCategory::values, BiomeCategory::byName);
            BY_NAME = Arrays.<BiomeCategory>stream(values()).collect(Collectors.toMap(BiomeCategory::getName, b -> b));
        }
    }
    
    public enum Precipitation implements StringRepresentable {
        NONE("none"), 
        RAIN("rain"), 
        SNOW("snow");
        
        public static final Codec<Precipitation> CODEC;
        private static final Map<String, Precipitation> BY_NAME;
        private final String name;
        
        private Precipitation(final String string3) {
            this.name = string3;
        }
        
        public String getName() {
            return this.name;
        }
        
        public static Precipitation byName(final String string) {
            return Precipitation.BY_NAME.get(string);
        }
        
        @Override
        public String getSerializedName() {
            return this.name;
        }
        
        static {
            CODEC = StringRepresentable.<Precipitation>fromEnum(Precipitation::values, Precipitation::byName);
            BY_NAME = Arrays.<Precipitation>stream(values()).collect(Collectors.toMap(Precipitation::getName, f -> f));
        }
    }
    
    public static class MobSpawnCost {
        private final double energyBudget;
        private final double charge;
        
        public MobSpawnCost(final double double1, final double double2) {
            this.energyBudget = double1;
            this.charge = double2;
        }
        
        public double getEnergyBudget() {
            return this.energyBudget;
        }
        
        public double getCharge() {
            return this.charge;
        }
    }
    
    public static class SpawnerData extends WeighedRandom.WeighedRandomItem {
        public static final Codec<SpawnerData> CODEC;
        public final EntityType<?> type;
        public final int minCount;
        public final int maxCount;
        
        public SpawnerData(final EntityType<?> aoq, final int integer2, final int integer3, final int integer4) {
            super(integer2);
            this.type = ((aoq.getCategory() == MobCategory.MISC) ? EntityType.PIG : aoq);
            this.minCount = integer3;
            this.maxCount = integer4;
        }
        
        @Override
        public String toString() {
            return EntityType.getKey(this.type) + "*(" + this.minCount + "-" + this.maxCount + "):" + this.weight;
        }
        
        static {
            CODEC = RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)Registry.ENTITY_TYPE.fieldOf("type").forGetter(g -> g.type), (App)Codec.INT.fieldOf("weight").forGetter(g -> g.weight), (App)Codec.INT.fieldOf("minCount").forGetter(g -> g.minCount), (App)Codec.INT.fieldOf("maxCount").forGetter(g -> g.maxCount)).apply(instance, SpawnerData::new));
        }
    }
    
    public static class BiomeBuilder {
        @Nullable
        private ConfiguredSurfaceBuilder<?> surfaceBuilder;
        @Nullable
        private Precipitation precipitation;
        @Nullable
        private BiomeCategory biomeCategory;
        @Nullable
        private Float depth;
        @Nullable
        private Float scale;
        @Nullable
        private Float temperature;
        @Nullable
        private Float downfall;
        @Nullable
        private String parent;
        @Nullable
        private List<ClimateParameters> optimalParameters;
        @Nullable
        private BiomeSpecialEffects specialEffects;
        
        public <SC extends SurfaceBuilderConfiguration> BiomeBuilder surfaceBuilder(final SurfaceBuilder<SC> cvw, final SC cvy) {
            this.surfaceBuilder = new ConfiguredSurfaceBuilder<>(cvw, cvy);
            return this;
        }
        
        public BiomeBuilder surfaceBuilder(final ConfiguredSurfaceBuilder<?> cvj) {
            this.surfaceBuilder = cvj;
            return this;
        }
        
        public BiomeBuilder precipitation(final Precipitation f) {
            this.precipitation = f;
            return this;
        }
        
        public BiomeBuilder biomeCategory(final BiomeCategory b) {
            this.biomeCategory = b;
            return this;
        }
        
        public BiomeBuilder depth(final float float1) {
            this.depth = float1;
            return this;
        }
        
        public BiomeBuilder scale(final float float1) {
            this.scale = float1;
            return this;
        }
        
        public BiomeBuilder temperature(final float float1) {
            this.temperature = float1;
            return this;
        }
        
        public BiomeBuilder downfall(final float float1) {
            this.downfall = float1;
            return this;
        }
        
        public BiomeBuilder parent(@Nullable final String string) {
            this.parent = string;
            return this;
        }
        
        public BiomeBuilder optimalParameters(final List<ClimateParameters> list) {
            this.optimalParameters = list;
            return this;
        }
        
        public BiomeBuilder specialEffects(final BiomeSpecialEffects bri) {
            this.specialEffects = bri;
            return this;
        }
        
        @Override
        public String toString() {
            return "BiomeBuilder{\nsurfaceBuilder=" + this.surfaceBuilder + ",\nprecipitation=" + this.precipitation + ",\nbiomeCategory=" + this.biomeCategory + ",\ndepth=" + this.depth + ",\nscale=" + this.scale + ",\ntemperature=" + this.temperature + ",\ndownfall=" + this.downfall + ",\nspecialEffects=" + this.specialEffects + ",\nparent='" + this.parent + '\'' + "\n" + '}';
        }
    }
    
    public static class ClimateParameters {
        public static final Codec<ClimateParameters> CODEC;
        private final float temperature;
        private final float humidity;
        private final float altitude;
        private final float weirdness;
        private final float offset;
        
        public ClimateParameters(final float float1, final float float2, final float float3, final float float4, final float float5) {
            this.temperature = float1;
            this.humidity = float2;
            this.altitude = float3;
            this.weirdness = float4;
            this.offset = float5;
        }
        
        @Override
        public boolean equals(final Object object) {
            if (this == object) {
                return true;
            }
            if (object == null || this.getClass() != object.getClass()) {
                return false;
            }
            final ClimateParameters d3 = (ClimateParameters)object;
            return Float.compare(d3.temperature, this.temperature) == 0 && Float.compare(d3.humidity, this.humidity) == 0 && Float.compare(d3.altitude, this.altitude) == 0 && Float.compare(d3.weirdness, this.weirdness) == 0;
        }
        
        @Override
        public int hashCode() {
            int integer2 = (this.temperature != 0.0f) ? Float.floatToIntBits(this.temperature) : 0;
            integer2 = 31 * integer2 + ((this.humidity != 0.0f) ? Float.floatToIntBits(this.humidity) : 0);
            integer2 = 31 * integer2 + ((this.altitude != 0.0f) ? Float.floatToIntBits(this.altitude) : 0);
            integer2 = 31 * integer2 + ((this.weirdness != 0.0f) ? Float.floatToIntBits(this.weirdness) : 0);
            return integer2;
        }
        
        public float fitness(final ClimateParameters d) {
            return (this.temperature - d.temperature) * (this.temperature - d.temperature) + (this.humidity - d.humidity) * (this.humidity - d.humidity) + (this.altitude - d.altitude) * (this.altitude - d.altitude) + (this.weirdness - d.weirdness) * (this.weirdness - d.weirdness) + (this.offset - d.offset) * (this.offset - d.offset);
        }
        
        static {
            CODEC = RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)Codec.FLOAT.fieldOf("temperature").forGetter(d -> d.temperature), (App)Codec.FLOAT.fieldOf("humidity").forGetter(d -> d.humidity), (App)Codec.FLOAT.fieldOf("altitude").forGetter(d -> d.altitude), (App)Codec.FLOAT.fieldOf("weirdness").forGetter(d -> d.weirdness), (App)Codec.FLOAT.fieldOf("offset").forGetter(d -> d.offset)).apply(instance, ClimateParameters::new));
        }
    }
}
