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

import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.PointsWinScore;
import eu.mhsl.minenet.minigames.util.BatchUtil;
import eu.mhsl.minenet.minigames.world.BlockPallet;
import net.kyori.adventure.sound.Sound;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.event.player.PlayerMoveEvent;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import net.minestom.server.sound.SoundEvent;
import org.jetbrains.annotations.NotNull;

import java.util.WeakHashMap;
import java.util.concurrent.CompletableFuture;

public class JumpDive extends StatelessGame {
    private final int radius;
    private final int height;
    private final int timeLimit;

    private final WeakHashMap<Player, Integer> scores = new WeakHashMap<>();

    public JumpDive(int radius, int height, int timeLimit) {
        super(Dimension.OVERWORLD.key, "jumpDive", new PointsWinScore());
        this.radius = radius;
        this.height = height;
        this.timeLimit = timeLimit;
    }

    @Override
    protected void onLoad(@NotNull CompletableFuture<Void> callback) {
        AbsoluteBlockBatch batch = new AbsoluteBlockBatch();

        for(int x = -radius*2; x <= radius*2; x++) {
            for(int z = -radius*2; z <= radius*2; z++) {
                if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) {
                    batch.setBlock(x, height, z, BlockPallet.STONE.rnd());
                } else {
                    batch.setBlock(x, 0, z, BlockPallet.GROUND.rnd());
                    batch.setBlock(x, 1, z, Block.WATER);
                }
            }
        }

        BatchUtil.loadAndApplyBatch(batch, this, () -> callback.complete(null));
    }

    @Override
    protected void onStart() {
        setTimeLimit(timeLimit);
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        Player p = playerMoveEvent.getPlayer();

        if(
            p.isOnGround() && playerMoveEvent.getNewPosition().y() < height
            || playerMoveEvent.getNewPosition().y() < 0
            || isBeforeBeginning && playerMoveEvent.getNewPosition().y() < height
        ) {
            p.teleport(getSpawn());
            playerMoveEvent.setCancelled(true);
        }

        if(
            playerMoveEvent.getNewPosition().y() <= 1
            && playerMoveEvent.getNewPosition().distance(0, 1, 0) < radius + 0.5
            && !(!isBeforeBeginning && !isRunning)
        ) {
            setBlock(playerMoveEvent.getNewPosition().withY(1), Block.REDSTONE_BLOCK);
            scores.merge(p, 1, Integer::sum);
            p.teleport(getSpawn());
            playerMoveEvent.setCancelled(true);
            p.playSound(Sound.sound(SoundEvent.ENTITY_EXPERIENCE_ORB_PICKUP, Sound.Source.PLAYER, 2f, 2f));
        }
    }

    @Override
    protected void onStop() {
        getPlayers().forEach(player -> getScore().insertResult(player, scores.getOrDefault(player, 0)));
    }

    @Override
    public Pos getSpawn() {
        double theta = rnd.nextDouble() * 2 * Math.PI;

        double spawnRadius = radius + 2;
        double x = spawnRadius * Math.cos(theta);
        double z = spawnRadius * Math.sin(theta);

        return new Pos(x, height + 2, z).withLookAt(new Pos(0, height, 0));

    }
}
