package cz.jzitnik.game.mobs;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Set;

import cz.jzitnik.game.Game;
import cz.jzitnik.game.annotations.EntityLogic;
import cz.jzitnik.game.entities.Block;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import org.reflections.Reflections;

@Slf4j
public class EntityLogicProvider {
    private static final int CHUNK_SIZE = 20;
    public final HashMap<String, EntityLogicInterface> logicList = new HashMap<>();

    @AllArgsConstructor
    @Getter
    public static class EntityLogicMobDTO {
        private Game game;
        private Block mob;
        private int x;
        private int y;
    }

    public void update(Game game) {
        log.debug("Updating all entities");
        int[] playerLocation = game.getPlayerCords();
        int playerX = playerLocation[0];
        int playerY = playerLocation[1];

        int startX = Math.max(0, playerX - CHUNK_SIZE);
        int startY = Math.max(0, playerY - CHUNK_SIZE);
        int endX = Math.min(game.getWorld()[0].length, playerX + CHUNK_SIZE);
        int endY = Math.min(game.getWorld().length, playerY + CHUNK_SIZE);

        List<EntityLogicMobDTO> mobs = new ArrayList<>();

        for (int y = startY; y <= endY; y++) {
            for (int x = startX; x <= endX; x++) {
                int finalX = x;
                int finalY = y;
                List<EntityLogicMobDTO> blockMobs = game.getWorld()[y][x].stream().filter(Block::isMob)
                        .map(mob -> new EntityLogicMobDTO(game, mob, finalX, finalY)).toList();
                mobs.addAll(blockMobs);
            }
        }

        for (EntityLogicMobDTO entityLogicMobDTO : mobs) {
            run(entityLogicMobDTO);
        }
    }

    private void run(EntityLogicMobDTO entityLogicMobDTO) {
        if (!logicList.containsKey(entityLogicMobDTO.getMob().getBlockId())) {
            return;
        }
        var logic = logicList.get(entityLogicMobDTO.getMob().getBlockId());

        log.debug("Updating entity {} using logic {} on coordinates x:{},y:{}", entityLogicMobDTO.getMob().getBlockId(),
                logic.getClass().getSimpleName(), entityLogicMobDTO.getX(), entityLogicMobDTO.getY());
        logic.nextIteration(entityLogicMobDTO);
    }

    public EntityLogicProvider() {
        log.info("Loading entity logic");
        registerHandlers();
    }

    private void registerHandlers() {
        Reflections reflections = new Reflections("cz.jzitnik.game.mobs.services");
        Set<Class<?>> handlerClasses = reflections.getTypesAnnotatedWith(EntityLogic.class);

        for (Class<?> clazz : handlerClasses) {
            if (EntityLogicInterface.class.isAssignableFrom(clazz)) {
                try {
                    EntityLogicInterface instance = (EntityLogicInterface) clazz.getDeclaredConstructor().newInstance();
                    EntityLogic annotation = clazz.getAnnotation(EntityLogic.class);
                    logicList.put(annotation.value(), instance);
                    log.info("Loaded logic for entity {}", annotation.value());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
