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

import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.score.LastWinsScore;
import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.util.BatchUtil;
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.Entity;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.metadata.other.FallingBlockMeta;
import net.minestom.server.event.entity.EntityTickEvent;
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.timer.Scheduler;
import net.minestom.server.timer.TaskSchedule;
import org.jetbrains.annotations.NotNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CompletableFuture;

class AnvilRun extends StatelessGame {

    final int spawnHeight = 30;
    final int radius;
    int anvilsPerSecond;
    final int seconds;
    final int anvilHeight = 200;
    final List<Pos> anvilSpawnPositions = new ArrayList<>();

    public AnvilRun(int radius, int seconds) {
        super(Dimension.OVERWORLD.key, "Anvil Run", new LastWinsScore());
        this.radius = radius;
        this.seconds = seconds;
        this.setGenerator(new CircularPlateTerrainGenerator(radius));

        eventNode().addListener(EntityTickEvent.class, this::onEntityTick);
    }

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

        for(int x = -radius; x <= radius; x++) {
            for (int z = -radius; z <= radius; z++) {
                if(new Pos(x, 0, z).distance(new Pos(0, 0, 0)) > radius) continue;

                anvilSpawnPositions.add(new Pos(x+0.5, anvilHeight, z+0.5));

                batch.setBlock(x, spawnHeight-1, z, Block.SNOW_BLOCK);
            }
        }

        this.anvilsPerSecond = anvilSpawnPositions.size() / this.seconds;
        Collections.shuffle(anvilSpawnPositions);

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

    protected void spawnAnvil(Pos spawnPosition) {
        Entity anvil = new Entity(EntityType.FALLING_BLOCK);
        ((FallingBlockMeta) anvil.getEntityMeta()).setBlock(Block.ANVIL);
        anvil.setInstance(this, spawnPosition);
    }

    @Override
    protected void onStart() {
        super.onStart();

        Scheduler scheduler = MinecraftServer.getSchedulerManager();
        for(int i=0; i<this.anvilSpawnPositions.size(); i++) {
            final int j = i;
            scheduler.scheduleTask(
                () -> spawnAnvil(this.anvilSpawnPositions.get(j)),
                TaskSchedule.millis(
                    (long) Math.floor((double) i/this.anvilsPerSecond * 1000)
                ),
                TaskSchedule.stop()
            );
        }
    }

    @Override
    protected void onPlayerMove(@NotNull PlayerMoveEvent playerMoveEvent) {
        super.onPlayerMove(playerMoveEvent);
        if(isBeforeBeginning && playerMoveEvent.getNewPosition().y() < spawnHeight - 2) {
            playerMoveEvent.setCancelled(true);
            playerMoveEvent.getPlayer().teleport(getSpawn());
            return;
        }
        if(playerMoveEvent.getNewPosition().y() < spawnHeight - 2) getScore().insertResult(playerMoveEvent.getPlayer());
    }

    protected void onEntityTick(@NotNull EntityTickEvent entityTickEvent) {
        if(!entityTickEvent.getEntity().getEntityType().equals(EntityType.FALLING_BLOCK)) return;
        Pos anvilPosition = entityTickEvent.getEntity().getPosition();
        if(anvilPosition.y() > spawnHeight + 0.5) return;
        if(anvilPosition.y() < spawnHeight - 3) entityTickEvent.getEntity().remove();
        if(this.getBlock(anvilPosition.withY(spawnHeight-1)).isAir()) return;
        this.setBlock(anvilPosition.withY(spawnHeight-1), Block.AIR);
    }

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