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

import com.mojang.serialization.Codec;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkGenerator;
import net.minecraft.world.level.levelgen.feature.configurations.VegetationPatchConfiguration;

public class VegetationPatchFeature extends Feature<VegetationPatchConfiguration> {
   public VegetationPatchFeature(Codec<VegetationPatchConfiguration> p_160588_) {
      super(p_160588_);
   }

   public boolean place(FeaturePlaceContext<VegetationPatchConfiguration> p_160612_) {
      WorldGenLevel worldgenlevel = p_160612_.level();
      VegetationPatchConfiguration vegetationpatchconfiguration = p_160612_.config();
      RandomSource randomsource = p_160612_.random();
      BlockPos blockpos = p_160612_.origin();
      Predicate<BlockState> predicate = (p_204782_) -> {
         return p_204782_.is(vegetationpatchconfiguration.replaceable);
      };
      int i = vegetationpatchconfiguration.xzRadius.sample(randomsource) + 1;
      int j = vegetationpatchconfiguration.xzRadius.sample(randomsource) + 1;
      Set<BlockPos> set = this.placeGroundPatch(worldgenlevel, vegetationpatchconfiguration, randomsource, blockpos, predicate, i, j);
      this.distributeVegetation(p_160612_, worldgenlevel, vegetationpatchconfiguration, randomsource, set, i, j);
      return !set.isEmpty();
   }

   protected Set<BlockPos> placeGroundPatch(WorldGenLevel p_225311_, VegetationPatchConfiguration p_225312_, RandomSource p_225313_, BlockPos p_225314_, Predicate<BlockState> p_225315_, int p_225316_, int p_225317_) {
      BlockPos.MutableBlockPos blockpos$mutableblockpos = p_225314_.mutable();
      BlockPos.MutableBlockPos blockpos$mutableblockpos1 = blockpos$mutableblockpos.mutable();
      Direction direction = p_225312_.surface.getDirection();
      Direction direction1 = direction.getOpposite();
      Set<BlockPos> set = new HashSet<>();

      for(int i = -p_225316_; i <= p_225316_; ++i) {
         boolean flag = i == -p_225316_ || i == p_225316_;

         for(int j = -p_225317_; j <= p_225317_; ++j) {
            boolean flag1 = j == -p_225317_ || j == p_225317_;
            boolean flag2 = flag || flag1;
            boolean flag3 = flag && flag1;
            boolean flag4 = flag2 && !flag3;
            if (!flag3 && (!flag4 || p_225312_.extraEdgeColumnChance != 0.0F && !(p_225313_.nextFloat() > p_225312_.extraEdgeColumnChance))) {
               blockpos$mutableblockpos.setWithOffset(p_225314_, i, 0, j);

               for(int k = 0; p_225311_.isStateAtPosition(blockpos$mutableblockpos, BlockBehaviour.BlockStateBase::isAir) && k < p_225312_.verticalRange; ++k) {
                  blockpos$mutableblockpos.move(direction);
               }

               for(int i1 = 0; p_225311_.isStateAtPosition(blockpos$mutableblockpos, (p_284926_) -> {
                  return !p_284926_.isAir();
               }) && i1 < p_225312_.verticalRange; ++i1) {
                  blockpos$mutableblockpos.move(direction1);
               }

               blockpos$mutableblockpos1.setWithOffset(blockpos$mutableblockpos, p_225312_.surface.getDirection());
               BlockState blockstate = p_225311_.getBlockState(blockpos$mutableblockpos1);
               if (p_225311_.isEmptyBlock(blockpos$mutableblockpos) && blockstate.isFaceSturdy(p_225311_, blockpos$mutableblockpos1, p_225312_.surface.getDirection().getOpposite())) {
                  int l = p_225312_.depth.sample(p_225313_) + (p_225312_.extraBottomBlockChance > 0.0F && p_225313_.nextFloat() < p_225312_.extraBottomBlockChance ? 1 : 0);
                  BlockPos blockpos = blockpos$mutableblockpos1.immutable();
                  boolean flag5 = this.placeGround(p_225311_, p_225312_, p_225315_, p_225313_, blockpos$mutableblockpos1, l);
                  if (flag5) {
                     set.add(blockpos);
                  }
               }
            }
         }
      }

      return set;
   }

   protected void distributeVegetation(FeaturePlaceContext<VegetationPatchConfiguration> p_225331_, WorldGenLevel p_225332_, VegetationPatchConfiguration p_225333_, RandomSource p_225334_, Set<BlockPos> p_225335_, int p_225336_, int p_225337_) {
      for(BlockPos blockpos : p_225335_) {
         if (p_225333_.vegetationChance > 0.0F && p_225334_.nextFloat() < p_225333_.vegetationChance) {
            this.placeVegetation(p_225332_, p_225333_, p_225331_.chunkGenerator(), p_225334_, blockpos);
         }
      }

   }

   protected boolean placeVegetation(WorldGenLevel p_225318_, VegetationPatchConfiguration p_225319_, ChunkGenerator p_225320_, RandomSource p_225321_, BlockPos p_225322_) {
      return p_225319_.vegetationFeature.value().place(p_225318_, p_225320_, p_225321_, p_225322_.relative(p_225319_.surface.getDirection().getOpposite()));
   }

   protected boolean placeGround(WorldGenLevel p_225324_, VegetationPatchConfiguration p_225325_, Predicate<BlockState> p_225326_, RandomSource p_225327_, BlockPos.MutableBlockPos p_225328_, int p_225329_) {
      for(int i = 0; i < p_225329_; ++i) {
         BlockState blockstate = p_225325_.groundState.getState(p_225327_, p_225328_);
         BlockState blockstate1 = p_225324_.getBlockState(p_225328_);
         if (!blockstate.is(blockstate1.getBlock())) {
            if (!p_225326_.test(blockstate1)) {
               return i != 0;
            }

            p_225324_.setBlock(p_225328_, blockstate, 2);
            p_225328_.move(p_225325_.surface.getDirection());
         }
      }

      return true;
   }
}