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

import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.util.Mth;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.core.BlockPos;
import java.util.Random;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.StructureFeatureManager;
import net.minecraft.world.level.WorldGenLevel;
import com.mojang.serialization.Codec;
import net.minecraft.world.level.levelgen.feature.configurations.BlockStateConfiguration;

public class IcebergFeature extends Feature<BlockStateConfiguration> {
    public IcebergFeature(final Codec<BlockStateConfiguration> codec) {
        super(codec);
    }
    
    @Override
    public boolean place(final WorldGenLevel bqu, final StructureFeatureManager bqq, final ChunkGenerator cha, final Random random, BlockPos fu, final BlockStateConfiguration cng) {
        fu = new BlockPos(fu.getX(), cha.getSeaLevel(), fu.getZ());
        final boolean boolean8 = random.nextDouble() > 0.7;
        final BlockState cfj9 = cng.state;
        final double double10 = random.nextDouble() * 2.0 * 3.141592653589793;
        final int integer12 = 11 - random.nextInt(5);
        final int integer13 = 3 + random.nextInt(3);
        final boolean boolean9 = random.nextDouble() > 0.7;
        final int integer14 = 11;
        int integer15 = boolean9 ? (random.nextInt(6) + 6) : (random.nextInt(15) + 3);
        if (!boolean9 && random.nextDouble() > 0.9) {
            integer15 += random.nextInt(19) + 7;
        }
        final int integer16 = Math.min(integer15 + random.nextInt(11), 18);
        final int integer17 = Math.min(integer15 + random.nextInt(7) - random.nextInt(5), 11);
        final int integer18 = boolean9 ? integer12 : 11;
        for (int integer19 = -integer18; integer19 < integer18; ++integer19) {
            for (int integer20 = -integer18; integer20 < integer18; ++integer20) {
                for (int integer21 = 0; integer21 < integer15; ++integer21) {
                    final int integer22 = boolean9 ? this.heightDependentRadiusEllipse(integer21, integer15, integer17) : this.heightDependentRadiusRound(random, integer21, integer15, integer17);
                    if (boolean9 || integer19 < integer22) {
                        this.generateIcebergBlock(bqu, random, fu, integer15, integer19, integer21, integer20, integer22, integer18, boolean9, integer13, double10, boolean8, cfj9);
                    }
                }
            }
        }
        this.smooth(bqu, fu, integer17, integer15, boolean9, integer12);
        for (int integer19 = -integer18; integer19 < integer18; ++integer19) {
            for (int integer20 = -integer18; integer20 < integer18; ++integer20) {
                for (int integer21 = -1; integer21 > -integer16; --integer21) {
                    final int integer22 = boolean9 ? Mth.ceil(integer18 * (1.0f - (float)Math.pow(integer21, 2.0) / (integer16 * 8.0f))) : integer18;
                    final int integer23 = this.heightDependentRadiusSteep(random, -integer21, integer16, integer17);
                    if (integer19 < integer23) {
                        this.generateIcebergBlock(bqu, random, fu, integer16, integer19, integer21, integer20, integer23, integer22, boolean9, integer13, double10, boolean8, cfj9);
                    }
                }
            }
        }
        final boolean boolean10 = boolean9 ? (random.nextDouble() > 0.1) : (random.nextDouble() > 0.7);
        if (boolean10) {
            this.generateCutOut(random, bqu, integer17, integer15, fu, boolean9, integer12, double10, integer13);
        }
        return true;
    }
    
    private void generateCutOut(final Random random, final LevelAccessor bqc, final int integer3, final int integer4, final BlockPos fu, final boolean boolean6, final int integer7, final double double8, final int integer9) {
        final int integer10 = random.nextBoolean() ? -1 : 1;
        final int integer11 = random.nextBoolean() ? -1 : 1;
        int integer12 = random.nextInt(Math.max(integer3 / 2 - 2, 1));
        if (random.nextBoolean()) {
            integer12 = integer3 / 2 + 1 - random.nextInt(Math.max(integer3 - integer3 / 2 - 1, 1));
        }
        int integer13 = random.nextInt(Math.max(integer3 / 2 - 2, 1));
        if (random.nextBoolean()) {
            integer13 = integer3 / 2 + 1 - random.nextInt(Math.max(integer3 - integer3 / 2 - 1, 1));
        }
        if (boolean6) {
            integer13 = (integer12 = random.nextInt(Math.max(integer7 - 5, 1)));
        }
        final BlockPos fu2 = new BlockPos(integer10 * integer12, 0, integer11 * integer13);
        final double double9 = boolean6 ? (double8 + 1.5707963267948966) : (random.nextDouble() * 2.0 * 3.141592653589793);
        for (int integer14 = 0; integer14 < integer4 - 3; ++integer14) {
            final int integer15 = this.heightDependentRadiusRound(random, integer14, integer4, integer3);
            this.carve(integer15, integer14, fu, bqc, false, double9, fu2, integer7, integer9);
        }
        for (int integer14 = -1; integer14 > -integer4 + random.nextInt(5); --integer14) {
            final int integer15 = this.heightDependentRadiusSteep(random, -integer14, integer4, integer3);
            this.carve(integer15, integer14, fu, bqc, true, double9, fu2, integer7, integer9);
        }
    }
    
