package cz.jzitnik.game.logic.services.saplings;

import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.BlockRegistry;
import cz.jzitnik.game.annotations.CustomLogic;
import cz.jzitnik.game.annotations.Sapling;
import cz.jzitnik.game.entities.Block;
import cz.jzitnik.game.generation.Trees;
import cz.jzitnik.game.logic.CustomLogicInterface;
import cz.jzitnik.tui.ScreenRenderer;

import org.reflections.Reflections;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

@CustomLogic
public class SaplingLogic implements CustomLogicInterface {
    private static final int RADIUS = 30;

    private static final Set<String> saplings = new HashSet<>();

    public SaplingLogic() {
        Reflections reflections = new Reflections("cz.jzitnik.game.entities.items.registry.blocks");
        Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(Sapling.class);

        for (Class<?> clazz : handlerClasses) {
            if (clazz.isAnnotationPresent(BlockRegistry.class)) {
                String value = clazz.getAnnotation(BlockRegistry.class).value();
                saplings.add(value);
            }
        }
    }

    @Override
    public void nextIteration(Game game, ScreenRenderer ignored) {
        int[] data = game.getPlayerCords();
        var world = game.getWorld();
        int playerX = data[0];
        int playerY = data[1];

        int startX = Math.max(0, playerX - RADIUS);
        int startY = Math.max(0, playerY - RADIUS);
        int endX = Math.min(world[0].length - 1, playerX + RADIUS);
        int endY = Math.min(world.length - 1, playerY + RADIUS);

        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                var blocks = world[y][x];
                if (blocks.stream().anyMatch(block -> saplings.contains(block.getBlockId()))) {
                    sapling(game, x, y);
                }
            }
        }
    }

    private boolean check(Block block) {
        return block.isMob() || block.getBlockId().equals("air") || saplings.contains(block.getBlockId());
    }

    private boolean canGrowSapling(List<Block>[][] world, int x, int y) {
        return
                world[y][x].stream().allMatch(this::check) &&
                world[y - 1][x].stream().allMatch(this::check) &&
                world[y - 2][x].stream().allMatch(this::check) &&
                world[y - 3][x].stream().allMatch(this::check) &&
                world[y - 4][x].stream().allMatch(this::check) &&
                world[y - 5][x].stream().allMatch(this::check) &&
                world[y - 3][x - 2].stream().allMatch(this::check) &&
                world[y - 3][x - 1].stream().allMatch(this::check) &&
                world[y - 3][x + 1].stream().allMatch(this::check) &&
                world[y - 3][x + 2].stream().allMatch(this::check) &&
                world[y - 4][x - 1].stream().allMatch(this::check) &&
                world[y - 4][x + 1].stream().allMatch(this::check);
    }

    private void sapling(Game game, int x, int y) {
        var world = game.getWorld();
        if (!canGrowSapling(world, x, y)) {
            return;
        }

        var sapling = world[y][x].stream().filter(i -> saplings.contains(i.getBlockId())).findFirst().get();
        var data = (SaplingData) sapling.getData();

        if (data.getGrowWait() == 1) {
            // Grow
            world[y][x].remove(sapling);
            Trees.tree(world, x, y + 1);

            return;
        }

        data.setGrowWait(data.getGrowWait() - 1);
    }
}
