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

import net.minecraft.world.level.block.state.StateHolder;
import net.minecraft.world.level.material.FluidState;
import java.util.Iterator;
import net.minecraft.world.level.block.Block;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockRotProcessor;
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 net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.SpawnGroupData;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.monster.Drowned;
import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.block.entity.ChestBlockEntity;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.tags.Tag;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.level.block.ChestBlock;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.BlockIgnoreProcessor;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructurePlaceSettings;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.level.levelgen.feature.StructurePieceType;
import com.google.common.collect.Lists;
import net.minecraft.util.Mth;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.levelgen.feature.configurations.OceanRuinConfiguration;
import java.util.List;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
import net.minecraft.Util;
import java.util.Random;
import net.minecraft.resources.ResourceLocation;

public class OceanRuinPieces {
    private static final ResourceLocation[] WARM_RUINS;
    private static final ResourceLocation[] RUINS_BRICK;
    private static final ResourceLocation[] RUINS_CRACKED;
    private static final ResourceLocation[] RUINS_MOSSY;
    private static final ResourceLocation[] BIG_RUINS_BRICK;
    private static final ResourceLocation[] BIG_RUINS_MOSSY;
    private static final ResourceLocation[] BIG_RUINS_CRACKED;
    private static final ResourceLocation[] BIG_WARM_RUINS;
    
    private static ResourceLocation getSmallWarmRuin(final Random random) {
        return Util.<ResourceLocation>getRandom(OceanRuinPieces.WARM_RUINS, random);
    }
    
    private static ResourceLocation getBigWarmRuin(final Random random) {
        return Util.<ResourceLocation>getRandom(OceanRuinPieces.BIG_WARM_RUINS, random);
    }
    
    public static void addPieces(final StructureManager cva, final BlockPos fu, final Rotation cap, final List<StructurePiece> list, final Random random, final OceanRuinConfiguration cob) {
        final boolean boolean7 = random.nextFloat() <= cob.largeProbability;
        final float float8 = boolean7 ? 0.9f : 0.8f;
        addPiece(cva, fu, cap, list, random, cob, boolean7, float8);
        if (boolean7 && random.nextFloat() <= cob.clusterProbability) {
            addClusterRuins(cva, random, cap, fu, cob, list);
        }
    }
    
    private static void addClusterRuins(final StructureManager cva, final Random random, final Rotation cap, final BlockPos fu, final OceanRuinConfiguration cob, final List<StructurePiece> list) {
        final int integer7 = fu.getX();
        final int integer8 = fu.getZ();
        final BlockPos fu2 = StructureTemplate.transform(new BlockPos(15, 0, 15), Mirror.NONE, cap, BlockPos.ZERO).offset(integer7, 0, integer8);
        final BoundingBox ctd10 = BoundingBox.createProper(integer7, 0, integer8, fu2.getX(), 0, fu2.getZ());
        final BlockPos fu3 = new BlockPos(Math.min(integer7, fu2.getX()), 0, Math.min(integer8, fu2.getZ()));
        final List<BlockPos> list2 = allPositions(random, fu3.getX(), fu3.getZ());
        for (int integer9 = Mth.nextInt(random, 4, 8), integer10 = 0; integer10 < integer9; ++integer10) {
            if (!list2.isEmpty()) {
                final int integer11 = random.nextInt(list2.size());
                final BlockPos fu4 = list2.remove(integer11);
                final int integer12 = fu4.getX();
                final int integer13 = fu4.getZ();
                final Rotation cap2 = Rotation.getRandom(random);
                final BlockPos fu5 = StructureTemplate.transform(new BlockPos(5, 0, 6), Mirror.NONE, cap2, BlockPos.ZERO).offset(integer12, 0, integer13);
                final BoundingBox ctd11 = BoundingBox.createProper(integer12, 0, integer13, fu5.getX(), 0, fu5.getZ());
                if (!ctd11.intersects(ctd10)) {
                    addPiece(cva, fu4, cap2, list, random, cob, false, 0.8f);
                }
            }
        }
    }
    
