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

import eu.mhsl.minenet.minigames.instance.Dimension;
import eu.mhsl.minenet.minigames.instance.game.stateless.StatelessGame;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.enemies.WaveGenerator;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.generator.MazeGenerator;
import eu.mhsl.minenet.minigames.instance.game.stateless.types.towerdefense.towers.*;
import eu.mhsl.minenet.minigames.score.LastWinsScore;
import net.minestom.server.MinecraftServer;
import net.minestom.server.coordinate.Pos;
import net.minestom.server.entity.Player;
import net.minestom.server.instance.batch.AbsoluteBlockBatch;
import net.minestom.server.instance.block.Block;
import net.minestom.server.item.Material;
import net.minestom.server.timer.Task;
import net.minestom.server.timer.TaskSchedule;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;

public class Towerdefense extends StatelessGame {
    private final Random random = new Random();
    private final AbsoluteBlockBatch mazeBatch = new AbsoluteBlockBatch();
    private final List<Pos> mazePath = new ArrayList<>();
    private final List<TowerdefenseRoom> instances = new ArrayList<>();
    private final WaveGenerator waveGenerator;
    private Task waveTask;
    private final Map<Material, Class<? extends Tower>> availableTowers = Map.of(
        Material.SKELETON_SPAWN_EGG, SkeletonTower.class,
        Material.ZOMBIE_SPAWN_EGG, ZombieTower.class,
        Material.BLAZE_SPAWN_EGG, BlazeTower.class,
        Material.ENDERMAN_SPAWN_EGG, EndermanTower.class,
        Material.PIGLIN_BRUTE_SPAWN_EGG, BruteTower.class,
        Material.WITHER_SPAWN_EGG, WitherTower.class,
        Material.ENDER_DRAGON_SPAWN_EGG, EnderDragonTower.class
    );
    private final Map<Class<? extends Tower>, Integer> prices = Map.of(
        ZombieTower.class, 4,
        SkeletonTower.class, 3,
        BlazeTower.class, 8,
        EndermanTower.class, 20,
        BruteTower.class, 27,
        WitherTower.class, 40,
        EnderDragonTower.class, 200
    );
    private static final int pathLength = 10;

    public Towerdefense() {
        super(Dimension.NETHER.key, "Towerdefense", new LastWinsScore());

        this.setGenerator(new MazeGenerator());
        this.generateMaze();

        this.waveGenerator = new WaveGenerator(this);
    }

    @Override
    protected void checkAbandoned() {
        this.scheduleNextTick((instance) -> {
            if(this.instances.stream().allMatch(room -> room.getPlayers().isEmpty()) && this.getPlayers().isEmpty()) {
                this.waveTask.cancel();
                this.unload();
            }
        });
    }

    @Override
    protected void onStart() {
        this.getPlayers().forEach(player -> {
            TowerdefenseRoom newRoom = new TowerdefenseRoom(player, this);
            this.instances.add(newRoom);
            player.setInstance(newRoom, new Pos(0, 1, 0));
        });
        this.waveTask = MinecraftServer.getSchedulerManager().scheduleTask(
            this.waveGenerator::startNextWave,
            TaskSchedule.seconds(3),
            TaskSchedule.seconds(25)
        );
    }

    private void generateMaze() {
        Pos position = new Pos(0, 0, 0);
        this.addMazePosition(position, Block.GREEN_WOOL);
        position = position.add(0,0,2);

        List<Integer> previousDirections = new ArrayList<>();
        int direction = 1;      // 0 -> right; 1 -> straight; 2 -> left
        for (int i = 0; i < pathLength; i++) {
            for (int j = 0; j < 9; j++) {
                position = position.add(direction-1,0,direction%2);
                this.addMazePosition(position, Block.WHITE_WOOL);
            }

            int origin = 0;
            int bound = 3;
            long rightLeftDifference = previousDirections.stream().filter(integer -> integer == 0).count() - previousDirections.stream().filter(integer -> integer == 2).count();
            if(rightLeftDifference >= 2 || direction == 2) origin = 1;
            if(rightLeftDifference <= -2 || direction == 0) bound = 2;
            direction = this.random.nextInt(origin, bound);
            previousDirections.add(direction);
        }
        this.addMazePosition(position, Block.WHITE_WOOL);
        this.addMazePosition(position.add(0,0,3), Block.WHITE_WOOL);
        this.addMazePosition(position.add(0,0,6), Block.RED_WOOL);
    }

    private void addMazePosition(Pos position, Block pathBlock) {
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                this.mazeBatch.setBlock(position.add(i-1,0,j-1), pathBlock);
            }
        }
        this.mazePath.add(position.add(0.5,1,0.5));
    }

    public AbsoluteBlockBatch getMazeBatch() {
        return this.mazeBatch;
    }

    public List<Pos> getMazePath() {
        return this.mazePath;
    }

    public List<TowerdefenseRoom> getInstances() {
        return this.instances;
    }

    @Override
    protected boolean onPlayerJoin(Player p) {
        MinecraftServer.getSchedulerManager().scheduleNextTick(() -> p.teleport(new Pos((long) -this.getPlayers().size(), 1, 0)));
        return super.onPlayerJoin(p);
    }

    public Map<Class<? extends Tower>, Integer> getPrices() {
        return this.prices;
    }

    public Map<Material, Class<? extends Tower>> getAvailableTowers() {
        return this.availableTowers;
    }
}
