package eu.mhsl.minenet.minigames.world.generator.terrain;

import de.articdive.jnoise.JNoise;
import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.PlateTerrainGenerator;
import eu.mhsl.minenet.minigames.world.generator.structures.generatable.PeakRock;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.generator.GenerationUnit;
import org.jetbrains.annotations.NotNull;

import java.util.Random;

public class CircularPlateTerrainGenerator extends PlateTerrainGenerator {
    protected final Random rnd = new Random();
    private final int size;

    public CircularPlateTerrainGenerator(int size) {
        this.size = size;
    }

    private final JNoise base = JNoise.newBuilder()
            .fastSimplex()
            .setSeed(rnd.nextLong())
            .setFrequency(0.01)
            .build();

    private final JNoise batches = JNoise.newBuilder()
            .fastSimplex()
            .setSeed(rnd.nextLong())
            .setFrequency(0.05)
            .build();

    private final JNoise peaks = JNoise.newBuilder()
            .fastSimplex()
            .setSeed(rnd.nextLong())
            .setFrequency(0.1)
            .build();


    @Override
    public void generate(@NotNull GenerationUnit unit) {
        Point start = unit.absoluteStart().withY(0);

        if(unit.absoluteStart().distance(new Pos(0, 0, 0)) > 500 + this.size) return;

        for (int x = 0; x < unit.size().x(); x++) {
            for (int z = 0; z < unit.size().z(); z++) {
                Point bottom = start.add(x, 0, z);

                double distance = bottom.distance(new Pos(0, 0, 0));

                if(distance <= this.size && generatePlate()) {
                    unit.modifier().fill(bottom, bottom.add(1, plateHeight, 1), platePallet.rnd());
                    continue;
                }

                unit.modifier().setBlock(bottom, Block.GRASS_BLOCK);

                synchronized (base) {
                    double baseNoise = base.getNoise(bottom.x(), bottom.z());
                    double currentHeight = minTwo(NumberUtil.map(distance, 0, 400, -((double) this.size / 5), 200)) + baseNoise * 8;

                    synchronized (batches) {
                        double elementNoise = batches.getNoise(bottom.x(), bottom.z());

                        unit.modifier().fill(
                                bottom,
                                bottom.add(1, 1, 1)
                                        .withY(currentHeight),
                                elementNoise < 0.9 ? elementNoise > 0 ? Block.GRASS_BLOCK : Block.SOUL_SAND : Block.STONE
                        );
                    }

                    synchronized (peaks) {
                        double peakNoise = peaks.getNoise(bottom.x(), bottom.z());

                        if(peakNoise > 0.97 && bottom.distance(new Pos(0, 0, 0)) > (this.size + 20)) {

                            Point center = bottom.add(1, currentHeight-3, 1);
                            unit.fork(setter -> new PeakRock(center, BlockPallet.STONE).generateStructure(setter));
                        }
                    }


                }
            }
        }

    }

    private static double minTwo(double input) {
        if(input < 2) return 2;
        return input;
    }
}