    private static List<BlockPos> allPositions(final Random random, final int integer2, final int integer3) {
        final List<BlockPos> list4 = Lists.newArrayList();
        list4.add(new BlockPos(integer2 - 16 + Mth.nextInt(random, 1, 8), 90, integer3 + 16 + Mth.nextInt(random, 1, 7)));
        list4.add(new BlockPos(integer2 - 16 + Mth.nextInt(random, 1, 8), 90, integer3 + Mth.nextInt(random, 1, 7)));
        list4.add(new BlockPos(integer2 - 16 + Mth.nextInt(random, 1, 8), 90, integer3 - 16 + Mth.nextInt(random, 4, 8)));
        list4.add(new BlockPos(integer2 + Mth.nextInt(random, 1, 7), 90, integer3 + 16 + Mth.nextInt(random, 1, 7)));
        list4.add(new BlockPos(integer2 + Mth.nextInt(random, 1, 7), 90, integer3 - 16 + Mth.nextInt(random, 4, 6)));
        list4.add(new BlockPos(integer2 + 16 + Mth.nextInt(random, 1, 7), 90, integer3 + 16 + Mth.nextInt(random, 3, 8)));
        list4.add(new BlockPos(integer2 + 16 + Mth.nextInt(random, 1, 7), 90, integer3 + Mth.nextInt(random, 1, 7)));
        list4.add(new BlockPos(integer2 + 16 + Mth.nextInt(random, 1, 7), 90, integer3 - 16 + Mth.nextInt(random, 4, 8)));
        return list4;
    }
    
    private static void addPiece(final StructureManager cva, final BlockPos fu, final Rotation cap, final List<StructurePiece> list, final Random random, final OceanRuinConfiguration cob, final boolean boolean7, final float float8) {
        if (cob.biomeTemp == OceanRuinFeature.Type.WARM) {
            final ResourceLocation uh9 = boolean7 ? getBigWarmRuin(random) : getSmallWarmRuin(random);
            list.add(new OceanRuinPiece(cva, uh9, fu, cap, float8, cob.biomeTemp, boolean7));
        }
        else if (cob.biomeTemp == OceanRuinFeature.Type.COLD) {
            final ResourceLocation[] arr9 = boolean7 ? OceanRuinPieces.BIG_RUINS_BRICK : OceanRuinPieces.RUINS_BRICK;
            final ResourceLocation[] arr10 = boolean7 ? OceanRuinPieces.BIG_RUINS_CRACKED : OceanRuinPieces.RUINS_CRACKED;
            final ResourceLocation[] arr11 = boolean7 ? OceanRuinPieces.BIG_RUINS_MOSSY : OceanRuinPieces.RUINS_MOSSY;
            final int integer12 = random.nextInt(arr9.length);
            list.add(new OceanRuinPiece(cva, arr9[integer12], fu, cap, float8, cob.biomeTemp, boolean7));
            list.add(new OceanRuinPiece(cva, arr10[integer12], fu, cap, 0.7f, cob.biomeTemp, boolean7));
            list.add(new OceanRuinPiece(cva, arr11[integer12], fu, cap, 0.5f, cob.biomeTemp, boolean7));
        }
    }
    
