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

import net.minecraft.world.level.levelgen.feature.configurations.FeatureConfiguration;
import net.minecraft.core.Direction;
import javax.annotation.Nullable;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
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;

public class HugeFungusFeature extends Feature<HugeFungusConfiguration> {
    public HugeFungusFeature(final Codec<HugeFungusConfiguration> codec) {
        super(codec);
    }
    
    @Override
    public boolean place(final WorldGenLevel bqu, final StructureFeatureManager bqq, final ChunkGenerator cha, final Random random, final BlockPos fu, final HugeFungusConfiguration cky) {
        final Block bvr8 = cky.validBaseState.getBlock();
        BlockPos fu2 = null;
        if (cky.planted) {
            final Block bvr9 = bqu.getBlockState(fu.below()).getBlock();
            if (bvr9 == bvr8) {
                fu2 = fu;
            }
        }
        else {
            fu2 = findOnNyliumPosition(bqu, fu, bvr8);
        }
        if (fu2 == null) {
            return false;
        }
        int integer10 = Mth.nextInt(random, 4, 13);
        if (random.nextInt(12) == 0) {
            integer10 *= 2;
        }
        if (!cky.planted) {
            final int integer11 = cha.getGenDepth();
            if (fu2.getY() + integer10 + 1 >= integer11) {
                return false;
            }
        }
        final boolean boolean11 = !cky.planted && random.nextFloat() < 0.06f;
        bqu.setBlock(fu, Blocks.AIR.defaultBlockState(), 4);
        this.placeStem(bqu, random, cky, fu2, integer10, boolean11);
        this.placeHat(bqu, random, cky, fu2, integer10, boolean11);
        return true;
    }
    
    private static boolean isReplaceable(final LevelAccessor bqc, final BlockPos fu, final boolean boolean3) {
        final Material cxd3;
        return bqc.isStateAtPosition(fu, cfj -> {
            cxd3 = cfj.getMaterial();
            return cfj.isAir() || cfj.is(Blocks.WATER) || cfj.is(Blocks.LAVA) || cxd3 == Material.REPLACEABLE_PLANT || (boolean3 && cxd3 == Material.PLANT);
        });
    }
    
    private void placeStem(final LevelAccessor bqc, final Random random, final HugeFungusConfiguration cky, final BlockPos fu, final int integer, final boolean boolean6) {
        final BlockPos.MutableBlockPos a8 = new BlockPos.MutableBlockPos();
        final BlockState cfj9 = cky.stemState;
        for (int integer2 = boolean6 ? 1 : 0, integer3 = -integer2; integer3 <= integer2; ++integer3) {
            for (int integer4 = -integer2; integer4 <= integer2; ++integer4) {
                final boolean boolean7 = boolean6 && Mth.abs(integer3) == integer2 && Mth.abs(integer4) == integer2;
                for (int integer5 = 0; integer5 < integer; ++integer5) {
                    a8.setWithOffset(fu, integer3, integer5, integer4);
                    if (isReplaceable(bqc, a8, true)) {
                        if (cky.planted) {
                            if (!bqc.getBlockState(a8.below()).isAir()) {
                                bqc.destroyBlock(a8, true);
                            }
                            bqc.setBlock(a8, cfj9, 3);
                        }
                        else if (boolean7) {
                            if (random.nextFloat() < 0.1f) {
                                this.setBlock(bqc, a8, cfj9);
                            }
                        }
                        else {
                            this.setBlock(bqc, a8, cfj9);
                        }
                    }
                }
            }
        }
    }
    
