package cz.jzitnik.events.handlers;

import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor;
import com.googlecode.lanterna.screen.TerminalScreen;
import cz.jzitnik.annotations.EventHandler;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.FullRoomDraw;
import cz.jzitnik.events.RerenderScreen;
import cz.jzitnik.events.TerminalTooSmallEvent;
import cz.jzitnik.game.GameRoom;
import cz.jzitnik.game.GameState;
import cz.jzitnik.game.Player;
import cz.jzitnik.game.ResourceManager;
import cz.jzitnik.states.RenderState;
import cz.jzitnik.states.ScreenBuffer;
import cz.jzitnik.states.TerminalState;
import cz.jzitnik.ui.pixels.ColoredPixel;
import cz.jzitnik.ui.pixels.Empty;
import cz.jzitnik.ui.pixels.Pixel;
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.*;

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

    @InjectState
    private GameState gameState;

    @InjectState
    private ScreenBuffer screenBuffer;

    @InjectDependency
    private ResourceManager resourceManager;

    @InjectState
    private TerminalState terminalState;

    @InjectDependency
    private EventManager eventManager;

    @InjectState
    private RenderState renderState;

    public enum DoorPosition {
        TOP,
        LEFT,
        RIGHT,
        BOTTOM,
    }

    @Override
    public void handle(FullRoomDraw event) {
        try {
            log.debug("Rendering full room");
            TerminalScreen terminalScreen = terminalState.getTerminalScreen();
            List<RerenderScreen.ScreenPart> partsToRerender = new ArrayList<>();
            GameRoom currentRoom = gameState.getCurrentRoom();

            Set<DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom);

            var buffer = screenBuffer.getRenderedBuffer();
            var overrideBuffer = currentRoom.getOverrideBuffer();

            BufferedImage room = resourceManager.getResource(currentRoom.getTexture());
            BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS);
            Player player = gameState.getPlayer();
            BufferedImage playerTexture = RerenderUtils.getPlayer(resourceManager, player);
            TerminalSize terminalSize = terminalScreen.getTerminalSize();

            int width = room.getWidth();
            int height = room.getHeight();

            var start = RerenderUtils.getStart(room, terminalSize);
            int startX = start.getX();
            int startY = start.getY();

            for (int y = 0; y < height; y++) {
                for (int x = 0; x < width; x++) {
                    RerenderUtils.PixelResult pixelResult = RerenderUtils.getPixel(currentRoom, room, doors, doorPositions, player, playerTexture, x, y);
                    int pixel = pixelResult.pixel();
                    int red = (pixel >> 16) & 0xff;
                    int green = (pixel >> 8) & 0xff;
                    int blue = pixel & 0xff;

                    Pixel overridePixel = overrideBuffer[y][x];
                    Pixel pixel1 = new ColoredPixel(new TextColor.RGB(red, green, blue));

                    Pixel finalPixelLeft = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel;

                    buffer[y + startY][x + startX] = finalPixelLeft;
                }
            }

            partsToRerender.add(new RerenderScreen.ScreenPart(
                    new TerminalPosition(startX, startY),
                    new TerminalPosition(startX + width, startY + height - 1)
            ));

            if (renderState.isFirstRender() || event.isFullRerender()) {
                eventManager.emitEvent(RerenderScreen.full(terminalSize));
                renderState.setFirstRender(false);
            } else {
                eventManager.emitEvent(new RerenderScreen(partsToRerender.toArray(RerenderScreen.ScreenPart[]::new)));
            }
        } catch (ArrayIndexOutOfBoundsException e) {
            // Screen too small to fit the room
            eventManager.emitEvent(new TerminalTooSmallEvent());
        }
    }
}