    static {
        WARM_RUINS = new ResourceLocation[] { new ResourceLocation("underwater_ruin/warm_1"), new ResourceLocation("underwater_ruin/warm_2"), new ResourceLocation("underwater_ruin/warm_3"), new ResourceLocation("underwater_ruin/warm_4"), new ResourceLocation("underwater_ruin/warm_5"), new ResourceLocation("underwater_ruin/warm_6"), new ResourceLocation("underwater_ruin/warm_7"), new ResourceLocation("underwater_ruin/warm_8") };
        RUINS_BRICK = new ResourceLocation[] { new ResourceLocation("underwater_ruin/brick_1"), new ResourceLocation("underwater_ruin/brick_2"), new ResourceLocation("underwater_ruin/brick_3"), new ResourceLocation("underwater_ruin/brick_4"), new ResourceLocation("underwater_ruin/brick_5"), new ResourceLocation("underwater_ruin/brick_6"), new ResourceLocation("underwater_ruin/brick_7"), new ResourceLocation("underwater_ruin/brick_8") };
        RUINS_CRACKED = new ResourceLocation[] { new ResourceLocation("underwater_ruin/cracked_1"), new ResourceLocation("underwater_ruin/cracked_2"), new ResourceLocation("underwater_ruin/cracked_3"), new ResourceLocation("underwater_ruin/cracked_4"), new ResourceLocation("underwater_ruin/cracked_5"), new ResourceLocation("underwater_ruin/cracked_6"), new ResourceLocation("underwater_ruin/cracked_7"), new ResourceLocation("underwater_ruin/cracked_8") };
        RUINS_MOSSY = new ResourceLocation[] { new ResourceLocation("underwater_ruin/mossy_1"), new ResourceLocation("underwater_ruin/mossy_2"), new ResourceLocation("underwater_ruin/mossy_3"), new ResourceLocation("underwater_ruin/mossy_4"), new ResourceLocation("underwater_ruin/mossy_5"), new ResourceLocation("underwater_ruin/mossy_6"), new ResourceLocation("underwater_ruin/mossy_7"), new ResourceLocation("underwater_ruin/mossy_8") };
        BIG_RUINS_BRICK = new ResourceLocation[] { new ResourceLocation("underwater_ruin/big_brick_1"), new ResourceLocation("underwater_ruin/big_brick_2"), new ResourceLocation("underwater_ruin/big_brick_3"), new ResourceLocation("underwater_ruin/big_brick_8") };
        BIG_RUINS_MOSSY = new ResourceLocation[] { new ResourceLocation("underwater_ruin/big_mossy_1"), new ResourceLocation("underwater_ruin/big_mossy_2"), new ResourceLocation("underwater_ruin/big_mossy_3"), new ResourceLocation("underwater_ruin/big_mossy_8") };
        BIG_RUINS_CRACKED = new ResourceLocation[] { new ResourceLocation("underwater_ruin/big_cracked_1"), new ResourceLocation("underwater_ruin/big_cracked_2"), new ResourceLocation("underwater_ruin/big_cracked_3"), new ResourceLocation("underwater_ruin/big_cracked_8") };
        BIG_WARM_RUINS = new ResourceLocation[] { new ResourceLocation("underwater_ruin/big_warm_4"), new ResourceLocation("underwater_ruin/big_warm_5"), new ResourceLocation("underwater_ruin/big_warm_6"), new ResourceLocation("underwater_ruin/big_warm_7") };
    }
    
    public static class OceanRuinPiece extends TemplateStructurePiece {
        private final OceanRuinFeature.Type biomeType;
        private final float integrity;
        private final ResourceLocation templateLocation;
        private final Rotation rotation;
        private final boolean isLarge;
        
        public OceanRuinPiece(final StructureManager cva, final ResourceLocation uh, final BlockPos fu, final Rotation cap, final float float5, final OceanRuinFeature.Type b, final boolean boolean7) {
            super(StructurePieceType.OCEAN_RUIN, 0);
            this.templateLocation = uh;
            this.templatePosition = fu;
            this.rotation = cap;
            this.integrity = float5;
            this.biomeType = b;
            this.isLarge = boolean7;
            this.loadTemplate(cva);
        }
        
        public OceanRuinPiece(final StructureManager cva, final CompoundTag le) {
            super(StructurePieceType.OCEAN_RUIN, le);
            this.templateLocation = new ResourceLocation(le.getString("Template"));
            this.rotation = Rotation.valueOf(le.getString("Rot"));
            this.integrity = le.getFloat("Integrity");
            this.biomeType = OceanRuinFeature.Type.valueOf(le.getString("BiomeType"));
            this.isLarge = le.getBoolean("IsLarge");
            this.loadTemplate(cva);
        }
        
        private void loadTemplate(final StructureManager cva) {
            final StructureTemplate cve3 = cva.getOrCreate(this.templateLocation);
            final StructurePlaceSettings cvb4 = new StructurePlaceSettings().setRotation(this.rotation).setMirror(Mirror.NONE).addProcessor(BlockIgnoreProcessor.STRUCTURE_AND_AIR);
            this.setup(cve3, this.templatePosition, cvb4);
        }
        
