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.core.BlockPos;
import java.util.Random;
import net.minecraft.world.level.levelgen.ChunkGeneratorSettings;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.LevelAccessor;
import com.mojang.datafixers.Dynamic;
import java.util.function.Function;
import net.minecraft.world.level.levelgen.feature.configurations.BlockStateConfiguration;

public class IcebergFeature extends Feature<BlockStateConfiguration> {
    public IcebergFeature(final Function<Dynamic<?>, ? extends BlockStateConfiguration> function) {
        super(function);
    }
    
    @Override
    public boolean place(final LevelAccessor bjx, final ChunkGenerator<? extends ChunkGeneratorSettings> caa, final Random random, BlockPos fk, final BlockStateConfiguration cge) {
        fk = new BlockPos(fk.getX(), bjx.getSeaLevel(), fk.getZ());
        final boolean boolean7 = random.nextDouble() > 0.7;
        final BlockState byj8 = cge.state;
        final double double9 = random.nextDouble() * 2.0 * 3.141592653589793;
        final int integer11 = 11 - random.nextInt(5);
        final int integer12 = 3 + random.nextInt(3);
        final boolean boolean8 = random.nextDouble() > 0.7;
        final int integer13 = 11;
        int integer14 = boolean8 ? (random.nextInt(6) + 6) : (random.nextInt(15) + 3);
        if (!boolean8 && random.nextDouble() > 0.9) {
            integer14 += random.nextInt(19) + 7;
        }
        final int integer15 = Math.min(integer14 + random.nextInt(11), 18);
        final int integer16 = Math.min(integer14 + random.nextInt(7) - random.nextInt(5), 11);
        final int integer17 = boolean8 ? integer11 : 11;
        for (int integer18 = -integer17; integer18 < integer17; ++integer18) {
            for (int integer19 = -integer17; integer19 < integer17; ++integer19) {
                for (int integer20 = 0; integer20 < integer14; ++integer20) {
                    final int integer21 = boolean8 ? this.heightDependentRadiusEllipse(integer20, integer14, integer16) : this.heightDependentRadiusRound(random, integer20, integer14, integer16);
                    if (boolean8 || integer18 < integer21) {
                        this.generateIcebergBlock(bjx, random, fk, integer14, integer18, integer20, integer19, integer21, integer17, boolean8, integer12, double9, boolean7, byj8);
                    }
                }
            }
        }
        this.smooth(bjx, fk, integer16, integer14, boolean8, integer11);
        for (int integer18 = -integer17; integer18 < integer17; ++integer18) {
            for (int integer19 = -integer17; integer19 < integer17; ++integer19) {
                for (int integer20 = -1; integer20 > -integer15; --integer20) {
                    final int integer21 = boolean8 ? Mth.ceil(integer17 * (1.0f - (float)Math.pow(integer20, 2.0) / (integer15 * 8.0f))) : integer17;
                    final int integer22 = this.heightDependentRadiusSteep(random, -integer20, integer15, integer16);
                    if (integer18 < integer22) {
                        this.generateIcebergBlock(bjx, random, fk, integer15, integer18, integer20, integer19, integer22, integer21, boolean8, integer12, double9, boolean7, byj8);
                    }
                }
            }
        }
        final boolean boolean9 = boolean8 ? (random.nextDouble() > 0.1) : (random.nextDouble() > 0.7);
        if (boolean9) {
            this.generateCutOut(random, bjx, integer16, integer14, fk, boolean8, integer11, double9, integer12);
        }
        return true;
    }
    
