package eu.mhsl.minenet.minigames.instance.game.stateless.types.acidRain;

import de.articdive.jnoise.JNoise;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LastWinsScore;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import eu.mhsl.minenet.minigames.util.NumberUtil;
import eu.mhsl.minenet.minigames.util.WeatherUtils;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import eu.mhsl.minenet.minigames.world.generator.terrain.CircularPlateTerrainGenerator;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.GameMode;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.event.player.PlayerTickEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import net.minestom.server.network.packet.server.play.ParticlePacket;
import net.minestom.server.particle.Particle;
import net.minestom.server.timer.ExecutionType;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;

import java.util.concurrent.CompletableFuture;


public class AcidRain extends StatelessGame {
    final private int radius = 20;
    private int generationOffset = 0;

    final private int roofHeight = 55;
    private int difficulty = 0;
    final JNoise noise = JNoise.newBuilder()
            .fastSimplex()
            .setSeed(rnd.nextLong())
            .setFrequency(0.09)
            .build();
    public AcidRain() {
        super(Dimension.OVERWORLD.key, "acidRain", new LastWinsScore());
        setGenerator(
                new CircularPlateTerrainGenerator(radius)
                        .setPlateHeight(50)
                        .setPlatePallet(BlockPallet.STONE)
        );


        eventNode().addListener(PlayerTickEvent.class, this::onPlayerTick);
    }

    @Override
    protected void onLoad(@NotNull CompletableFuture<Void> callback) {
        this.generatePlatform(0);
        callback.complete(null);
    }

    @Override
    protected void onStart() {
        new WeatherUtils().startRain(this.getPlayers());

        MinecraftServer.getSchedulerManager().submitTask(() -> {

            getPlayers().forEach(player -> {
                player.sendPacket(new ParticlePacket(Particle.SNEEZE, 0, 60, 0, radius, radius, radius, 1f, 500));
                player.sendPacket(new ParticlePacket(Particle.ITEM_SLIME, 0, 60, 0, radius, radius, radius, 1f, 500));
            });

            if(!isRunning) return TaskSchedule.stop();
            return TaskSchedule.millis(100);
        }, ExecutionType.TICK_END);

        MinecraftServer.getSchedulerManager().submitTask(() -> {
            generationOffset++;
            generatePlatform(generationOffset);

            if(!isRunning) return TaskSchedule.stop();
            return TaskSchedule.millis((long) NumberUtil.map(50 - difficulty, 0, 50, 100, 1000));
        }, ExecutionType.TICK_END);

        MinecraftServer.getSchedulerManager().submitTask(() -> {
            difficulty++;

            if(difficulty >= 50) return TaskSchedule.stop();
            return TaskSchedule.seconds(3);
        });
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        if(playerMoveEvent.getNewPosition().y() < 50) playerDeath(playerMoveEvent.getPlayer());
    }

    private void onPlayerTick(PlayerTickEvent playerTickEvent) {
        if(isBeforeBeginning) return;
        if(getBlock(playerTickEvent.getPlayer().getPosition().withY(roofHeight)).isAir()) playerDeath(playerTickEvent.getPlayer());
    }

    private void generatePlatform(long offset) {
        int radius = this.radius + 20;

        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();
        for(int x = -radius; x <= radius; x++) {
            for(int z = -radius; z <= radius; z++) {
                batch.setBlock(x, roofHeight, z, Block.AIR);
                if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) continue;
                if(noise.getNoise(x + offset, z + offset) > 0.4) continue;

                batch.setBlock(x, roofHeight, z, Block.OAK_PLANKS);
            }
        }
        BatchUtil.loadAndApplyBatch(batch, this, () -> {});
    }

    private void playerDeath(Player p) {
        p.setGameMode(GameMode.SPECTATOR);
        getScore().insertResult(p);
    }

    @Override
    public Pos getSpawn() {
        return new Pos(0, 51, 0);
    }
}
