import numpy as np
from tcod import console, tcod

from components import Coordinates, Appearance
from engine import colors
from engine.component_manager import ComponentManager
from gui.gui_element import GuiElement


class PlayWindow(GuiElement):
    def __init__(
            self,
            x: int, y: int,
            width: int, height: int,
            component_manager: ComponentManager,
            visibility_map: np.array,
            memory_map: np.array
    ):
        """
        :param x: horizontal starting position of the PlayWindow
        :param y: vertical starting position of the PlayWindow
        :param width: horizontal expanse of the PlayWindow in tiles
        :param height: vertical expanse of the PlayWindow in tiles
        :param component_manager: a reference to the game's ComponentManager
        :param visibility_map: a boolean array indicating whether a tile is visible or not
        :param memory_map: a boolean array indicating whether a tile has ever been visible or not
        """
        super().__init__(x, y)
        self.cm = component_manager
        self.memory_map = memory_map
        self.visibility_map = visibility_map
        self.width = width
        self.height = height

        # store a console with the static terrain on it so that we don't have to regenerate it each frame
        self.memory_console = tcod.console.Console(width, height, order='F')
        self.terrain_console = tcod.console.Console(width, height, order='F')
        self.black = tcod.console.Console(width, height, order='F')
        self.console = tcod.console.Console(width, height, order='F')  # buffer console

    def on_load(self) -> None:
        coordinates = self.cm.get(Coordinates)
        coordinates = [c for c in coordinates if c.terrain]
        coordinates = sorted(coordinates, key=lambda c: c.priority)
        for coord in coordinates:
            appearance = self.cm.get_one(Appearance, entity=coord.entity)
            self.terrain_console.tiles[coord.x, coord.y] = appearance.to_tile()
            self.memory_console.tiles[coord.x, coord.y] = (
                ord(appearance.symbol),
                (*colors.darken(appearance.color, factor=.3), 255),
                (*colors.darken(appearance.bg_color, factor=.3), 255)
            )

    def render(self, panel: console.Console) -> None:
        self.console.clear()
        self.terrain_console.blit(self.console)
        coordinates = self.cm.get(Coordinates)
        coordinates = [c for c in coordinates if not c.terrain]
        coordinates = sorted(coordinates, key=lambda c: c.priority)
        for coord in coordinates:
            appearance = self.cm.get_one(Appearance, entity=coord.entity)
            if appearance:
                self.console.tiles[coord.x, coord.y] = appearance.to_tile()

        buffer = np.where(
            self.visibility_map,
            self.console.tiles,
            np.where(
                self.memory_map,
                self.memory_console.tiles,
                self.black.tiles
            )
        )
        self.console = tcod.console.Console(self.width, self.height, order='F', buffer=buffer)
        self.console.blit(panel, dest_x=self.x, dest_y=self.y, width=self.width, height=self.height)
