package cz.jzitnik.utils;

import com.googlecode.lanterna.TerminalSize;
import com.googlecode.lanterna.TextColor;
import cz.jzitnik.events.handlers.FullRoomDrawHandler;
import cz.jzitnik.game.GameRoom;
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.ui.pixels.ColoredPixel;
import cz.jzitnik.ui.pixels.Empty;
import cz.jzitnik.ui.pixels.Pixel;

import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class RerenderUtils {
    public static RoomCords getStart(BufferedImage room, TerminalSize terminalSize) {
        int width = room.getWidth();
        int height = room.getHeight();
        int terminalHeight = terminalSize.getRows() * 2;
        int terminalWidth = terminalSize.getColumns();

        int x = (terminalWidth - width) / 2;
        int y = (terminalHeight - height) / 2;

        return new RoomCords(x, y);
    }

    public static void rerenderPart(int startX, int endX, int startY, int endY, int screenStartX, int screenStartY, GameRoom currentRoom, BufferedImage room, Player player, BufferedImage playerTexture, ScreenBuffer screenBuffer, ResourceManager resourceManager) {
        var buffer = screenBuffer.getRenderedBuffer();
        var overrideBuffer = currentRoom.getOverrideBuffer();
        BufferedImage doors = resourceManager.getResource(ResourceManager.Resource.DOORS);
        Set<FullRoomDrawHandler.DoorPosition> doorPositions = RerenderUtils.getDoorPositions(currentRoom);
        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                PixelResult pixelResult = 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 finalPixel = overridePixel.getClass() == Empty.class || pixelResult.isPlayer() ? pixel1 : overridePixel;

                buffer[y + screenStartY][x + screenStartX] = finalPixel;
            }
        }
    }

    public record PixelResult(int pixel, boolean isPlayer) {}

    public static PixelResult getPixel(GameRoom currentRoom, BufferedImage room, BufferedImage doors, Set<FullRoomDrawHandler.DoorPosition> doorPositions, Player player, BufferedImage playerTexture, int x, int y) {
        if (x >= player.getPlayerCords().getX() && x < player.getPlayerCords().getX() + playerTexture.getWidth() - 1 && y >= player.getPlayerCords().getY() && y <= player.getPlayerCords().getY() + playerTexture.getHeight() - 1) {
            int pixel = playerTexture.getRGB(x - player.getPlayerCords().getX(), y - player.getPlayerCords().getY());
            int alpha = (pixel >> 24) & 0xff;

            if (alpha != 0) {
                return new PixelResult(pixel, true);
            }
        }

        for (GameObject object : currentRoom.getObjects()) {
            RoomCords startObjectCords = object.getCords();
            BufferedImage texture = object.getTexture();
            RoomCords endObjectCords = new RoomCords(startObjectCords.getX() + texture.getWidth() - 1, startObjectCords.getY() + texture.getHeight() - 1);

            if (x >= startObjectCords.getX() && x <= endObjectCords.getX() && y >= startObjectCords.getY() && y <= endObjectCords.getY()) {
                int pixel = texture.getRGB(x - startObjectCords.getX(), y - startObjectCords.getY());
                int alpha = (pixel >> 24) & 0xff;
                int r = (pixel >> 16) & 0xff;
                int g = (pixel >> 8) & 0xff;
                int b = pixel & 0xff;
                float factor = 1.5f; // brightness multiplier

                if (alpha != 0) {
                    if (object.isSelected()) {
                        r = Math.min(255, (int)(r * factor));
                        g = Math.min(255, (int)(g * factor));
                        b = Math.min(255, (int)(b * factor));
                        pixel = (alpha << 24) | (r << 16) | (g << 8) | b;
                    }

                    return new PixelResult(pixel, false);
                }
            }
        }

        record DoorBounds(int startX, int startY, int endX, int endY) {
            boolean contains(int x, int y) {
                return x >= startX && x <= endX && y >= startY && y <= endY;
            }
        }

        Map<FullRoomDrawHandler.DoorPosition, DoorBounds> doorBounds = Map.of(
                FullRoomDrawHandler.DoorPosition.TOP, new DoorBounds(96, 13, 132, 47),
                FullRoomDrawHandler.DoorPosition.LEFT, new DoorBounds(0, 84, 37, 135),
                FullRoomDrawHandler.DoorPosition.RIGHT, new DoorBounds(187, 98, 244, 136),
                FullRoomDrawHandler.DoorPosition.BOTTOM, new DoorBounds(84, 190, 140, 225)
        );

        for (FullRoomDrawHandler.DoorPosition pos : doorPositions) {
            DoorBounds bounds = doorBounds.get(pos);
            if (bounds != null && bounds.contains(x, y)) {
                return new PixelResult(doors.getRGB(x, y), false);
            }
        }

        return new PixelResult(room.getRGB(x, y), false);
    }

    public static BufferedImage getPlayer(ResourceManager resourceManager, Player player) {
        return resourceManager.getResource(switch (player.getPlayerRotation()) {
            case FRONT -> ResourceManager.Resource.PLAYER_FRONT;
            case BACK -> ResourceManager.Resource.PLAYER_BACK;
            case LEFT -> ResourceManager.Resource.PLAYER_LEFT;
            case RIGHT -> ResourceManager.Resource.PLAYER_RIGHT;
        });
    }

    public static Set<FullRoomDrawHandler.DoorPosition> getDoorPositions(GameRoom currentRoom) {
        Set<FullRoomDrawHandler.DoorPosition> doorPositions = new HashSet<>();
        if (currentRoom.getLeft() != null) doorPositions.add(FullRoomDrawHandler.DoorPosition.LEFT);
        if (currentRoom.getRight() != null) doorPositions.add(FullRoomDrawHandler.DoorPosition.RIGHT);
        if (currentRoom.getUp() != null) doorPositions.add(FullRoomDrawHandler.DoorPosition.TOP);
        if (currentRoom.getDown() != null) doorPositions.add(FullRoomDrawHandler.DoorPosition.BOTTOM);

        return doorPositions;
    }
}
