package cz.jzitnik.game.objects;

import com.google.common.collect.Lists;
import com.googlecode.lanterna.TerminalPosition;
import com.googlecode.lanterna.TextColor;
import cz.jzitnik.annotations.injectors.InjectDependency;
import cz.jzitnik.annotations.injectors.InjectState;
import cz.jzitnik.events.MouseAction;
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.items.GameItem;
import cz.jzitnik.game.utils.RoomCords;
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.StateManager;
import cz.jzitnik.utils.UIClickHandlerRepository;
import cz.jzitnik.utils.events.EventManager;
import lombok.extern.slf4j.Slf4j;

import java.awt.image.BufferedImage;
import java.util.HashSet;
import java.util.List;

@Slf4j
public final class Chest extends GameObject implements Interactable, UIClickHandler {
    private static final TextColor BORDER_COLOR = new TextColor.RGB(0, 0, 0);
    private static final TextColor TRANSPARENT_COLOR = new TextColor.RGB(255, 255, 255);

    private final List<GameItem> items;
    private final DependencyManager dependencyManager;

    private int listenerHashCode;

    private int actualDisplayStartX;
    private int actualDisplayStartY;
    private int chestUISizeX;
    private int chestUISizeY;

    @InjectDependency
    private StateManager sm;
    @InjectDependency
    private ResourceManager resourceManager;
    @InjectDependency
    private EventManager eventManager;
    @InjectDependency
    private UIClickHandlerRepository uiClickHandlerRepository;

    @InjectState
    private GameState gameState;
    @InjectState
    private ScreenBuffer screenBuffer;
    @InjectState
    private TerminalState terminalState;

    public Chest(DependencyManager dm, ResourceManager resourceManager, RoomCords cords, GameItem[] items) {
        super(resourceManager.getResource(ResourceManager.Resource.CHEST), cords, true);
        this.items = Lists.newArrayList(items);
        this.dependencyManager = dm;
    }

    @Override
    public void interact(DependencyManager dm) {
        dm.inject(this);
        log.debug("Interacted with chest");
        render(false);
    }

    private void render(boolean clear) {
        GameRoom currentRoom = gameState.getCurrentRoom();
        Player player = gameState.getPlayer();

        BufferedImage roomTexture = resourceManager.getResource(currentRoom.getTexture());
        BufferedImage playerTexture = RerenderUtils.getPlayer(resourceManager, player);
        BufferedImage chestTexture = getTexture();

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

        RoomCords start = RerenderUtils.getStart(
                roomTexture,
                terminalState.getTerminalScreen().getTerminalSize()
        );

        int itemCount = items.size();
        calculateUISize(itemCount);

        int chestUIStartX = getCords().getX();
        int chestUIStartY = getCords().getY();

        int guiStartX = chestUIStartX + (chestTexture.getWidth() / 2) - (chestUISizeX / 2);
        int guiStartY = chestUIStartY - chestUISizeY - 1;

        TerminalPosition guiStart = new TerminalPosition(guiStartX + start.getX(), guiStartY + start.getY());
        TerminalPosition guiEnd = new TerminalPosition(
                (guiStartX + chestUISizeX - 1) + start.getX(),
                (guiStartY + chestUISizeY - 1) + start.getY()
        );

        actualDisplayStartX = guiStartX + start.getX() + 2;
        actualDisplayStartY = guiStartY + start.getY() + 2;

        if (clear) {
            clearPreviousUI(
                    currentRoom,
                    roomTexture,
                    player,
                    playerTexture,
                    buffer,
                    overrideBuffer,
                    start,
                    guiStartY,
                    chestUIStartX,
                    chestTexture.getWidth(),
                    itemCount
            );

            int clearWidth = 4 + (itemCount + 1) * 16 + itemCount * 2;
            int clearStartX = chestUIStartX + (chestTexture.getWidth() / 2) - (clearWidth / 2);

            guiStart = new TerminalPosition(clearStartX + start.getX(), guiStartY + start.getY());
            guiEnd = new TerminalPosition(clearStartX + start.getX() + clearWidth, (guiStartY + chestUISizeY - 1) + start.getY());
        }

        if (!items.isEmpty()) {
            drawUI(buffer, overrideBuffer, start, guiStartX, guiStartY);
        }

        RerenderUtils.rerenderPart(
                guiStart.getColumn() - start.getX(),
                guiEnd.getColumn() - start.getX(),
                guiStart.getRow() - start.getY(),
                guiEnd.getRow() - start.getY(),
                start.getX(),
                start.getY(),
                currentRoom,
                roomTexture,
                player,
                playerTexture,
                screenBuffer,
                resourceManager
        );

        RerenderScreen.ScreenPart sp = new RerenderScreen.ScreenPart(guiStart, guiEnd);

        listenerHashCode = uiClickHandlerRepository.registerCurrentRoomHandler(sp, this);

        //eventManager.emitEvent(RerenderScreen.full(terminalState.getTerminalScreen().getTerminalSize()));

        eventManager.emitEvent(new RerenderScreen(sp));
    }

