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

import java.util.Iterator;
import java.util.Collection;
import com.google.common.collect.Lists;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.JigsawBlock;
import net.minecraft.core.Direction;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.phys.shapes.BooleanOp;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.block.Rotation;
import com.google.common.collect.Queues;
import java.util.Deque;
import net.minecraft.world.phys.shapes.VoxelShape;
import java.util.concurrent.atomic.AtomicReference;
import net.minecraft.world.level.levelgen.structure.PoolElementStructurePiece;
import org.apache.logging.log4j.LogManager;
import net.minecraft.world.level.levelgen.structure.StructureFeatureIO;
import java.util.Random;
import net.minecraft.world.level.levelgen.structure.StructurePiece;
import java.util.List;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.resources.ResourceLocation;
import org.apache.logging.log4j.Logger;

public class JigsawPlacement {
    private static final Logger LOGGER;
    public static final StructureTemplatePools POOLS;
    
    public static void addPieces(final ResourceLocation sm, final int integer, final PieceFactory a, final ChunkGenerator<?> caa, final StructureManager cmo, final BlockPos fk, final List<StructurePiece> list, final Random random) {
        StructureFeatureIO.bootstrap();
        new Placer(sm, integer, a, caa, cmo, fk, list, random);
    }
    
    static {
        LOGGER = LogManager.getLogger();
        (POOLS = new StructureTemplatePools()).register(StructureTemplatePool.EMPTY);
    }
    
    static final class PieceState {
        private final PoolElementStructurePiece piece;
        private final AtomicReference<VoxelShape> free;
        private final int boundsTop;
        private final int depth;
        
        private PieceState(final PoolElementStructurePiece clo, final AtomicReference<VoxelShape> atomicReference, final int integer3, final int integer4) {
            this.piece = clo;
            this.free = atomicReference;
            this.boundsTop = integer3;
            this.depth = integer4;
        }
    }
    
    static final class Placer {
        private final int maxDepth;
        private final PieceFactory factory;
        private final ChunkGenerator<?> chunkGenerator;
        private final StructureManager structureManager;
        private final List<StructurePiece> pieces;
        private final Random random;
        private final Deque<PieceState> placing;
        
        public Placer(final ResourceLocation sm, final int integer, final PieceFactory a, final ChunkGenerator<?> caa, final StructureManager cmo, final BlockPos fk, final List<StructurePiece> list, final Random random) {
            this.placing = Queues.newArrayDeque();
            this.maxDepth = integer;
            this.factory = a;
            this.chunkGenerator = caa;
            this.structureManager = cmo;
            this.pieces = list;
            this.random = random;
            final Rotation btu10 = Rotation.getRandom(random);
            final StructureTemplatePool cim11 = JigsawPlacement.POOLS.getPool(sm);
            final StructurePoolElement cik12 = cim11.getRandomTemplate(random);
            final PoolElementStructurePiece clo13 = a.create(cmo, cik12, fk, cik12.getGroundLevelDelta(), btu10, cik12.getBoundingBox(cmo, fk, btu10));
            final BoundingBox clb14 = clo13.getBoundingBox();
            final int integer2 = (clb14.x1 + clb14.x0) / 2;
            final int integer3 = (clb14.z1 + clb14.z0) / 2;
            final int integer4 = caa.getFirstFreeHeight(integer2, integer3, Heightmap.Types.WORLD_SURFACE_WG);
            clo13.move(0, integer4 - (clb14.y0 + clo13.getGroundLevelDelta()), 0);
            list.add(clo13);
            if (integer <= 0) {
                return;
            }
            final int integer5 = 80;
            final AABB cvf19 = new AABB(integer2 - 80, integer4 - 80, integer3 - 80, integer2 + 80 + 1, integer4 + 80 + 1, integer3 + 80 + 1);
            this.placing.addLast(new PieceState(clo13, new AtomicReference((V)Shapes.join(Shapes.create(cvf19), Shapes.create(AABB.of(clb14)), BooleanOp.ONLY_FIRST)), integer4 + 80, 0));
            while (!this.placing.isEmpty()) {
                final PieceState b20 = this.placing.removeFirst();
                this.tryPlacingChildren(b20.piece, b20.free, b20.boundsTop, b20.depth);
            }
        }
        
