package cz.jzitnik.game.mobs.services.zombie;

import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.EntitySpawn;
import cz.jzitnik.game.entities.items.ItemBlockSupplier;
import cz.jzitnik.game.mobs.EntitySpawnInterface;
import cz.jzitnik.game.sprites.Zombie;
import cz.jzitnik.tui.ScreenMovingCalculationProvider;
import lombok.extern.slf4j.Slf4j;
import org.jline.terminal.Terminal;

import java.util.*;
@EntitySpawn
public class ZombieSpawnLogic implements EntitySpawnInterface {
    private final Random random = new Random();

    @Override
    public void spawn(int playerX, int playerY, Game game, Terminal terminal) {
        if (!game.isNight()) {
            return;
        }

        int[] view = ScreenMovingCalculationProvider.calculate(playerX, playerY, terminal.getHeight(), terminal.getWidth(), game.getWorld()[0].length, game.getWorld().length);
        int startX = view[0];
        int endX = view[1];

        attemptZombieSpawn(startX - 20, startX - 5, playerY, game);
        attemptZombieSpawn(endX + 5, endX + 20, playerY, game);
    }


    private void attemptZombieSpawn(int startX, int endX, int centerY, Game game) {
        if (countZombies(startX, endX, centerY - 15, centerY + 15, game) < 3 && random.nextInt(100) < 2) {
            var spawnLocations = zombieCanSpawn(startX, endX, centerY, game);
            if (!spawnLocations.isEmpty()) {
                for (int i = 0; i < Math.min(4, spawnLocations.size()); i++) {
                    var loc = getRandomEntry(spawnLocations);
                    int x = loc.getKey();
                    int y = loc.getValue();

                    var top = ItemBlockSupplier.getEntity("zombie");
                    top.setSpriteState(Zombie.ZombieState.TOP);

                    var bottom = ItemBlockSupplier.getEntity("zombie");
                    bottom.setSpriteState(Zombie.ZombieState.BOTTOM);

                    top.setLinkedMobTexture(bottom);
                    bottom.setLinkedMobTexture(top);

                    game.getWorld()[y - 1][x].add(top);
                    game.getWorld()[y][x].add(bottom);
                }
            }
        }
    }


    private HashMap<Integer, Integer> zombieCanSpawn(int startX, int endX, int playerY, Game game) {
        var map = new HashMap<Integer, Integer>();
        var world = game.getWorld();
        for (int x = startX; x <= endX; x++) {
            for (int y = Math.max(1, playerY - 30); y < Math.min(world.length - 2, playerY + 30); y++) {
                if (world[y + 1][x].stream().anyMatch(b -> b.getBlockId().equals("grass"))) {
                    map.put(x, y);
                }
            }
        }
        return map;
    }

    private long countZombies(int startX, int endX, int startY, int endY, Game game) {
        long count = 0;
        for (int y = startY; y <= endY; y++) {
            for (int x = startX; x <= endX; x++) {
                count += game.getWorld()[y][x].stream().filter(i -> i.getBlockId().equals("zombie")).count();
            }
        }
        return count / 2; // Each zombie has two parts
    }

    public static <K, V> Map.Entry<K, V> getRandomEntry(HashMap<K, V> map) {
        List<Map.Entry<K, V>> list = new ArrayList<>(map.entrySet());
        return list.get(new Random().nextInt(list.size()));
    }
}
