package net.minecraft.world.level.levelgen.feature.structures;

import java.util.stream.Collectors;
import java.util.Arrays;
import net.minecraft.world.level.levelgen.structure.templatesystem.GravityProcessor;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
import java.util.Map;
import net.minecraft.util.StringRepresentable;
import org.apache.logging.log4j.LogManager;
import com.mojang.datafixers.kinds.Applicative;
import java.util.function.Consumer;
import net.minecraft.Util;
import java.util.function.Function;
import com.mojang.datafixers.kinds.App;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import it.unimi.dsi.fastutil.objects.ObjectArrays;
import java.util.Random;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import java.util.Iterator;
import com.google.common.collect.Lists;
import java.util.Collection;
import java.util.List;
import com.mojang.datafixers.util.Pair;
import com.google.common.collect.ImmutableList;
import net.minecraft.resources.ResourceLocation;
import com.mojang.serialization.Codec;
import org.apache.logging.log4j.Logger;

public class StructureTemplatePool {
    private static final Logger LOGGER;
    public static final Codec<StructureTemplatePool> CODEC;
    public static final StructureTemplatePool EMPTY;
    public static final StructureTemplatePool INVALID;
    private final ResourceLocation name;
    private final ImmutableList<Pair<StructurePoolElement, Integer>> rawTemplates;
    private final List<StructurePoolElement> templates;
    private final ResourceLocation fallback;
    private final Projection projection;
    private int maxSize;
    
    public StructureTemplatePool(final ResourceLocation uh1, final ResourceLocation uh2, final List<Pair<StructurePoolElement, Integer>> list, final Projection a) {
        this.maxSize = Integer.MIN_VALUE;
        this.name = uh1;
        this.rawTemplates = ImmutableList.<Pair<StructurePoolElement, Integer>>copyOf(list);
        this.templates = Lists.newArrayList();
        for (final Pair<StructurePoolElement, Integer> pair7 : list) {
            for (int integer8 = 0; integer8 < (int)pair7.getSecond(); ++integer8) {
                this.templates.add(((StructurePoolElement)pair7.getFirst()).setProjection(a));
            }
        }
        this.fallback = uh2;
        this.projection = a;
    }
    
    public int getMaxSize(final StructureManager cva) {
        if (this.maxSize == Integer.MIN_VALUE) {
            this.maxSize = this.templates.stream().mapToInt(cqd -> cqd.getBoundingBox(cva, BlockPos.ZERO, Rotation.NONE).getYSpan()).max().orElse(0);
        }
        return this.maxSize;
    }
    
    public ResourceLocation getFallback() {
        return this.fallback;
    }
    
    public StructurePoolElement getRandomTemplate(final Random random) {
        return this.templates.get(random.nextInt(this.templates.size()));
    }
    
    public List<StructurePoolElement> getShuffledTemplates(final Random random) {
        return ImmutableList.copyOf(ObjectArrays.shuffle((Object[])this.templates.<StructurePoolElement>toArray(new StructurePoolElement[0]), random));
    }
    
    public ResourceLocation getName() {
        return this.name;
    }
    
    public int size() {
        return this.templates.size();
    }
    
    static {
        LOGGER = LogManager.getLogger();
        CODEC = RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)ResourceLocation.CODEC.fieldOf("name").forGetter((Function)StructureTemplatePool::getName), (App)ResourceLocation.CODEC.fieldOf("fallback").forGetter((Function)StructureTemplatePool::getFallback), (App)Codec.mapPair(StructurePoolElement.CODEC.fieldOf("element"), Codec.INT.fieldOf("weight")).codec().listOf().promotePartial((Consumer)Util.prefix("Pool element: ", StructureTemplatePool.LOGGER::error)).fieldOf("elements").forGetter(cqf -> cqf.rawTemplates), (App)Projection.CODEC.fieldOf("projection").forGetter(cqf -> cqf.projection)).apply(instance, StructureTemplatePool::new));
        EMPTY = new StructureTemplatePool(new ResourceLocation("empty"), new ResourceLocation("empty"), ImmutableList.of(), Projection.RIGID);
        INVALID = new StructureTemplatePool(new ResourceLocation("invalid"), new ResourceLocation("invalid"), ImmutableList.of(), Projection.RIGID);
    }
    
    public enum Projection implements StringRepresentable {
        TERRAIN_MATCHING("terrain_matching", ImmutableList.of(new GravityProcessor(Heightmap.Types.WORLD_SURFACE_WG, -1))), 
        RIGID("rigid", ImmutableList.<StructureProcessor>of());
        
        public static final Codec<Projection> CODEC;
        private static final Map<String, Projection> BY_NAME;
        private final String name;
        private final ImmutableList<StructureProcessor> processors;
        
        private Projection(final String string3, final ImmutableList<StructureProcessor> immutableList) {
            this.name = string3;
            this.processors = immutableList;
        }
        
        public String getName() {
            return this.name;
        }
        
        public static Projection byName(final String string) {
            return Projection.BY_NAME.get(string);
        }
        
        public ImmutableList<StructureProcessor> getProcessors() {
            return this.processors;
        }
        
        @Override
        public String getSerializedName() {
            return this.name;
        }
        
        static {
            CODEC = StringRepresentable.<Projection>fromEnum(Projection::values, Projection::byName);
            BY_NAME = Arrays.<Projection>stream(values()).collect(Collectors.toMap(Projection::getName, a -> a));
        }
    }
}