    private void generateCutOut(final Random random, final LevelAccessor bjx, final int integer3, final int integer4, final BlockPos fk, 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 fk2 = 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, fk, bjx, false, double9, fk2, 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, fk, bjx, true, double9, fk2, integer7, integer9);
        }
    }
    
    private void carve(final int integer1, final int integer2, final BlockPos fk3, final LevelAccessor bjx, final boolean boolean5, final double double6, final BlockPos fk7, 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, fk7, integer10, integer11, double6);
                if (double7 < 0.0) {
                    final BlockPos fk8 = fk3.offset(integer12, integer2, integer13);
                    final Block bph19 = bjx.getBlockState(fk8).getBlock();
                    if (this.isIcebergBlock(bph19) || bph19 == Blocks.SNOW_BLOCK) {
                        if (boolean5) {
                            this.setBlock(bjx, fk8, Blocks.WATER.defaultBlockState());
                        }
                        else {
                            this.setBlock(bjx, fk8, Blocks.AIR.defaultBlockState());
                            this.removeFloatingSnowLayer(bjx, fk8);
                        }
                    }
                }
            }
        }
    }
    
    private void removeFloatingSnowLayer(final LevelAccessor bjx, final BlockPos fk) {
        if (bjx.getBlockState(fk.above()).getBlock() == Blocks.SNOW) {
            this.setBlock(bjx, fk.above(), Blocks.AIR.defaultBlockState());
        }
    }
    
    private void generateIcebergBlock(final LevelAccessor bjx, final Random random, final BlockPos fk, 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 byj) {
        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 fk2 = fk.offset(integer5, integer6, integer7);
            final double double14 = boolean10 ? -0.5 : (-6 - random.nextInt(3));
            if (double13 > double14 && random.nextDouble() > 0.9) {
                return;
            }
            this.setIcebergBlock(fk2, bjx, random, integer4 - integer6, integer4, boolean10, boolean13, byj);
        }
    }
    
    private void setIcebergBlock(final BlockPos fk, final LevelAccessor bjx, final Random random, final int integer4, final int integer5, final boolean boolean6, final boolean boolean7, final BlockState byj) {
        final BlockState byj2 = bjx.getBlockState(fk);
        final Block bph11 = byj2.getBlock();
        if (byj2.getMaterial() == Material.AIR || bph11 == Blocks.SNOW_BLOCK || bph11 == Blocks.ICE || bph11 == Blocks.WATER) {
            final boolean boolean8 = !boolean6 || random.nextDouble() > 0.05;
            final int integer6 = boolean6 ? 3 : 2;
            if (boolean7 && bph11 != Blocks.WATER && integer4 <= random.nextInt(Math.max(1, integer5 / integer6)) + integer5 * 0.6 && boolean8) {
                this.setBlock(bjx, fk, Blocks.SNOW_BLOCK.defaultBlockState());
            }
            else {
                this.setBlock(bjx, fk, byj);
            }
        }
    }
    
    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 fk, 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 - fk.getX(), 2.0) + Math.pow(integer2 - fk.getZ(), 2.0) - Math.pow(integer4, 2.0);
    }
    
    private double signedDistanceEllipse(final int integer1, final int integer2, final BlockPos fk, final int integer4, final int integer5, final double double6) {
        return Math.pow(((integer1 - fk.getX()) * Math.cos(double6) - (integer2 - fk.getZ()) * Math.sin(double6)) / integer4, 2.0) + Math.pow(((integer1 - fk.getX()) * Math.sin(double6) + (integer2 - fk.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 bph) {
        return bph == Blocks.PACKED_ICE || bph == Blocks.SNOW_BLOCK || bph == Blocks.BLUE_ICE;
    }
    
    private boolean belowIsAir(final BlockGetter bjg, final BlockPos fk) {
        return bjg.getBlockState(fk.below()).getMaterial() == Material.AIR;
    }
    
    private void smooth(final LevelAccessor bjx, final BlockPos fk, 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 fk2 = fk.offset(integer8, integer10, integer9);
                    final Block bph13 = bjx.getBlockState(fk2).getBlock();
                    if (this.isIcebergBlock(bph13) || bph13 == Blocks.SNOW) {
                        if (this.belowIsAir(bjx, fk2)) {
                            this.setBlock(bjx, fk2, Blocks.AIR.defaultBlockState());
                            this.setBlock(bjx, fk2.above(), Blocks.AIR.defaultBlockState());
                        }
                        else if (this.isIcebergBlock(bph13)) {
                            final Block[] arr14 = { bjx.getBlockState(fk2.west()).getBlock(), bjx.getBlockState(fk2.east()).getBlock(), bjx.getBlockState(fk2.north()).getBlock(), bjx.getBlockState(fk2.south()).getBlock() };
                            int integer11 = 0;
                            for (final Block bph14 : arr14) {
                                if (!this.isIcebergBlock(bph14)) {
                                    ++integer11;
                                }
                            }
                            if (integer11 >= 3) {
                                this.setBlock(bjx, fk2, Blocks.AIR.defaultBlockState());
                            }
                        }
                    }
                }
            }
        }
    }
}