    private void placeHat(final LevelAccessor bqc, final Random random, final HugeFungusConfiguration cky, final BlockPos fu, final int integer, final boolean boolean6) {
        final BlockPos.MutableBlockPos a8 = new BlockPos.MutableBlockPos();
        final boolean boolean7 = cky.hatState.is(Blocks.NETHER_WART_BLOCK);
        final int integer2 = Math.min(random.nextInt(1 + integer / 3) + 5, integer);
        int integer4;
        for (int integer3 = integer4 = integer - integer2; integer4 <= integer; ++integer4) {
            int integer5 = (integer4 < integer - random.nextInt(3)) ? 2 : 1;
            if (integer2 > 8 && integer4 < integer3 + 4) {
                integer5 = 3;
            }
            if (boolean6) {
                ++integer5;
            }
            for (int integer6 = -integer5; integer6 <= integer5; ++integer6) {
                for (int integer7 = -integer5; integer7 <= integer5; ++integer7) {
                    final boolean boolean8 = integer6 == -integer5 || integer6 == integer5;
                    final boolean boolean9 = integer7 == -integer5 || integer7 == integer5;
                    final boolean boolean10 = !boolean8 && !boolean9 && integer4 != integer;
                    final boolean boolean11 = boolean8 && boolean9;
                    final boolean boolean12 = integer4 < integer3 + 3;
                    a8.setWithOffset(fu, integer6, integer4, integer7);
                    if (isReplaceable(bqc, a8, false)) {
                        if (cky.planted && !bqc.getBlockState(a8.below()).isAir()) {
                            bqc.destroyBlock(a8, true);
                        }
                        if (boolean12) {
                            if (!boolean10) {
                                this.placeHatDropBlock(bqc, random, a8, cky.hatState, boolean7);
                            }
                        }
                        else if (boolean10) {
                            this.placeHatBlock(bqc, random, cky, a8, 0.1f, 0.2f, boolean7 ? 0.1f : 0.0f);
                        }
                        else if (boolean11) {
                            this.placeHatBlock(bqc, random, cky, a8, 0.01f, 0.7f, boolean7 ? 0.083f : 0.0f);
                        }
                        else {
                            this.placeHatBlock(bqc, random, cky, a8, 5.0E-4f, 0.98f, boolean7 ? 0.07f : 0.0f);
                        }
                    }
                }
            }
        }
    }
    
    private void placeHatBlock(final LevelAccessor bqc, final Random random, final HugeFungusConfiguration cky, final BlockPos.MutableBlockPos a, final float float5, final float float6, final float float7) {
        if (random.nextFloat() < float5) {
            this.setBlock(bqc, a, cky.decorState);
        }
        else if (random.nextFloat() < float6) {
            this.setBlock(bqc, a, cky.hatState);
            if (random.nextFloat() < float7) {
                tryPlaceWeepingVines(a, bqc, random);
            }
        }
    }
    
    private void placeHatDropBlock(final LevelAccessor bqc, final Random random, final BlockPos fu, final BlockState cfj, final boolean boolean5) {
        if (bqc.getBlockState(fu.below()).is(cfj.getBlock())) {
            this.setBlock(bqc, fu, cfj);
        }
        else if (random.nextFloat() < 0.15) {
            this.setBlock(bqc, fu, cfj);
            if (boolean5 && random.nextInt(11) == 0) {
                tryPlaceWeepingVines(fu, bqc, random);
            }
        }
    }
    
    @Nullable
    private static BlockPos.MutableBlockPos findOnNyliumPosition(final LevelAccessor bqc, final BlockPos fu, final Block bvr) {
        final BlockPos.MutableBlockPos a4 = fu.mutable();
        for (int integer5 = fu.getY(); integer5 >= 1; --integer5) {
            a4.setY(integer5);
            final Block bvr2 = bqc.getBlockState(a4.below()).getBlock();
            if (bvr2 == bvr) {
                return a4;
            }
        }
        return null;
    }
    
    private static void tryPlaceWeepingVines(final BlockPos fu, final LevelAccessor bqc, final Random random) {
        final BlockPos.MutableBlockPos a4 = fu.mutable().move(Direction.DOWN);
        if (!bqc.isEmptyBlock(a4)) {
            return;
        }
        int integer5 = Mth.nextInt(random, 1, 5);
        if (random.nextInt(7) == 0) {
            integer5 *= 2;
        }
        final int integer6 = 23;
        final int integer7 = 25;
        WeepingVinesFeature.placeWeepingVinesColumn(bqc, random, a4, integer5, 23, 25);
    }
}