    private void carve(final int integer1, final int integer2, final BlockPos fu3, final LevelAccessor bqc, final boolean boolean5, final double double6, final BlockPos fu7, final int integer8, final int integer9) {
        final int integer10 = integer1 + 1 + integer8 / 3;
        final int integer11 = Math.min(integer1 - 3, 3) + integer9 / 2 - 1;
        for (int integer12 = -integer10; integer12 < integer10; ++integer12) {
            for (int integer13 = -integer10; integer13 < integer10; ++integer13) {
                final double double7 = this.signedDistanceEllipse(integer12, integer13, fu7, integer10, integer11, double6);
                if (double7 < 0.0) {
                    final BlockPos fu8 = fu3.offset(integer12, integer2, integer13);
                    final Block bvr19 = bqc.getBlockState(fu8).getBlock();
                    if (this.isIcebergBlock(bvr19) || bvr19 == Blocks.SNOW_BLOCK) {
                        if (boolean5) {
                            this.setBlock(bqc, fu8, Blocks.WATER.defaultBlockState());
                        }
                        else {
                            this.setBlock(bqc, fu8, Blocks.AIR.defaultBlockState());
                            this.removeFloatingSnowLayer(bqc, fu8);
                        }
                    }
                }
            }
        }
    }
    
    private void removeFloatingSnowLayer(final LevelAccessor bqc, final BlockPos fu) {
        if (bqc.getBlockState(fu.above()).is(Blocks.SNOW)) {
            this.setBlock(bqc, fu.above(), Blocks.AIR.defaultBlockState());
        }
    }
    
    private void generateIcebergBlock(final LevelAccessor bqc, final Random random, final BlockPos fu, final int integer4, final int integer5, final int integer6, final int integer7, final int integer8, final int integer9, final boolean boolean10, final int integer11, final double double12, final boolean boolean13, final BlockState cfj) {
        final double double13 = boolean10 ? this.signedDistanceEllipse(integer5, integer7, BlockPos.ZERO, integer9, this.getEllipseC(integer6, integer4, integer11), double12) : this.signedDistanceCircle(integer5, integer7, BlockPos.ZERO, integer8, random);
        if (double13 < 0.0) {
            final BlockPos fu2 = fu.offset(integer5, integer6, integer7);
            final double double14 = boolean10 ? -0.5 : (-6 - random.nextInt(3));
            if (double13 > double14 && random.nextDouble() > 0.9) {
                return;
            }
            this.setIcebergBlock(fu2, bqc, random, integer4 - integer6, integer4, boolean10, boolean13, cfj);
        }
    }
    
    private void setIcebergBlock(final BlockPos fu, final LevelAccessor bqc, final Random random, final int integer4, final int integer5, final boolean boolean6, final boolean boolean7, final BlockState cfj) {
        final BlockState cfj2 = bqc.getBlockState(fu);
        if (cfj2.getMaterial() == Material.AIR || cfj2.is(Blocks.SNOW_BLOCK) || cfj2.is(Blocks.ICE) || cfj2.is(Blocks.WATER)) {
            final boolean boolean8 = !boolean6 || random.nextDouble() > 0.05;
            final int integer6 = boolean6 ? 3 : 2;
            if (boolean7 && !cfj2.is(Blocks.WATER) && integer4 <= random.nextInt(Math.max(1, integer5 / integer6)) + integer5 * 0.6 && boolean8) {
                this.setBlock(bqc, fu, Blocks.SNOW_BLOCK.defaultBlockState());
            }
            else {
                this.setBlock(bqc, fu, cfj);
            }
        }
    }
    