        @Override
        protected void addAdditionalSaveData(final CompoundTag le) {
            super.addAdditionalSaveData(le);
            le.putString("Template", this.templateLocation.toString());
            le.putString("Rot", this.rotation.name());
            le.putFloat("Integrity", this.integrity);
            le.putString("BiomeType", this.biomeType.toString());
            le.putBoolean("IsLarge", this.isLarge);
        }
        
        @Override
        protected void handleDataMarker(final String string, final BlockPos fu, final LevelAccessor bqc, final Random random, final BoundingBox ctd) {
            if ("chest".equals(string)) {
                bqc.setBlock(fu, ((StateHolder<O, BlockState>)Blocks.CHEST.defaultBlockState()).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, bqc.getFluidState(fu).is(FluidTags.WATER)), 2);
                final BlockEntity cdl7 = bqc.getBlockEntity(fu);
                if (cdl7 instanceof ChestBlockEntity) {
                    ((ChestBlockEntity)cdl7).setLootTable(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL, random.nextLong());
                }
            }
            else if ("drowned".equals(string)) {
                final Drowned bbp7 = EntityType.DROWNED.create(bqc.getLevel());
                bbp7.setPersistenceRequired();
                bbp7.moveTo(fu, 0.0f, 0.0f);
                bbp7.finalizeSpawn(bqc, bqc.getCurrentDifficultyAt(fu), MobSpawnType.STRUCTURE, null, null);
                bqc.addFreshEntity(bbp7);
                if (fu.getY() > bqc.getSeaLevel()) {
                    bqc.setBlock(fu, Blocks.AIR.defaultBlockState(), 2);
                }
                else {
                    bqc.setBlock(fu, Blocks.WATER.defaultBlockState(), 2);
                }
            }
        }
        
        @Override
        public boolean postProcess(final WorldGenLevel bqu, final StructureFeatureManager bqq, final ChunkGenerator cha, final Random random, final BoundingBox ctd, final ChunkPos bph, final BlockPos fu) {
            this.placeSettings.clearProcessors().addProcessor(new BlockRotProcessor(this.integrity)).addProcessor(BlockIgnoreProcessor.STRUCTURE_AND_AIR);
            final int integer9 = bqu.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, this.templatePosition.getX(), this.templatePosition.getZ());
            this.templatePosition = new BlockPos(this.templatePosition.getX(), integer9, this.templatePosition.getZ());
            final BlockPos fu2 = StructureTemplate.transform(new BlockPos(this.template.getSize().getX() - 1, 0, this.template.getSize().getZ() - 1), Mirror.NONE, this.rotation, BlockPos.ZERO).offset(this.templatePosition);
            this.templatePosition = new BlockPos(this.templatePosition.getX(), this.getHeight(this.templatePosition, bqu, fu2), this.templatePosition.getZ());
            return super.postProcess(bqu, bqq, cha, random, ctd, bph, fu);
        }
        
        private int getHeight(final BlockPos fu1, final BlockGetter bpg, final BlockPos fu3) {
            int integer5 = fu1.getY();
            int integer6 = 512;
            final int integer7 = integer5 - 1;
            int integer8 = 0;
            for (final BlockPos fu4 : BlockPos.betweenClosed(fu1, fu3)) {
                final int integer9 = fu4.getX();
                final int integer10 = fu4.getZ();
                int integer11 = fu1.getY() - 1;
                final BlockPos.MutableBlockPos a14 = new BlockPos.MutableBlockPos(integer9, integer11, integer10);
                BlockState cfj15 = bpg.getBlockState(a14);
                for (FluidState cxa16 = bpg.getFluidState(a14); (cfj15.isAir() || cxa16.is(FluidTags.WATER) || cfj15.getBlock().is(BlockTags.ICE)) && integer11 > 1; cfj15 = bpg.getBlockState(a14), cxa16 = bpg.getFluidState(a14)) {
                    --integer11;
                    a14.set(integer9, integer11, integer10);
                }
                integer6 = Math.min(integer6, integer11);
                if (integer11 < integer7 - 2) {
                    ++integer8;
                }
            }
            final int integer12 = Math.abs(fu1.getX() - fu3.getX());
            if (integer7 - integer6 > 2 && integer8 > integer12 - 2) {
                integer5 = integer6 + 1;
            }
            return integer5;
        }
    }
}
