package cz.jzitnik.events.handlers;

import com.googlecode.lanterna.TerminalPosition;
import cz.jzitnik.annotations.EventHandler;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.MouseAction;
import cz.jzitnik.events.MouseMoveEvent;
import cz.jzitnik.events.RerenderScreen;
import cz.jzitnik.game.GameRoom;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.Player;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.game.objects.GameObject;
import cz.jzitnik.game.utils.RoomCords;
import cz.jzitnik.states.ScreenBuffer;
import cz.jzitnik.states.TerminalState;
import cz.jzitnik.utils.DependencyManager;
import cz.jzitnik.utils.RerenderUtils;
import cz.jzitnik.utils.events.AbstractEventHandler;
import cz.jzitnik.utils.events.EventManager;
import lombok.extern.slf4j.Slf4j;

import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j
@EventHandler(MouseMoveEvent.class)
public class MouseMoveEventHandler extends AbstractEventHandler<MouseMoveEvent> {
    public MouseMoveEventHandler(DependencyManager dm) {
        super(dm);
    }

    @InjectState
    private GameState gameState;

    @InjectState
    private ScreenBuffer screenBuffer;

    @InjectState
    private TerminalState terminalState;

    @InjectDependency
    private ResourceManager resourceManager;

    @InjectDependency
    private EventManager eventManager;

    @Override
    public void handle(MouseMoveEvent event) {
        MouseAction mouseAction = event.getMouseAction();
        int mouseX = mouseAction.getPosition().getColumn();
        int mouseY = mouseAction.getPosition().getRow();

        GameRoom currentRoom = gameState.getCurrentRoom();
        BufferedImage room = resourceManager.getResource(currentRoom.getTexture());
        var start = RerenderUtils.getStart(room, terminalState.getTerminalScreen().getTerminalSize());
        int startX = start.getX();
        int startY = start.getY();

        Set<GameObject> selectedObjects = currentRoom.getObjects().stream().filter(gameObject -> {
            if (!gameObject.isSelectable()) return false;
            BufferedImage texture = gameObject.getTexture();
            RoomCords cords = gameObject.getCords();
            return
                    mouseX - startX >= cords.getX() &&
                    mouseX - startX < cords.getX() + texture.getWidth() &&
                    (mouseY * 2 - startY) >= cords.getY() &&
                    (mouseY * 2 - startY)  < cords.getY() + texture.getHeight();
        }).collect(Collectors.toSet());

        Set<GameObject> changedObjects = new HashSet<>();

        for (GameObject object : currentRoom.getObjects()) {
            boolean newValue = selectedObjects.contains(object);
            boolean changed = object.isSelected() != newValue;

            if (changed) {
                object.setSelected(newValue);
                changedObjects.add(object);
            }
        }

        List<RerenderScreen.ScreenPart> parts = new ArrayList<>();

        for (GameObject object : changedObjects) {
            RoomCords cords = object.getCords();
            BufferedImage objectTexture = object.getTexture();
            Player player = gameState.getPlayer();
            BufferedImage playerTexture = RerenderUtils.getPlayer(resourceManager, player);

            int forStartX = cords.getX();
            int forStartY = cords.getY();
            int forEndX = cords.getX() + objectTexture.getWidth() - 1;
            int forEndY = cords.getY() + objectTexture.getHeight();

            RerenderUtils.rerenderPart(
                    forStartX,
                    forEndX,
                    forStartY,
                    forEndY,
                    startX,
                    startY,
                    currentRoom,
                    room,
                    player,
                    playerTexture,
                    screenBuffer,
                    resourceManager
            );

            parts.add(new RerenderScreen.ScreenPart(
                    new TerminalPosition(forStartX, forStartY),
                    new TerminalPosition(forEndX * 2 + 1 + startX, forEndY + startY)
            ));
        }

        if (!parts.isEmpty()) {
            eventManager.emitEvent(new RerenderScreen(parts.toArray(RerenderScreen.ScreenPart[]::new)));
        }
    }
}