    private void calculateUISize(int itemCount) {
        chestUISizeY = 20;
        chestUISizeX = 4 + itemCount * 16 + (itemCount - 1) * 2;
    }

    private void clearPreviousUI(
            GameRoom room,
            BufferedImage roomTexture,
            Player player,
            BufferedImage playerTexture,
            Pixel[][] buffer,
            Pixel[][] overrideBuffer,
            RoomCords start,
            int guiStartY,
            int chestUIStartX,
            int chestTextureWidth,
            int itemCount
    ) {
        int clearWidth = 4 + (itemCount + 1) * 16 + itemCount * 2;
        int clearStartX = chestUIStartX + (chestTextureWidth / 2) - (clearWidth / 2);

        for (int y = guiStartY; y < guiStartY + chestUISizeY; y++) {
            for (int x = clearStartX; x < clearStartX + clearWidth; x++) {
                int pixel = RerenderUtils.getPixel(
                        room,
                        roomTexture,
                        null,
                        new HashSet<>(),
                        player,
                        playerTexture,
                        x,
                        y
                ).pixel();

                buffer[y + start.getY()][x + start.getX()] = pixelToColored(pixel);
                overrideBuffer[y][x] = new Empty();
            }
        }
    }

    private void drawUI(
            Pixel[][] buffer,
            Pixel[][] overrideBuffer,
            RoomCords start,
            int guiStartX,
            int guiStartY
    ) {
        for (int y = 0; y < chestUISizeY; y++) {
            for (int x = 0; x < chestUISizeX; x++) {
                Pixel pixel = getPixel(x, y, chestUISizeX, chestUISizeY);
                buffer[guiStartY + y + start.getY()][guiStartX + x + start.getX()] = pixel;
                overrideBuffer[guiStartY + y][guiStartX + x] = pixel;
            }
        }
    }

    private Pixel pixelToColored(int argb) {
        int r = (argb >> 16) & 0xff;
        int g = (argb >> 8) & 0xff;
        int b = argb & 0xff;
        return new ColoredPixel(new TextColor.RGB(r, g, b));
    }

    private Pixel getPixel(int objectX, int objectY, int width, int height) {
        if (isBorder(objectX, objectY, width, height)) {
            return new ColoredPixel(BORDER_COLOR);
        }

        int screenX = objectX - 2;
        int screenY = objectY - 2;

        int part = screenX / 18;
        int rest = screenX % 18;

        if (rest >= 16) {
            return new ColoredPixel(BORDER_COLOR);
        }

        GameItem item = items.get(part);
        BufferedImage texture = item.getTexture();

        int pixel = texture.getRGB(rest, screenY);
        int alpha = (pixel >> 24) & 0xff;

        if (alpha == 0) {
            return new ColoredPixel(TRANSPARENT_COLOR);
        }

        return pixelToColored(pixel);
    }

    private boolean isBorder(int x, int y, int width, int height) {
        return x <= 1 || x >= width - 2 || y <= 1 || y >= height - 2;
    }

    @Override
    public void handleClick(MouseAction mouseAction) {
        int mouseX = mouseAction.getPosition().getColumn();
        int mouseY = mouseAction.getPosition().getRow();

        log.debug("Mouse: x: {}, y: {}", mouseX, mouseY);

        int screenX = mouseX - actualDisplayStartX;
        int screenY = (mouseY * 2) - actualDisplayStartY;

        log.debug("Screen: x: {}, y: {}", screenX, screenY);

        if (screenX < 0 || screenY < 0 || screenX > chestUISizeX || screenY > chestUISizeY) {
            return;
        }

        int part = screenX / 18;
        if (part >= items.size()) {
            return;
        }

        int rest = screenX % 18;
        if (rest >= 16) {
            return;
        }

        items.remove(part);

        if (items.isEmpty()) {
            uiClickHandlerRepository.removeHandlerForCurrentRoom(listenerHashCode, this);
        }
        render(true);
    }
}
