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

import com.mojang.datafixers.Products;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.util.RandomSource;
import net.minecraft.util.valueproviders.IntProvider;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.material.Fluids;

public abstract class FoliagePlacer {
   public static final Codec<FoliagePlacer> CODEC = BuiltInRegistries.FOLIAGE_PLACER_TYPE.byNameCodec().dispatch(FoliagePlacer::type, FoliagePlacerType::codec);
   protected final IntProvider radius;
   protected final IntProvider offset;

   protected static <P extends FoliagePlacer> Products.P2<RecordCodecBuilder.Mu<P>, IntProvider, IntProvider> foliagePlacerParts(RecordCodecBuilder.Instance<P> p_68574_) {
      return p_68574_.group(IntProvider.codec(0, 16).fieldOf("radius").forGetter((p_161449_) -> {
         return p_161449_.radius;
      }), IntProvider.codec(0, 16).fieldOf("offset").forGetter((p_161447_) -> {
         return p_161447_.offset;
      }));
   }

   public FoliagePlacer(IntProvider p_161411_, IntProvider p_161412_) {
      this.radius = p_161411_;
      this.offset = p_161412_;
   }

   protected abstract FoliagePlacerType<?> type();

   public void createFoliage(LevelSimulatedReader p_273526_, FoliagePlacer.FoliageSetter p_273018_, RandomSource p_273425_, TreeConfiguration p_273138_, int p_273282_, FoliagePlacer.FoliageAttachment p_272944_, int p_272930_, int p_272727_) {
      this.createFoliage(p_273526_, p_273018_, p_273425_, p_273138_, p_273282_, p_272944_, p_272930_, p_272727_, this.offset(p_273425_));
   }

   protected abstract void createFoliage(LevelSimulatedReader p_225613_, FoliagePlacer.FoliageSetter p_273598_, RandomSource p_225615_, TreeConfiguration p_225616_, int p_225617_, FoliagePlacer.FoliageAttachment p_225618_, int p_225619_, int p_225620_, int p_225621_);

   public abstract int foliageHeight(RandomSource p_225601_, int p_225602_, TreeConfiguration p_225603_);

   public int foliageRadius(RandomSource p_225593_, int p_225594_) {
      return this.radius.sample(p_225593_);
   }

   private int offset(RandomSource p_225592_) {
      return this.offset.sample(p_225592_);
   }

   protected abstract boolean shouldSkipLocation(RandomSource p_225595_, int p_225596_, int p_225597_, int p_225598_, int p_225599_, boolean p_225600_);

   protected boolean shouldSkipLocationSigned(RandomSource p_225639_, int p_225640_, int p_225641_, int p_225642_, int p_225643_, boolean p_225644_) {
      int i;
      int j;
      if (p_225644_) {
         i = Math.min(Math.abs(p_225640_), Math.abs(p_225640_ - 1));
         j = Math.min(Math.abs(p_225642_), Math.abs(p_225642_ - 1));
      } else {
         i = Math.abs(p_225640_);
         j = Math.abs(p_225642_);
      }

      return this.shouldSkipLocation(p_225639_, i, p_225641_, j, p_225643_, p_225644_);
   }

   protected void placeLeavesRow(LevelSimulatedReader p_225629_, FoliagePlacer.FoliageSetter p_272772_, RandomSource p_225631_, TreeConfiguration p_225632_, BlockPos p_225633_, int p_225634_, int p_225635_, boolean p_225636_) {
      int i = p_225636_ ? 1 : 0;
      BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();

      for(int j = -p_225634_; j <= p_225634_ + i; ++j) {
         for(int k = -p_225634_; k <= p_225634_ + i; ++k) {
            if (!this.shouldSkipLocationSigned(p_225631_, j, p_225635_, k, p_225634_, p_225636_)) {
               blockpos$mutableblockpos.setWithOffset(p_225633_, j, p_225635_, k);
               tryPlaceLeaf(p_225629_, p_272772_, p_225631_, p_225632_, blockpos$mutableblockpos);
            }
         }
      }

   }

