package net.minecraft.world.level.biome;

import com.google.common.collect.Maps;
import com.mojang.serialization.DataResult;
import java.util.function.LongFunction;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import com.mojang.datafixers.kinds.Applicative;
import java.util.function.BiFunction;
import net.minecraft.core.Registry;
import com.mojang.datafixers.kinds.App;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.datafixers.util.Either;
import java.util.stream.Stream;
import java.util.Objects;
import java.util.Comparator;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.level.levelgen.WorldgenRandom;
import java.util.stream.IntStream;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.function.Function;
import java.util.Optional;
import com.mojang.datafixers.util.Pair;
import java.util.List;
import net.minecraft.world.level.levelgen.synth.NormalNoise;
import com.mojang.serialization.Codec;
import com.mojang.serialization.MapCodec;

public class MultiNoiseBiomeSource extends BiomeSource {
    public static final MapCodec<MultiNoiseBiomeSource> DIRECT_CODEC;
    public static final Codec<MultiNoiseBiomeSource> CODEC;
    private final NormalNoise temperatureNoise;
    private final NormalNoise humidityNoise;
    private final NormalNoise altitudeNoise;
    private final NormalNoise weirdnessNoise;
    private final List<Pair<Biome.ClimateParameters, Biome>> parameters;
    private final boolean useY;
    private final long seed;
    private final Optional<Preset> preset;
    
    private MultiNoiseBiomeSource(final long long1, final List<Pair<Biome.ClimateParameters, Biome>> list) {
        this(long1, list, Optional.<Preset>empty());
    }
    
    public MultiNoiseBiomeSource(final long long1, final List<Pair<Biome.ClimateParameters, Biome>> list, final Optional<Preset> optional) {
        super(list.stream().map(Pair::getSecond).collect(Collectors.toList()));
        this.seed = long1;
        this.preset = optional;
        final IntStream intStream6 = IntStream.rangeClosed(-7, -6);
        final IntStream intStream7 = IntStream.rangeClosed(-7, -6);
        final IntStream intStream8 = IntStream.rangeClosed(-7, -6);
        final IntStream intStream9 = IntStream.rangeClosed(-7, -6);
        this.temperatureNoise = new NormalNoise(new WorldgenRandom(long1), intStream6);
        this.humidityNoise = new NormalNoise(new WorldgenRandom(long1 + 1L), intStream7);
        this.altitudeNoise = new NormalNoise(new WorldgenRandom(long1 + 2L), intStream8);
        this.weirdnessNoise = new NormalNoise(new WorldgenRandom(long1 + 3L), intStream9);
        this.parameters = list;
        this.useY = false;
    }
    
    private static MultiNoiseBiomeSource defaultNether(final long long1) {
        final ImmutableList<Biome> immutableList3 = ImmutableList.<Biome>of(Biomes.NETHER_WASTES, Biomes.SOUL_SAND_VALLEY, Biomes.CRIMSON_FOREST, Biomes.WARPED_FOREST, Biomes.BASALT_DELTAS);
        return new MultiNoiseBiomeSource(long1, immutableList3.stream().flatMap(bre -> bre.optimalParameters().map(d -> Pair.of(d, bre))).collect(ImmutableList.toImmutableList()), Optional.<Preset>of(Preset.NETHER));
    }
    
    @Override
    protected Codec<? extends BiomeSource> codec() {
        return MultiNoiseBiomeSource.CODEC;
    }
    
    @Override
    public BiomeSource withSeed(final long long1) {
        return new MultiNoiseBiomeSource(long1, this.parameters, this.preset);
    }
    
    @Override
    public Biome getNoiseBiome(final int integer1, final int integer2, final int integer3) {
        final int integer4 = this.useY ? integer2 : 0;
        final Biome.ClimateParameters d6 = new Biome.ClimateParameters((float)this.temperatureNoise.getValue(integer1, integer4, integer3), (float)this.humidityNoise.getValue(integer1, integer4, integer3), (float)this.altitudeNoise.getValue(integer1, integer4, integer3), (float)this.weirdnessNoise.getValue(integer1, integer4, integer3), 0.0f);
        return this.parameters.stream().min(Comparator.comparing(pair -> ((Biome.ClimateParameters)pair.getFirst()).fitness(d6))).<Biome>map(Pair::getSecond).orElse(Biomes.THE_VOID);
    }
    
    public boolean stable(final long long1) {
        return this.seed == long1 && Objects.equals(this.preset, Optional.<Preset>of(Preset.NETHER));
    }
    
    static {
        DIRECT_CODEC = RecordCodecBuilder.mapCodec(instance -> ((RecordCodecBuilder.Instance)instance).group((App)Codec.LONG.fieldOf("seed").forGetter(btc -> btc.seed), (App)RecordCodecBuilder.create(instance -> instance.group((App)Biome.ClimateParameters.CODEC.fieldOf("parameters").forGetter((Function)Pair::getFirst), (App)Registry.BIOME.fieldOf("biome").forGetter((Function)Pair::getSecond)).apply((Applicative)instance, (BiFunction)Pair::of)).listOf().fieldOf("biomes").forGetter(btc -> btc.parameters)).apply(instance, (BiFunction)MultiNoiseBiomeSource::new));
        CODEC = Codec.mapEither((MapCodec)Preset.CODEC, (MapCodec)MultiNoiseBiomeSource.DIRECT_CODEC).xmap(either -> (MultiNoiseBiomeSource)either.map(pair -> ((Preset)pair.getFirst()).biomeSource((long)pair.getSecond()), (Function)Function.identity()), btc -> btc.preset.map(a -> Either.left(Pair.of((Object)a, (Object)btc.seed))).orElseGet(() -> Either.right(btc))).codec();
    }
    
    public static class Preset {
        private static final Map<ResourceLocation, Preset> BY_NAME;
        public static final MapCodec<Pair<Preset, Long>> CODEC;
        public static final Preset NETHER;
        private final ResourceLocation name;
        private final LongFunction<MultiNoiseBiomeSource> biomeSource;
        
        public Preset(final ResourceLocation uh, final LongFunction<MultiNoiseBiomeSource> longFunction) {
            this.name = uh;
            this.biomeSource = longFunction;
            Preset.BY_NAME.put(uh, this);
        }
        
        public MultiNoiseBiomeSource biomeSource(final long long1) {
            return this.biomeSource.apply(long1);
        }
        
        static {
            BY_NAME = Maps.newHashMap();
            CODEC = Codec.mapPair(ResourceLocation.CODEC.flatXmap(uh -> Optional.<Preset>ofNullable(Preset.BY_NAME.get(uh)).map(DataResult::success).orElseGet(() -> DataResult.error("Unknown preset: " + uh)), a -> DataResult.success(a.name)).fieldOf("preset"), Codec.LONG.fieldOf("seed")).stable();
            NETHER = new Preset(new ResourceLocation("nether"), long1 -> defaultNether(long1));
        }
    }
}