    private int getEllipseC(final int integer1, final int integer2, final int integer3) {
        int integer4 = integer3;
        if (integer1 > 0 && integer2 - integer1 <= 3) {
            integer4 -= 4 - (integer2 - integer1);
        }
        return integer4;
    }
    
    private double signedDistanceCircle(final int integer1, final int integer2, final BlockPos fu, final int integer4, final Random random) {
        final float float7 = 10.0f * Mth.clamp(random.nextFloat(), 0.2f, 0.8f) / integer4;
        return float7 + Math.pow(integer1 - fu.getX(), 2.0) + Math.pow(integer2 - fu.getZ(), 2.0) - Math.pow(integer4, 2.0);
    }
    
    private double signedDistanceEllipse(final int integer1, final int integer2, final BlockPos fu, final int integer4, final int integer5, final double double6) {
        return Math.pow(((integer1 - fu.getX()) * Math.cos(double6) - (integer2 - fu.getZ()) * Math.sin(double6)) / integer4, 2.0) + Math.pow(((integer1 - fu.getX()) * Math.sin(double6) + (integer2 - fu.getZ()) * Math.cos(double6)) / integer5, 2.0) - 1.0;
    }
    
    private int heightDependentRadiusRound(final Random random, final int integer2, final int integer3, final int integer4) {
        final float float6 = 3.5f - random.nextFloat();
        float float7 = (1.0f - (float)Math.pow(integer2, 2.0) / (integer3 * float6)) * integer4;
        if (integer3 > 15 + random.nextInt(5)) {
            final int integer5 = (integer2 < 3 + random.nextInt(6)) ? (integer2 / 2) : integer2;
            float7 = (1.0f - integer5 / (integer3 * float6 * 0.4f)) * integer4;
        }
        return Mth.ceil(float7 / 2.0f);
    }
    
    private int heightDependentRadiusEllipse(final int integer1, final int integer2, final int integer3) {
        final float float5 = 1.0f;
        final float float6 = (1.0f - (float)Math.pow(integer1, 2.0) / (integer2 * 1.0f)) * integer3;
        return Mth.ceil(float6 / 2.0f);
    }
    
    private int heightDependentRadiusSteep(final Random random, final int integer2, final int integer3, final int integer4) {
        final float float6 = 1.0f + random.nextFloat() / 2.0f;
        final float float7 = (1.0f - integer2 / (integer3 * float6)) * integer4;
        return Mth.ceil(float7 / 2.0f);
    }
    
    private boolean isIcebergBlock(final Block bvr) {
        return bvr == Blocks.PACKED_ICE || bvr == Blocks.SNOW_BLOCK || bvr == Blocks.BLUE_ICE;
    }
    
    private boolean belowIsAir(final BlockGetter bpg, final BlockPos fu) {
        return bpg.getBlockState(fu.below()).getMaterial() == Material.AIR;
    }
    
    private void smooth(final LevelAccessor bqc, final BlockPos fu, final int integer3, final int integer4, final boolean boolean5, final int integer6) {
        for (int integer7 = boolean5 ? integer6 : (integer3 / 2), integer8 = -integer7; integer8 <= integer7; ++integer8) {
            for (int integer9 = -integer7; integer9 <= integer7; ++integer9) {
                for (int integer10 = 0; integer10 <= integer4; ++integer10) {
                    final BlockPos fu2 = fu.offset(integer8, integer10, integer9);
                    final Block bvr13 = bqc.getBlockState(fu2).getBlock();
                    if (this.isIcebergBlock(bvr13) || bvr13 == Blocks.SNOW) {
                        if (this.belowIsAir(bqc, fu2)) {
                            this.setBlock(bqc, fu2, Blocks.AIR.defaultBlockState());
                            this.setBlock(bqc, fu2.above(), Blocks.AIR.defaultBlockState());
                        }
                        else if (this.isIcebergBlock(bvr13)) {
                            final Block[] arr14 = { bqc.getBlockState(fu2.west()).getBlock(), bqc.getBlockState(fu2.east()).getBlock(), bqc.getBlockState(fu2.north()).getBlock(), bqc.getBlockState(fu2.south()).getBlock() };
                            int integer11 = 0;
                            for (final Block bvr14 : arr14) {
                                if (!this.isIcebergBlock(bvr14)) {
                                    ++integer11;
                                }
                            }
                            if (integer11 >= 3) {
                                this.setBlock(bqc, fu2, Blocks.AIR.defaultBlockState());
                            }
                        }
                    }
                }
            }
        }
    }
}