        private void tryPlacingChildren(final PoolElementStructurePiece clo, final AtomicReference<VoxelShape> atomicReference, final int integer3, final int integer4) {
            final StructurePoolElement cik6 = clo.getElement();
            final BlockPos fk7 = clo.getPosition();
            final Rotation btu8 = clo.getRotation();
            final StructureTemplatePool.Projection a9 = cik6.getProjection();
            final boolean boolean10 = a9 == StructureTemplatePool.Projection.RIGID;
            final AtomicReference<VoxelShape> atomicReference2 = new AtomicReference<VoxelShape>();
            final BoundingBox clb12 = clo.getBoundingBox();
            final int integer5 = clb12.y0;
            for (final StructureTemplate.StructureBlockInfo b2 : cik6.getShuffledJigsawBlocks(this.structureManager, fk7, btu8, this.random)) {
                final Direction fp16 = b2.state.<Direction>getValue((Property<Direction>)JigsawBlock.FACING);
                final BlockPos fk8 = b2.pos;
                final BlockPos fk9 = fk8.relative(fp16);
                final int integer6 = fk8.getY() - integer5;
                int integer7 = -1;
                final StructureTemplatePool cim21 = JigsawPlacement.POOLS.getPool(new ResourceLocation(b2.nbt.getString("target_pool")));
                final StructureTemplatePool cim22 = JigsawPlacement.POOLS.getPool(cim21.getFallback());
                if (cim21 == StructureTemplatePool.INVALID || (cim21.size() == 0 && cim21 != StructureTemplatePool.EMPTY)) {
                    JigsawPlacement.LOGGER.warn("Empty or none existent pool: {}", b2.nbt.getString("target_pool"));
                }
                else {
                    final boolean boolean11 = clb12.isInside(fk9);
                    AtomicReference<VoxelShape> atomicReference3;
                    int integer8;
                    if (boolean11) {
                        atomicReference3 = atomicReference2;
                        integer8 = integer5;
                        if (atomicReference2.get() == null) {
                            atomicReference2.set(Shapes.create(AABB.of(clb12)));
                        }
                    }
                    else {
                        atomicReference3 = atomicReference;
                        integer8 = integer3;
                    }
                    final List<StructurePoolElement> list26 = Lists.newArrayList();
                    if (integer4 != this.maxDepth) {
                        list26.addAll(cim21.getShuffledTemplates(this.random));
                    }
                    list26.addAll(cim22.getShuffledTemplates(this.random));
                Label_1101:
                    for (final StructurePoolElement cik7 : list26) {
                        if (cik7 == EmptyPoolElement.INSTANCE) {
                            break;
                        }
                        for (final Rotation btu9 : Rotation.getShuffled(this.random)) {
                            final List<StructureTemplate.StructureBlockInfo> list27 = cik7.getShuffledJigsawBlocks(this.structureManager, BlockPos.ZERO, btu9, this.random);
                            final BoundingBox clb13 = cik7.getBoundingBox(this.structureManager, BlockPos.ZERO, btu9);
                            int integer9;
                            if (clb13.getYSpan() > 16) {
                                integer9 = 0;
                            }
                            else {
                                ResourceLocation sm4;
                                StructureTemplatePool cim23;
                                StructureTemplatePool cim24;
                                integer9 = list27.stream().mapToInt(b -> {
                                    if (!clb13.isInside(b.pos.relative(b.state.<Direction>getValue((Property<Direction>)JigsawBlock.FACING)))) {
                                        return 0;
                                    }
                                    else {
                                        sm4 = new ResourceLocation(b.nbt.getString("target_pool"));
                                        cim23 = JigsawPlacement.POOLS.getPool(sm4);
                                        cim24 = JigsawPlacement.POOLS.getPool(cim23.getFallback());
                                        return Math.max(cim23.getMaxSize(this.structureManager), cim24.getMaxSize(this.structureManager));
                                    }
                                }).max().orElse(0);
                            }
                            for (final StructureTemplate.StructureBlockInfo b3 : list27) {
                                if (!JigsawBlock.canAttach(b2, b3)) {
                                    continue;
                                }
                                final BlockPos fk10 = b3.pos;
                                final BlockPos fk11 = new BlockPos(fk9.getX() - fk10.getX(), fk9.getY() - fk10.getY(), fk9.getZ() - fk10.getZ());
                                final BoundingBox clb14 = cik7.getBoundingBox(this.structureManager, fk11, btu9);
                                final int integer10 = clb14.y0;
                                final StructureTemplatePool.Projection a10 = cik7.getProjection();
                                final boolean boolean12 = a10 == StructureTemplatePool.Projection.RIGID;
                                final int integer11 = fk10.getY();
                                final int integer12 = integer6 - integer11 + b2.state.<Direction>getValue((Property<Direction>)JigsawBlock.FACING).getStepY();
                                int integer13;
                                if (boolean10 && boolean12) {
                                    integer13 = integer5 + integer12;
                                }
                                else {
                                    if (integer7 == -1) {
                                        integer7 = this.chunkGenerator.getFirstFreeHeight(fk8.getX(), fk8.getZ(), Heightmap.Types.WORLD_SURFACE_WG);
                                    }
                                    integer13 = integer7 - integer11;
                                }
                                final int integer14 = integer13 - integer10;
                                final BoundingBox clb15 = clb14.moved(0, integer14, 0);
                                final BlockPos fk12 = fk11.offset(0, integer14, 0);
                                if (integer9 > 0) {
                                    final int integer15 = Math.max(integer9 + 1, clb15.y1 - clb15.y0);
                                    clb15.y1 = clb15.y0 + integer15;
                                }
                                if (Shapes.joinIsNotEmpty(atomicReference3.get(), Shapes.create(AABB.of(clb15).deflate(0.25)), BooleanOp.ONLY_SECOND)) {
                                    continue;
                                }
                                atomicReference3.set(Shapes.joinUnoptimized(atomicReference3.get(), Shapes.create(AABB.of(clb15)), BooleanOp.ONLY_FIRST));
                                final int integer15 = clo.getGroundLevelDelta();
                                int integer16;
                                if (boolean12) {
                                    integer16 = integer15 - integer12;
                                }
                                else {
                                    integer16 = cik7.getGroundLevelDelta();
                                }
                                final PoolElementStructurePiece clo2 = this.factory.create(this.structureManager, cik7, fk12, integer16, btu9, clb15);
                                int integer17;
                                if (boolean10) {
                                    integer17 = integer5 + integer6;
                                }
                                else if (boolean12) {
                                    integer17 = integer13 + integer11;
                                }
                                else {
                                    if (integer7 == -1) {
                                        integer7 = this.chunkGenerator.getFirstFreeHeight(fk8.getX(), fk8.getZ(), Heightmap.Types.WORLD_SURFACE_WG);
                                    }
                                    integer17 = integer7 + integer12 / 2;
                                }
                                clo.addJunction(new JigsawJunction(fk9.getX(), integer17 - integer6 + integer15, fk9.getZ(), integer12, a10));
                                clo2.addJunction(new JigsawJunction(fk8.getX(), integer17 - integer11 + integer16, fk8.getZ(), -integer12, a9));
                                this.pieces.add(clo2);
                                if (integer4 + 1 <= this.maxDepth) {
                                    this.placing.addLast(new PieceState(clo2, (AtomicReference)atomicReference3, integer8, integer4 + 1));
                                    break Label_1101;
                                }
                                break Label_1101;
                            }
                        }
                    }
                }
            }
        }
    }
    
    public interface PieceFactory {
        PoolElementStructurePiece create(final StructureManager cmo, final StructurePoolElement cik, final BlockPos fk, final int integer, final Rotation btu, final BoundingBox clb);
    }
}
