package net.minecraft.world.level.levelgen.structure;

import net.minecraft.world.level.block.state.StateHolder;
import com.google.common.collect.ImmutableSet;
import net.minecraft.world.level.block.entity.DispenserBlockEntity;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.BlockGetter;
import java.util.Iterator;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.WorldGenLevel;
import java.util.Random;
import java.util.List;
import net.minecraft.nbt.Tag;
import net.minecraft.core.Registry;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.block.Block;
import java.util.Set;
import net.minecraft.world.level.levelgen.feature.StructurePieceType;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.Mirror;
import javax.annotation.Nullable;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.state.BlockState;

public abstract class StructurePiece {
    protected static final BlockState CAVE_AIR;
    protected BoundingBox boundingBox;
    @Nullable
    private Direction orientation;
    private Mirror mirror;
    private Rotation rotation;
    protected int genDepth;
    private final StructurePieceType type;
    private static final Set<Block> SHAPE_CHECK_BLOCKS;
    
    protected StructurePiece(final StructurePieceType cmm, final int integer) {
        this.type = cmm;
        this.genDepth = integer;
    }
    
    public StructurePiece(final StructurePieceType cmm, final CompoundTag le) {
        this(cmm, le.getInt("GD"));
        if (le.contains("BB")) {
            this.boundingBox = new BoundingBox(le.getIntArray("BB"));
        }
        final int integer4 = le.getInt("O");
        this.setOrientation((integer4 == -1) ? null : Direction.from2DDataValue(integer4));
    }
    
    public final CompoundTag createTag() {
        final CompoundTag le2 = new CompoundTag();
        le2.putString("id", Registry.STRUCTURE_PIECE.getKey(this.getType()).toString());
        le2.put("BB", this.boundingBox.createTag());
        final Direction fz3 = this.getOrientation();
        le2.putInt("O", (fz3 == null) ? -1 : fz3.get2DDataValue());
        le2.putInt("GD", this.genDepth);
        this.addAdditionalSaveData(le2);
        return le2;
    }
    
    protected abstract void addAdditionalSaveData(final CompoundTag le);
    
    public void addChildren(final StructurePiece cty, final List<StructurePiece> list, final Random random) {
    }
    
    public abstract boolean postProcess(final WorldGenLevel bqu, final StructureFeatureManager bqq, final ChunkGenerator cha, final Random random, final BoundingBox ctd, final ChunkPos bph, final BlockPos fu);
    
    public BoundingBox getBoundingBox() {
        return this.boundingBox;
    }
    
    public int getGenDepth() {
        return this.genDepth;
    }
    
    public boolean isCloseToChunk(final ChunkPos bph, final int integer) {
        final int integer2 = bph.x << 4;
        final int integer3 = bph.z << 4;
        return this.boundingBox.intersects(integer2 - integer, integer3 - integer, integer2 + 15 + integer, integer3 + 15 + integer);
    }
    
    public static StructurePiece findCollisionPiece(final List<StructurePiece> list, final BoundingBox ctd) {
        for (final StructurePiece cty4 : list) {
            if (cty4.getBoundingBox() != null && cty4.getBoundingBox().intersects(ctd)) {
                return cty4;
            }
        }
        return null;
    }
    