   protected final void placeLeavesRowWithHangingLeavesBelow(LevelSimulatedReader p_273087_, FoliagePlacer.FoliageSetter p_273225_, RandomSource p_272629_, TreeConfiguration p_272885_, BlockPos p_273412_, int p_272712_, int p_272656_, boolean p_272689_, float p_273464_, float p_273068_) {
      this.placeLeavesRow(p_273087_, p_273225_, p_272629_, p_272885_, p_273412_, p_272712_, p_272656_, p_272689_);
      int i = p_272689_ ? 1 : 0;
      BlockPos blockpos = p_273412_.below();
      BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos();

      for(Direction direction : Direction.Plane.HORIZONTAL) {
         Direction direction1 = direction.getClockWise();
         int j = direction1.getAxisDirection() == Direction.AxisDirection.POSITIVE ? p_272712_ + i : p_272712_;
         blockpos$mutableblockpos.setWithOffset(p_273412_, 0, p_272656_ - 1, 0).move(direction1, j).move(direction, -p_272712_);
         int k = -p_272712_;

         while(k < p_272712_ + i) {
            boolean flag = p_273225_.isSet(blockpos$mutableblockpos.move(Direction.UP));
            blockpos$mutableblockpos.move(Direction.DOWN);
            if (flag && tryPlaceExtension(p_273087_, p_273225_, p_272629_, p_272885_, p_273464_, blockpos, blockpos$mutableblockpos)) {
               blockpos$mutableblockpos.move(Direction.DOWN);
               tryPlaceExtension(p_273087_, p_273225_, p_272629_, p_272885_, p_273068_, blockpos, blockpos$mutableblockpos);
               blockpos$mutableblockpos.move(Direction.UP);
            }

            ++k;
            blockpos$mutableblockpos.move(direction);
         }
      }

   }

   private static boolean tryPlaceExtension(LevelSimulatedReader p_277577_, FoliagePlacer.FoliageSetter p_277449_, RandomSource p_277966_, TreeConfiguration p_277897_, float p_277979_, BlockPos p_277833_, BlockPos.MutableBlockPos p_277567_) {
      if (p_277567_.distManhattan(p_277833_) >= 7) {
         return false;
      } else {
         return p_277966_.nextFloat() > p_277979_ ? false : tryPlaceLeaf(p_277577_, p_277449_, p_277966_, p_277897_, p_277567_);
      }
   }

   protected static boolean tryPlaceLeaf(LevelSimulatedReader p_273596_, FoliagePlacer.FoliageSetter p_273054_, RandomSource p_272977_, TreeConfiguration p_273040_, BlockPos p_273406_) {
      if (!TreeFeature.validTreePos(p_273596_, p_273406_)) {
         return false;
      } else {
         BlockState blockstate = p_273040_.foliageProvider.getState(p_272977_, p_273406_);
         if (blockstate.hasProperty(BlockStateProperties.WATERLOGGED)) {
            blockstate = blockstate.setValue(BlockStateProperties.WATERLOGGED, Boolean.valueOf(p_273596_.isFluidAtPosition(p_273406_, (p_225638_) -> {
               return p_225638_.isSourceOfType(Fluids.WATER);
            })));
         }

         p_273054_.set(p_273406_, blockstate);
         return true;
      }
   }

   public static final class FoliageAttachment {
      private final BlockPos pos;
      private final int radiusOffset;
      private final boolean doubleTrunk;

      public FoliageAttachment(BlockPos p_68585_, int p_68586_, boolean p_68587_) {
         this.pos = p_68585_;
         this.radiusOffset = p_68586_;
         this.doubleTrunk = p_68587_;
      }

      public BlockPos pos() {
         return this.pos;
      }

      public int radiusOffset() {
         return this.radiusOffset;
      }

      public boolean doubleTrunk() {
         return this.doubleTrunk;
      }
   }

   public interface FoliageSetter {
      void set(BlockPos p_273742_, BlockState p_273780_);

      boolean isSet(BlockPos p_273118_);
   }
}