package net.halbear.aotg.custom.WorldGen.GenerationFeatures;

import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.halbear.aotg.registries.ModTreeGen;
import net.minecraft.core.BlockPos;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.WorldGenLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.feature.configurations.TreeConfiguration;
import net.minecraft.world.level.levelgen.feature.foliageplacers.FoliagePlacer;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacer;
import net.minecraft.world.level.levelgen.feature.trunkplacers.TrunkPlacerType;
import org.joml.Vector2i;
import org.joml.Vector3f;

import java.util.ArrayList;
import java.util.List;
import java.util.function.BiConsumer;

public class PineTreeTrunkPlacer extends TrunkPlacer {

    public PineTreeTrunkPlacer(int baseHeight, int heightRandA, int heightRandB) {
        super(baseHeight, heightRandA, heightRandB);
    }


    public static final MapCodec<PineTreeTrunkPlacer> CODEC = RecordCodecBuilder.mapCodec(instance ->
            trunkPlacerParts(instance).apply(instance, PineTreeTrunkPlacer::new)
    );

    @Override
    protected TrunkPlacerType<?> type() {
        return ModTreeGen.PINE_TRUNK_PACER.get();
    }

    @Override
    public List<FoliagePlacer.FoliageAttachment> placeTrunk(WorldGenLevel worldGenLevel, BiConsumer<BlockPos, BlockState> biConsumer, RandomSource randomSource, int freeSpace, BlockPos blockPos, TreeConfiguration treeConfiguration) {
        int TRUNK_WIDTH = 3;
        List<FoliagePlacer.FoliageAttachment> leafNodes = new ArrayList<>();
        int mainHeight = this.getTreeHeight(randomSource);
        BlockPos currentPos = blockPos;
        Vector2i Direction = new Vector2i(randomSource.nextIntBetweenInclusive(-1,1), randomSource.nextIntBetweenInclusive(-1,1));

        for (int i = 0; i < mainHeight; i++) {
            TRUNK_WIDTH = (int)Math.max(3 * (1.0-((i*0.75)/(double)mainHeight)),1);
            for (int xOffset = 0; xOffset < TRUNK_WIDTH; xOffset++) {
                for (int zOffset = 0; zOffset < TRUNK_WIDTH; zOffset++) {
                    BlockPos thickLogPos = currentPos.offset(xOffset, 0, zOffset);
                    if(i < 2 && randomSource.nextFloat() < 0.70f){
                        generateRoots(worldGenLevel, biConsumer, randomSource, thickLogPos, treeConfiguration);
                    }
                    this.placeLog(worldGenLevel, biConsumer, randomSource, thickLogPos, treeConfiguration);
                }
            }
            if (randomSource.nextFloat() < 0.3f && i > 2) {
                currentPos = currentPos.offset(Direction.x, 0, Direction.y);
                this.placeLog(worldGenLevel, biConsumer, randomSource, currentPos.below(), treeConfiguration);
            }

            if (i > 5 && randomSource.nextFloat() < 0.75) {
                BlockPos branchStart = currentPos.offset(randomSource.nextInt(TRUNK_WIDTH), 0, randomSource.nextInt(TRUNK_WIDTH));
                BlockPos branchEnd = generateBranch(worldGenLevel, biConsumer, randomSource, branchStart, treeConfiguration,Math.min(mainHeight - i,6), leafNodes,new Vector2i(randomSource.nextIntBetweenInclusive(-1,1), randomSource.nextIntBetweenInclusive(-1,1)));

            }

            currentPos = currentPos.above();
        }

        leafNodes.add(new FoliagePlacer.FoliageAttachment(currentPos, 0, false));

        return leafNodes;
    }

    private BlockPos generateRoots(WorldGenLevel level, BiConsumer<BlockPos, BlockState> blockSetter,
                                    RandomSource random, BlockPos branchStart, TreeConfiguration config) {
        int dirX = random.nextBoolean() ? 1 : -1;
        int dirZ = random.nextBoolean() ? 1 : -1;
        int branchLength = 2 + random.nextInt(6);

        BlockPos bPos = branchStart;
        for (int b = 0; b < branchLength; b++) {
            int stepX = random.nextFloat() < 0.7f ? dirX : 0;
            int stepZ = random.nextFloat() < 0.7f ? dirZ : 0;
            int stepY = random.nextFloat() < 0.4f ? -1 : 0;
            bPos = bPos.offset(stepX, stepY, stepZ);
            this.placeLog(level, blockSetter, random, bPos, config);
        }
        return bPos;
    }

    private BlockPos generateBranch(WorldGenLevel level, BiConsumer<BlockPos, BlockState> blockSetter,
            RandomSource random, BlockPos branchStart, TreeConfiguration config, int BranchLength,List<FoliagePlacer.FoliageAttachment> leafNodes, Vector2i Direction) {
        int dirX = Direction.x;
        int dirZ = Direction.y;
        int branchLength = BranchLength;

        BlockPos bPos = branchStart;
        for (int b = 0; b < branchLength; b++) {
            int stepX = random.nextFloat() < 0.7f ? dirX : 0;
            int stepZ = random.nextFloat() < 0.7f ? dirZ : 0;
            int stepY = random.nextFloat() < 0.4f ? -1 : 0;
            bPos = bPos.offset(stepX, stepY, stepZ);
            this.placeLog(level, blockSetter, random, bPos, config);
            leafNodes.add(new FoliagePlacer.FoliageAttachment(bPos, Math.min(random.nextInt(branchLength - b),2), false));
        }
        return bPos;
    }

    public static int checkPointPosition(double xp, double yp, double xc, double yc, double r) {
        double dx = xp - xc;
        double dy = yp - yc;
        double squaredDistance = (dx * dx) + (dy * dy);

        double squaredRadius = r * r * 1.25;

        if (squaredDistance < squaredRadius) {
            return -1;
        } else if (squaredDistance == squaredRadius) {
            return 0;
        } else {
            return 1;
        }
    }
}