    protected boolean edgesLiquid(final BlockGetter bpg, final BoundingBox ctd) {
        final int integer4 = Math.max(this.boundingBox.x0 - 1, ctd.x0);
        final int integer5 = Math.max(this.boundingBox.y0 - 1, ctd.y0);
        final int integer6 = Math.max(this.boundingBox.z0 - 1, ctd.z0);
        final int integer7 = Math.min(this.boundingBox.x1 + 1, ctd.x1);
        final int integer8 = Math.min(this.boundingBox.y1 + 1, ctd.y1);
        final int integer9 = Math.min(this.boundingBox.z1 + 1, ctd.z1);
        final BlockPos.MutableBlockPos a10 = new BlockPos.MutableBlockPos();
        for (int integer10 = integer4; integer10 <= integer7; ++integer10) {
            for (int integer11 = integer6; integer11 <= integer9; ++integer11) {
                if (bpg.getBlockState(a10.set(integer10, integer5, integer11)).getMaterial().isLiquid()) {
                    return true;
                }
                if (bpg.getBlockState(a10.set(integer10, integer8, integer11)).getMaterial().isLiquid()) {
                    return true;
                }
            }
        }
        for (int integer10 = integer4; integer10 <= integer7; ++integer10) {
            for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                if (bpg.getBlockState(a10.set(integer10, integer11, integer6)).getMaterial().isLiquid()) {
                    return true;
                }
                if (bpg.getBlockState(a10.set(integer10, integer11, integer9)).getMaterial().isLiquid()) {
                    return true;
                }
            }
        }
        for (int integer10 = integer6; integer10 <= integer9; ++integer10) {
            for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                if (bpg.getBlockState(a10.set(integer4, integer11, integer10)).getMaterial().isLiquid()) {
                    return true;
                }
                if (bpg.getBlockState(a10.set(integer7, integer11, integer10)).getMaterial().isLiquid()) {
                    return true;
                }
            }
        }
        return false;
    }
    
    protected int getWorldX(final int integer1, final int integer2) {
        final Direction fz4 = this.getOrientation();
        if (fz4 == null) {
            return integer1;
        }
        switch (fz4) {
            case NORTH:
            case SOUTH: {
                return this.boundingBox.x0 + integer1;
            }
            case WEST: {
                return this.boundingBox.x1 - integer2;
            }
            case EAST: {
                return this.boundingBox.x0 + integer2;
            }
            default: {
                return integer1;
            }
        }
    }
    
    protected int getWorldY(final int integer) {
        if (this.getOrientation() == null) {
            return integer;
        }
        return integer + this.boundingBox.y0;
    }
    
    protected int getWorldZ(final int integer1, final int integer2) {
        final Direction fz4 = this.getOrientation();
        if (fz4 == null) {
            return integer2;
        }
        switch (fz4) {
            case NORTH: {
                return this.boundingBox.z1 - integer2;
            }
            case SOUTH: {
                return this.boundingBox.z0 + integer2;
            }
            case WEST:
            case EAST: {
                return this.boundingBox.z0 + integer1;
            }
            default: {
                return integer2;
            }
        }
    }
    
    protected void placeBlock(final LevelAccessor bqc, BlockState cfj, final int integer3, final int integer4, final int integer5, final BoundingBox ctd) {
        final BlockPos fu8 = new BlockPos(this.getWorldX(integer3, integer5), this.getWorldY(integer4), this.getWorldZ(integer3, integer5));
        if (!ctd.isInside(fu8)) {
            return;
        }
        if (this.mirror != Mirror.NONE) {
            cfj = cfj.mirror(this.mirror);
        }
        if (this.rotation != Rotation.NONE) {
            cfj = cfj.rotate(this.rotation);
        }
        bqc.setBlock(fu8, cfj, 2);
        final FluidState cxa9 = bqc.getFluidState(fu8);
        if (!cxa9.isEmpty()) {
            bqc.getLiquidTicks().scheduleTick(fu8, cxa9.getType(), 0);
        }
        if (StructurePiece.SHAPE_CHECK_BLOCKS.contains(cfj.getBlock())) {
            bqc.getChunk(fu8).markPosForPostprocessing(fu8);
        }
    }
    
    protected BlockState getBlock(final BlockGetter bpg, final int integer2, final int integer3, final int integer4, final BoundingBox ctd) {
        final int integer5 = this.getWorldX(integer2, integer4);
        final int integer6 = this.getWorldY(integer3);
        final int integer7 = this.getWorldZ(integer2, integer4);
        final BlockPos fu10 = new BlockPos(integer5, integer6, integer7);
        if (!ctd.isInside(fu10)) {
            return Blocks.AIR.defaultBlockState();
        }
        return bpg.getBlockState(fu10);
    }
    
    protected boolean isInterior(final LevelReader bqd, final int integer2, final int integer3, final int integer4, final BoundingBox ctd) {
        final int integer5 = this.getWorldX(integer2, integer4);
        final int integer6 = this.getWorldY(integer3 + 1);
        final int integer7 = this.getWorldZ(integer2, integer4);
        final BlockPos fu10 = new BlockPos(integer5, integer6, integer7);
        return ctd.isInside(fu10) && integer6 < bqd.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, integer5, integer7);
    }
    
    protected void generateAirBox(final LevelAccessor bqc, final BoundingBox ctd, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7, final int integer8) {
        for (int integer9 = integer4; integer9 <= integer7; ++integer9) {
            for (int integer10 = integer3; integer10 <= integer6; ++integer10) {
                for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                    this.placeBlock(bqc, Blocks.AIR.defaultBlockState(), integer10, integer9, integer11, ctd);
                }
            }
        }
    }
    
    protected void generateBox(final LevelAccessor bqc, final BoundingBox ctd, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7, final int integer8, final BlockState cfj9, final BlockState cfj10, final boolean boolean11) {
        for (int integer9 = integer4; integer9 <= integer7; ++integer9) {
            for (int integer10 = integer3; integer10 <= integer6; ++integer10) {
                for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                    if (!boolean11 || !this.getBlock(bqc, integer10, integer9, integer11, ctd).isAir()) {
                        if (integer9 == integer4 || integer9 == integer7 || integer10 == integer3 || integer10 == integer6 || integer11 == integer5 || integer11 == integer8) {
                            this.placeBlock(bqc, cfj9, integer10, integer9, integer11, ctd);
                        }
                        else {
                            this.placeBlock(bqc, cfj10, integer10, integer9, integer11, ctd);
                        }
                    }
                }
            }
        }
    }
    
    protected void generateBox(final LevelAccessor bqc, final BoundingBox ctd, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7, final int integer8, final boolean boolean9, final Random random, final BlockSelector a) {
        for (int integer9 = integer4; integer9 <= integer7; ++integer9) {
            for (int integer10 = integer3; integer10 <= integer6; ++integer10) {
                for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                    if (!boolean9 || !this.getBlock(bqc, integer10, integer9, integer11, ctd).isAir()) {
                        a.next(random, integer10, integer9, integer11, integer9 == integer4 || integer9 == integer7 || integer10 == integer3 || integer10 == integer6 || integer11 == integer5 || integer11 == integer8);
                        this.placeBlock(bqc, a.getNext(), integer10, integer9, integer11, ctd);
                    }
                }
            }
        }
    }
    
    protected void generateMaybeBox(final LevelAccessor bqc, final BoundingBox ctd, final Random random, final float float4, final int integer5, final int integer6, final int integer7, final int integer8, final int integer9, final int integer10, final BlockState cfj11, final BlockState cfj12, final boolean boolean13, final boolean boolean14) {
        for (int integer11 = integer6; integer11 <= integer9; ++integer11) {
            for (int integer12 = integer5; integer12 <= integer8; ++integer12) {
                for (int integer13 = integer7; integer13 <= integer10; ++integer13) {
                    if (random.nextFloat() <= float4) {
                        if (!boolean13 || !this.getBlock(bqc, integer12, integer11, integer13, ctd).isAir()) {
                            if (!boolean14 || this.isInterior(bqc, integer12, integer11, integer13, ctd)) {
                                if (integer11 == integer6 || integer11 == integer9 || integer12 == integer5 || integer12 == integer8 || integer13 == integer7 || integer13 == integer10) {
                                    this.placeBlock(bqc, cfj11, integer12, integer11, integer13, ctd);
                                }
                                else {
                                    this.placeBlock(bqc, cfj12, integer12, integer11, integer13, ctd);
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    protected void maybeGenerateBlock(final LevelAccessor bqc, final BoundingBox ctd, final Random random, final float float4, final int integer5, final int integer6, final int integer7, final BlockState cfj) {
        if (random.nextFloat() < float4) {
            this.placeBlock(bqc, cfj, integer5, integer6, integer7, ctd);
        }
    }
    
    protected void generateUpperHalfSphere(final LevelAccessor bqc, final BoundingBox ctd, final int integer3, final int integer4, final int integer5, final int integer6, final int integer7, final int integer8, final BlockState cfj, final boolean boolean10) {
        final float float12 = (float)(integer6 - integer3 + 1);
        final float float13 = (float)(integer7 - integer4 + 1);
        final float float14 = (float)(integer8 - integer5 + 1);
        final float float15 = integer3 + float12 / 2.0f;
        final float float16 = integer5 + float14 / 2.0f;
        for (int integer9 = integer4; integer9 <= integer7; ++integer9) {
            final float float17 = (integer9 - integer4) / float13;
            for (int integer10 = integer3; integer10 <= integer6; ++integer10) {
                final float float18 = (integer10 - float15) / (float12 * 0.5f);
                for (int integer11 = integer5; integer11 <= integer8; ++integer11) {
                    final float float19 = (integer11 - float16) / (float14 * 0.5f);
                    if (!boolean10 || !this.getBlock(bqc, integer10, integer9, integer11, ctd).isAir()) {
                        final float float20 = float18 * float18 + float17 * float17 + float19 * float19;
                        if (float20 <= 1.05f) {
                            this.placeBlock(bqc, cfj, integer10, integer9, integer11, ctd);
                        }
                    }
                }
            }
        }
    }
    
    protected void fillColumnDown(final LevelAccessor bqc, final BlockState cfj, final int integer3, final int integer4, final int integer5, final BoundingBox ctd) {
        final int integer6 = this.getWorldX(integer3, integer5);
        int integer7 = this.getWorldY(integer4);
        final int integer8 = this.getWorldZ(integer3, integer5);
        if (!ctd.isInside(new BlockPos(integer6, integer7, integer8))) {
            return;
        }
        while ((bqc.isEmptyBlock(new BlockPos(integer6, integer7, integer8)) || bqc.getBlockState(new BlockPos(integer6, integer7, integer8)).getMaterial().isLiquid()) && integer7 > 1) {
            bqc.setBlock(new BlockPos(integer6, integer7, integer8), cfj, 2);
            --integer7;
        }
    }
    
    protected boolean createChest(final LevelAccessor bqc, final BoundingBox ctd, final Random random, final int integer4, final int integer5, final int integer6, final ResourceLocation uh) {
        final BlockPos fu9 = new BlockPos(this.getWorldX(integer4, integer6), this.getWorldY(integer5), this.getWorldZ(integer4, integer6));
        return this.createChest(bqc, ctd, random, fu9, uh, null);
    }
    
    public static BlockState reorient(final BlockGetter bpg, final BlockPos fu, final BlockState cfj) {
        Direction fz4 = null;
        for (final Direction fz5 : Direction.Plane.HORIZONTAL) {
            final BlockPos fu2 = fu.relative(fz5);
            final BlockState cfj2 = bpg.getBlockState(fu2);
            if (cfj2.is(Blocks.CHEST)) {
                return cfj;
            }
            if (!cfj2.isSolidRender(bpg, fu2)) {
                continue;
            }
            if (fz4 != null) {
                fz4 = null;
                break;
            }
            fz4 = fz5;
        }
        if (fz4 != null) {
            return ((StateHolder<O, BlockState>)cfj).<Comparable, Direction>setValue((Property<Comparable>)HorizontalDirectionalBlock.FACING, fz4.getOpposite());
        }
        Direction fz6 = cfj.<Direction>getValue((Property<Direction>)HorizontalDirectionalBlock.FACING);
        BlockPos fu3 = fu.relative(fz6);
        if (bpg.getBlockState(fu3).isSolidRender(bpg, fu3)) {
            fz6 = fz6.getOpposite();
            fu3 = fu.relative(fz6);
        }
        if (bpg.getBlockState(fu3).isSolidRender(bpg, fu3)) {
            fz6 = fz6.getClockWise();
            fu3 = fu.relative(fz6);
        }
        if (bpg.getBlockState(fu3).isSolidRender(bpg, fu3)) {
            fz6 = fz6.getOpposite();
            fu3 = fu.relative(fz6);
        }
        return ((StateHolder<O, BlockState>)cfj).<Comparable, Direction>setValue((Property<Comparable>)HorizontalDirectionalBlock.FACING, fz6);
    }
    
    protected boolean createChest(final LevelAccessor bqc, final BoundingBox ctd, final Random random, final BlockPos fu, final ResourceLocation uh, @Nullable BlockState cfj) {
        if (!ctd.isInside(fu) || bqc.getBlockState(fu).is(Blocks.CHEST)) {
            return false;
        }
        if (cfj == null) {
            cfj = reorient(bqc, fu, Blocks.CHEST.defaultBlockState());
        }
        bqc.setBlock(fu, cfj, 2);
        final BlockEntity cdl8 = bqc.getBlockEntity(fu);
        if (cdl8 instanceof ChestBlockEntity) {
            ((ChestBlockEntity)cdl8).setLootTable(uh, random.nextLong());
        }
        return true;
    }
    
    protected boolean createDispenser(final LevelAccessor bqc, final BoundingBox ctd, final Random random, final int integer4, final int integer5, final int integer6, final Direction fz, final ResourceLocation uh) {
        final BlockPos fu10 = new BlockPos(this.getWorldX(integer4, integer6), this.getWorldY(integer5), this.getWorldZ(integer4, integer6));
        if (ctd.isInside(fu10) && !bqc.getBlockState(fu10).is(Blocks.DISPENSER)) {
            this.placeBlock(bqc, ((StateHolder<O, BlockState>)Blocks.DISPENSER.defaultBlockState()).<Comparable, Direction>setValue((Property<Comparable>)DispenserBlock.FACING, fz), integer4, integer5, integer6, ctd);
            final BlockEntity cdl11 = bqc.getBlockEntity(fu10);
            if (cdl11 instanceof DispenserBlockEntity) {
                ((DispenserBlockEntity)cdl11).setLootTable(uh, random.nextLong());
            }
            return true;
        }
        return false;
    }
    
    public void move(final int integer1, final int integer2, final int integer3) {
        this.boundingBox.move(integer1, integer2, integer3);
    }
    
    @Nullable
    public Direction getOrientation() {
        return this.orientation;
    }
    
    public void setOrientation(@Nullable final Direction fz) {
        this.orientation = fz;
        if (fz == null) {
            this.rotation = Rotation.NONE;
            this.mirror = Mirror.NONE;
        }
        else {
            switch (fz) {
                case SOUTH: {
                    this.mirror = Mirror.LEFT_RIGHT;
                    this.rotation = Rotation.NONE;
                    break;
                }
                case WEST: {
                    this.mirror = Mirror.LEFT_RIGHT;
                    this.rotation = Rotation.CLOCKWISE_90;
                    break;
                }
                case EAST: {
                    this.mirror = Mirror.NONE;
                    this.rotation = Rotation.CLOCKWISE_90;
                    break;
                }
                default: {
                    this.mirror = Mirror.NONE;
                    this.rotation = Rotation.NONE;
                    break;
                }
            }
        }
    }
    
    public Rotation getRotation() {
        return this.rotation;
    }
    
    public StructurePieceType getType() {
        return this.type;
    }
    
    static {
        CAVE_AIR = Blocks.CAVE_AIR.defaultBlockState();
        SHAPE_CHECK_BLOCKS = ImmutableSet.<Block>builder().add(Blocks.NETHER_BRICK_FENCE).add(Blocks.TORCH).add(Blocks.WALL_TORCH).add(Blocks.OAK_FENCE).add(Blocks.SPRUCE_FENCE).add(Blocks.DARK_OAK_FENCE).add(Blocks.ACACIA_FENCE).add(Blocks.BIRCH_FENCE).add(Blocks.JUNGLE_FENCE).add(Blocks.LADDER).add(Blocks.IRON_BARS).build();
    }
    
    public abstract static class BlockSelector {
        protected BlockState next;
        
        protected BlockSelector() {
            this.next = Blocks.AIR.defaultBlockState();
        }
        
        public abstract void next(final Random random, final int integer2, final int integer3, final int integer4, final boolean boolean5);
        
        public BlockState getNext() {
            return this.next;
        }
    }
}
