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

import java.util.function.Function;
import net.minecraft.core.Registry;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.levelgen.feature.Feature;
import net.minecraft.world.level.LevelSimulatedReader;
import net.minecraft.core.Vec3i;
import net.minecraft.world.level.levelgen.feature.TreeFeature;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.LevelWriter;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;
import java.util.List;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import java.util.Set;
import net.minecraft.core.BlockPos;
import java.util.Random;
import net.minecraft.world.level.LevelSimulatedRW;
import com.mojang.datafixers.kinds.App;
import com.mojang.datafixers.Products;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import com.mojang.serialization.Codec;

public abstract class TrunkPlacer {
    public static final Codec<TrunkPlacer> CODEC;
    protected final int baseHeight;
    protected final int heightRandA;
    protected final int heightRandB;
    
    protected static <P extends TrunkPlacer> Products.P3<RecordCodecBuilder.Mu<P>, Integer, Integer, Integer> trunkPlacerParts(final RecordCodecBuilder.Instance<P> instance) {
        return (Products.P3<RecordCodecBuilder.Mu<P>, Integer, Integer, Integer>)instance.group((App)Codec.INT.fieldOf("base_height").forGetter(cqw -> cqw.baseHeight), (App)Codec.INT.fieldOf("height_rand_a").forGetter(cqw -> cqw.heightRandA), (App)Codec.INT.fieldOf("height_rand_b").forGetter(cqw -> cqw.heightRandB));
    }
    
    public TrunkPlacer(final int integer1, final int integer2, final int integer3) {
        this.baseHeight = integer1;
        this.heightRandA = integer2;
        this.heightRandB = integer3;
    }
    
    protected abstract TrunkPlacerType<?> type();
    
    public abstract List<FoliagePlacer.FoliageAttachment> placeTrunk(final LevelSimulatedRW bqf, final Random random, final int integer, final BlockPos fu, final Set<BlockPos> set, final BoundingBox ctd, final TreeConfiguration cou);
    
    public int getTreeHeight(final Random random) {
        return this.baseHeight + random.nextInt(this.heightRandA + 1) + random.nextInt(this.heightRandB + 1);
    }
    
    protected static void setBlock(final LevelWriter bqh, final BlockPos fu, final BlockState cfj, final BoundingBox ctd) {
        TreeFeature.setBlockKnownShape(bqh, fu, cfj);
        ctd.expand(new BoundingBox(fu, fu));
    }
    
    private static boolean isDirt(final LevelSimulatedReader bqg, final BlockPos fu) {
        final Block bvr2;
        return bqg.isStateAtPosition(fu, cfj -> {
            bvr2 = cfj.getBlock();
            return Feature.isDirt(bvr2) && !cfj.is(Blocks.GRASS_BLOCK) && !cfj.is(Blocks.MYCELIUM);
        });
    }
    
    protected static void setDirtAt(final LevelSimulatedRW bqf, final BlockPos fu) {
        if (!isDirt(bqf, fu)) {
            TreeFeature.setBlockKnownShape(bqf, fu, Blocks.DIRT.defaultBlockState());
        }
    }
    
    protected static boolean placeLog(final LevelSimulatedRW bqf, final Random random, final BlockPos fu, final Set<BlockPos> set, final BoundingBox ctd, final TreeConfiguration cou) {
        if (TreeFeature.validTreePos(bqf, fu)) {
            setBlock(bqf, fu, cou.trunkProvider.getState(random, fu), ctd);
            set.add(fu.immutable());
            return true;
        }
        return false;
    }
    
    protected static void placeLogIfFree(final LevelSimulatedRW bqf, final Random random, final BlockPos.MutableBlockPos a, final Set<BlockPos> set, final BoundingBox ctd, final TreeConfiguration cou) {
        if (TreeFeature.isFree(bqf, a)) {
            placeLog(bqf, random, a, set, ctd, cou);
        }
    }
    
    static {
        CODEC = Registry.TRUNK_PLACER_TYPES.dispatch((Function)TrunkPlacer::type, (Function)TrunkPlacerType::codec);
    }
}
