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

import net.minecraft.world.level.block.state.AbstractStateHolder;
import net.minecraft.world.level.material.FluidState;
import java.util.Iterator;
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.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.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 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 OceanRuinPieces.WARM_RUINS[random.nextInt(OceanRuinPieces.WARM_RUINS.length)];
    }
    
    private static ResourceLocation getBigWarmRuin(final Random random) {
        return OceanRuinPieces.BIG_WARM_RUINS[random.nextInt(OceanRuinPieces.BIG_WARM_RUINS.length)];
    }
    
    public static void addPieces(final StructureManager cmo, final BlockPos fk, final Rotation btu, final List<StructurePiece> list, final Random random, final OceanRuinConfiguration cgw) {
        final boolean boolean7 = random.nextFloat() <= cgw.largeProbability;
        final float float8 = boolean7 ? 0.9f : 0.8f;
        addPiece(cmo, fk, btu, list, random, cgw, boolean7, float8);
        if (boolean7 && random.nextFloat() <= cgw.clusterProbability) {
            addClusterRuins(cmo, random, btu, fk, cgw, list);
        }
    }
    
    private static void addClusterRuins(final StructureManager cmo, final Random random, final Rotation btu, final BlockPos fk, final OceanRuinConfiguration cgw, final List<StructurePiece> list) {
        final int integer7 = fk.getX();
        final int integer8 = fk.getZ();
        final BlockPos fk2 = StructureTemplate.transform(new BlockPos(15, 0, 15), Mirror.NONE, btu, BlockPos.ZERO).offset(integer7, 0, integer8);
        final BoundingBox clb10 = BoundingBox.createProper(integer7, 0, integer8, fk2.getX(), 0, fk2.getZ());
        final BlockPos fk3 = new BlockPos(Math.min(integer7, fk2.getX()), 0, Math.min(integer8, fk2.getZ()));
        final List<BlockPos> list2 = allPositions(random, fk3.getX(), fk3.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 fk4 = list2.remove(integer11);
                final int integer12 = fk4.getX();
                final int integer13 = fk4.getZ();
                final Rotation btu2 = Rotation.values()[random.nextInt(Rotation.values().length)];
                final BlockPos fk5 = StructureTemplate.transform(new BlockPos(5, 0, 6), Mirror.NONE, btu2, BlockPos.ZERO).offset(integer12, 0, integer13);
                final BoundingBox clb11 = BoundingBox.createProper(integer12, 0, integer13, fk5.getX(), 0, fk5.getZ());
                if (!clb11.intersects(clb10)) {
                    addPiece(cmo, fk4, btu2, list, random, cgw, 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 cmo, final BlockPos fk, final Rotation btu, final List<StructurePiece> list, final Random random, final OceanRuinConfiguration cgw, final boolean boolean7, final float float8) {
        if (cgw.biomeTemp == OceanRuinFeature.Type.WARM) {
            final ResourceLocation sm9 = boolean7 ? getBigWarmRuin(random) : getSmallWarmRuin(random);
            list.add(new OceanRuinPiece(cmo, sm9, fk, btu, float8, cgw.biomeTemp, boolean7));
        }
        else if (cgw.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(cmo, arr9[integer12], fk, btu, float8, cgw.biomeTemp, boolean7));
            list.add(new OceanRuinPiece(cmo, arr10[integer12], fk, btu, 0.7f, cgw.biomeTemp, boolean7));
            list.add(new OceanRuinPiece(cmo, arr11[integer12], fk, btu, 0.5f, cgw.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 cmo, final ResourceLocation sm, final BlockPos fk, final Rotation btu, final float float5, final OceanRuinFeature.Type b, final boolean boolean7) {
            super(StructurePieceType.OCEAN_RUIN, 0);
            this.templateLocation = sm;
            this.templatePosition = fk;
            this.rotation = btu;
            this.integrity = float5;
            this.biomeType = b;
            this.isLarge = boolean7;
            this.loadTemplate(cmo);
        }
        
        public OceanRuinPiece(final StructureManager cmo, final CompoundTag jt) {
            super(StructurePieceType.OCEAN_RUIN, jt);
            this.templateLocation = new ResourceLocation(jt.getString("Template"));
            this.rotation = Rotation.valueOf(jt.getString("Rot"));
            this.integrity = jt.getFloat("Integrity");
            this.biomeType = OceanRuinFeature.Type.valueOf(jt.getString("BiomeType"));
            this.isLarge = jt.getBoolean("IsLarge");
            this.loadTemplate(cmo);
        }
        
        private void loadTemplate(final StructureManager cmo) {
            final StructureTemplate cms3 = cmo.getOrCreate(this.templateLocation);
            final StructurePlaceSettings cmp4 = new StructurePlaceSettings().setRotation(this.rotation).setMirror(Mirror.NONE).addProcessor(BlockIgnoreProcessor.STRUCTURE_AND_AIR);
            this.setup(cms3, this.templatePosition, cmp4);
        }
        
        @Override
        protected void addAdditionalSaveData(final CompoundTag jt) {
            super.addAdditionalSaveData(jt);
            jt.putString("Template", this.templateLocation.toString());
            jt.putString("Rot", this.rotation.name());
            jt.putFloat("Integrity", this.integrity);
            jt.putString("BiomeType", this.biomeType.toString());
            jt.putBoolean("IsLarge", this.isLarge);
        }
        
        @Override
        protected void handleDataMarker(final String string, final BlockPos fk, final LevelAccessor bjx, final Random random, final BoundingBox clb) {
            if ("chest".equals(string)) {
                bjx.setBlock(fk, ((AbstractStateHolder<O, BlockState>)Blocks.CHEST.defaultBlockState()).<Comparable, Boolean>setValue((Property<Comparable>)ChestBlock.WATERLOGGED, bjx.getFluidState(fk).is(FluidTags.WATER)), 2);
                final BlockEntity bwl7 = bjx.getBlockEntity(fk);
                if (bwl7 instanceof ChestBlockEntity) {
                    ((ChestBlockEntity)bwl7).setLootTable(this.isLarge ? BuiltInLootTables.UNDERWATER_RUIN_BIG : BuiltInLootTables.UNDERWATER_RUIN_SMALL, random.nextLong());
                }
            }
            else if ("drowned".equals(string)) {
                final Drowned awk7 = EntityType.DROWNED.create(bjx.getLevel());
                awk7.setPersistenceRequired();
                awk7.moveTo(fk, 0.0f, 0.0f);
                awk7.finalizeSpawn(bjx, bjx.getCurrentDifficultyAt(fk), MobSpawnType.STRUCTURE, null, null);
                bjx.addFreshEntity(awk7);
                if (fk.getY() > bjx.getSeaLevel()) {
                    bjx.setBlock(fk, Blocks.AIR.defaultBlockState(), 2);
                }
                else {
                    bjx.setBlock(fk, Blocks.WATER.defaultBlockState(), 2);
                }
            }
        }
        
        @Override
        public boolean postProcess(final LevelAccessor bjx, final ChunkGenerator<?> caa, final Random random, final BoundingBox clb, final ChunkPos bjh) {
            this.placeSettings.clearProcessors().addProcessor(new BlockRotProcessor(this.integrity)).addProcessor(BlockIgnoreProcessor.STRUCTURE_AND_AIR);
            final int integer7 = bjx.getHeight(Heightmap.Types.OCEAN_FLOOR_WG, this.templatePosition.getX(), this.templatePosition.getZ());
            this.templatePosition = new BlockPos(this.templatePosition.getX(), integer7, this.templatePosition.getZ());
            final BlockPos fk8 = 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, bjx, fk8), this.templatePosition.getZ());
            return super.postProcess(bjx, caa, random, clb, bjh);
        }
        
        private int getHeight(final BlockPos fk1, final BlockGetter bjg, final BlockPos fk3) {
            int integer5 = fk1.getY();
            int integer6 = 512;
            final int integer7 = integer5 - 1;
            int integer8 = 0;
            for (final BlockPos fk4 : BlockPos.betweenClosed(fk1, fk3)) {
                final int integer9 = fk4.getX();
                final int integer10 = fk4.getZ();
                int integer11 = fk1.getY() - 1;
                final BlockPos.MutableBlockPos a14 = new BlockPos.MutableBlockPos(integer9, integer11, integer10);
                BlockState byj15 = bjg.getBlockState(a14);
                for (FluidState coj16 = bjg.getFluidState(a14); (byj15.isAir() || coj16.is(FluidTags.WATER) || byj15.getBlock().is(BlockTags.ICE)) && integer11 > 1; byj15 = bjg.getBlockState(a14), coj16 = bjg.getFluidState(a14)) {
                    --integer11;
                    a14.set(integer9, integer11, integer10);
                }
                integer6 = Math.min(integer6, integer11);
                if (integer11 < integer7 - 2) {
                    ++integer8;
                }
            }
            final int integer12 = Math.abs(fk1.getX() - fk3.getX());
            if (integer7 - integer6 > 2 && integer8 > integer12 - 2) {
                integer5 = integer6 + 1;
            }
            return integer5;
        }
    }
}
