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

import com.mojang.datafixers.kinds.Applicative;
import com.mojang.datafixers.kinds.App;
import java.util.function.Consumer;
import net.minecraft.world.level.levelgen.structure.templatesystem.JigsawReplacementProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.Collections;
import java.util.Random;
import java.util.Iterator;
import net.minecraft.world.level.block.state.properties.StructureMode;
import com.google.common.collect.Lists;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.core.BlockPos;
import java.util.function.Function;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import java.util.Collection;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessorType;
import java.util.List;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import java.util.Optional;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.DynamicOps;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
import com.google.common.collect.ImmutableList;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.resources.ResourceLocation;
import com.mojang.datafixers.util.Either;
import com.mojang.serialization.Codec;

public class SinglePoolElement extends StructurePoolElement {
    private static final Codec<Either<ResourceLocation, StructureTemplate>> TEMPLATE_CODEC;
    public static final Codec<SinglePoolElement> CODEC;
    protected final Either<ResourceLocation, StructureTemplate> template;
    protected final ImmutableList<StructureProcessor> processors;
    
    private static <T> DataResult<T> encodeTemplate(final Either<ResourceLocation, StructureTemplate> either, final DynamicOps<T> dynamicOps, final T object) {
        final Optional<ResourceLocation> optional4 = (Optional<ResourceLocation>)either.left();
        if (!optional4.isPresent()) {
            return (DataResult<T>)DataResult.error("Can not serialize a runtime pool element");
        }
        return (DataResult<T>)ResourceLocation.CODEC.encode(optional4.get(), (DynamicOps)dynamicOps, object);
    }
    
    protected static <E extends SinglePoolElement> RecordCodecBuilder<E, List<StructureProcessor>> processorsCodec() {
        return (RecordCodecBuilder<E, List<StructureProcessor>>)StructureProcessorType.CODEC.listOf().fieldOf("processors").forGetter(cqc -> cqc.processors);
    }
    
    protected static <E extends SinglePoolElement> RecordCodecBuilder<E, Either<ResourceLocation, StructureTemplate>> templateCodec() {
        return (RecordCodecBuilder<E, Either<ResourceLocation, StructureTemplate>>)SinglePoolElement.TEMPLATE_CODEC.fieldOf("location").forGetter(cqc -> cqc.template);
    }
    
    @Deprecated
    public SinglePoolElement(final String string, final List<StructureProcessor> list) {
        this((Either<ResourceLocation, StructureTemplate>)Either.left(new ResourceLocation(string)), list, StructureTemplatePool.Projection.RIGID);
    }
    
    protected SinglePoolElement(final Either<ResourceLocation, StructureTemplate> either, final List<StructureProcessor> list, final StructureTemplatePool.Projection a) {
        super(a);
        this.template = either;
        this.processors = ImmutableList.<StructureProcessor>copyOf(list);
    }
    
    public SinglePoolElement(final StructureTemplate cve, final List<StructureProcessor> list, final StructureTemplatePool.Projection a) {
        this((Either<ResourceLocation, StructureTemplate>)Either.right(cve), list, a);
    }
    
    @Deprecated
    public SinglePoolElement(final String string) {
        this(string, ImmutableList.of());
    }
    
    private StructureTemplate getTemplate(final StructureManager cva) {
        return (StructureTemplate)this.template.map((Function)cva::getOrCreate, (Function)Function.identity());
    }
    
    public List<StructureTemplate.StructureBlockInfo> getDataMarkers(final StructureManager cva, final BlockPos fu, final Rotation cap, final boolean boolean4) {
        final StructureTemplate cve6 = this.getTemplate(cva);
        final List<StructureTemplate.StructureBlockInfo> list7 = cve6.filterBlocks(fu, new StructurePlaceSettings().setRotation(cap), Blocks.STRUCTURE_BLOCK, boolean4);
        final List<StructureTemplate.StructureBlockInfo> list8 = Lists.newArrayList();
        for (final StructureTemplate.StructureBlockInfo c10 : list7) {
            if (c10.nbt == null) {
                continue;
            }
            final StructureMode cgq11 = StructureMode.valueOf(c10.nbt.getString("mode"));
            if (cgq11 != StructureMode.DATA) {
                continue;
            }
            list8.add(c10);
        }
        return list8;
    }
    
    @Override
    public List<StructureTemplate.StructureBlockInfo> getShuffledJigsawBlocks(final StructureManager cva, final BlockPos fu, final Rotation cap, final Random random) {
        final StructureTemplate cve6 = this.getTemplate(cva);
        final List<StructureTemplate.StructureBlockInfo> list7 = cve6.filterBlocks(fu, new StructurePlaceSettings().setRotation(cap), Blocks.JIGSAW, true);
        Collections.shuffle(list7, random);
        return list7;
    }
    
    @Override
    public BoundingBox getBoundingBox(final StructureManager cva, final BlockPos fu, final Rotation cap) {
        final StructureTemplate cve5 = this.getTemplate(cva);
        return cve5.getBoundingBox(new StructurePlaceSettings().setRotation(cap), fu);
    }
    
    @Override
    public boolean place(final StructureManager cva, final WorldGenLevel bqu, final StructureFeatureManager bqq, final ChunkGenerator cha, final BlockPos fu5, final BlockPos fu6, final Rotation cap, final BoundingBox ctd, final Random random, final boolean boolean10) {
        final StructureTemplate cve12 = this.getTemplate(cva);
        final StructurePlaceSettings cvb13 = this.getSettings(cap, ctd, boolean10);
        if (cve12.placeInWorld(bqu, fu5, fu6, cvb13, random, 18)) {
            final List<StructureTemplate.StructureBlockInfo> list14 = StructureTemplate.processBlockInfos(bqu, fu5, fu6, cvb13, this.getDataMarkers(cva, fu5, cap, false));
            for (final StructureTemplate.StructureBlockInfo c16 : list14) {
                this.handleDataMarker(bqu, c16, fu5, cap, random, ctd);
            }
            return true;
        }
        return false;
    }
    
    protected StructurePlaceSettings getSettings(final Rotation cap, final BoundingBox ctd, final boolean boolean3) {
        final StructurePlaceSettings cvb5 = new StructurePlaceSettings();
        cvb5.setBoundingBox(ctd);
        cvb5.setRotation(cap);
        cvb5.setKnownShape(true);
        cvb5.setIgnoreEntities(false);
        cvb5.addProcessor(BlockIgnoreProcessor.STRUCTURE_BLOCK);
        cvb5.setFinalizeEntities(true);
        if (!boolean3) {
            cvb5.addProcessor(JigsawReplacementProcessor.INSTANCE);
        }
        this.processors.forEach(cvb5::addProcessor);
        this.getProjection().getProcessors().forEach(cvb5::addProcessor);
        return cvb5;
    }
    
    @Override
    public StructurePoolElementType<?> getType() {
        return StructurePoolElementType.SINGLE;
    }
    
    @Override
    public String toString() {
        return "Single[" + this.template + "]";
    }
    
    static {
        TEMPLATE_CODEC = Codec.of(SinglePoolElement::encodeTemplate, ResourceLocation.CODEC.map((Function)Either::left));
        CODEC = RecordCodecBuilder.create(instance -> ((RecordCodecBuilder.Instance)instance).group((App)SinglePoolElement.<SinglePoolElement>templateCodec(), (App)SinglePoolElement.<SinglePoolElement>processorsCodec(), (App)StructurePoolElement.<StructurePoolElement>projectionCodec()).apply(instance, SinglePoolElement::new));
    }
}
