import numpy as np
from typing import Tuple, List
import pieces
import colorama


def generate_orientations(piece: np.ndarray) -> List[np.ndarray]:
    """
    Generate all unique orientations of a piece.
    """
    orientations = []

    # Rotations
    current = piece.copy()
    for _ in range(4):
        current = np.rot90(current)
        if not any(np.array_equal(current, o) for o in orientations):
            orientations.append(current.copy())

    # Flip + rotations
    flipped = np.flip(piece, axis=0)
    for _ in range(4):
        flipped = np.rot90(flipped)
        if not any(np.array_equal(flipped, o) for o in orientations):
            orientations.append(flipped.copy())

    return orientations


class Board:
    def __init__(self, size: Tuple[int, int] = (5, 5)):
        self.rows, self.cols = size
        self.grid = np.zeros((self.rows, self.cols), dtype=int)

    def in_bounds(self, r: int, c: int) -> bool:
        return 0 <= r < self.rows and 0 <= c < self.cols

    def placeable(self, shape: np.ndarray, top_left: Tuple[int,int]) -> bool:
        """
        check if valid
        """
        (r_offset, c_offset) = top_left
        shape_rows, shape_cols = shape.shape

        for i in range(shape_rows):
            for j in range(shape_cols):
                if shape[i, j] == 1:
                    r = r_offset + i
                    c = c_offset + j
                    if not self.in_bounds(r, c) or self.grid[r, c] != 0:
                        return False
        return True

    def place(self, shape: np.ndarray, top_left: Tuple[int,int], piece_id: int) -> None:
        """ 
        place the piece
        """
        (r_offset, c_offset) = top_left
        shape_rows, shape_cols = shape.shape
        for i in range(shape_rows):
            for j in range(shape_cols):
                if shape[i, j] == 1:
                    self.grid[r_offset + i, c_offset + j] = piece_id

    def remove(self, shape: np.ndarray, top_left: Tuple[int,int]) -> None:
        (r_offset, c_offset) = top_left
        shape_rows, shape_cols = shape.shape
        for i in range(shape_rows):
            for j in range(shape_cols):
                if shape[i, j] == 1:
                    self.grid[r_offset + i, c_offset + j] = 0

    def is_full(self) -> bool:
        return np.all(self.grid != 0)

    def __str__(self):
        return str(self.grid)
