from __future__ import annotations

from enum import Enum


class Cell(Enum):
    EMPTY = 0
    FILLED = 1
    UNKNOWN = 2

    def __or__(self, value: Cell) -> Cell:
        if self != Cell.UNKNOWN:
            return self
        return value


class Matrix:
    def __init__(self, width: int, height: int) -> None:
        self.width = width
        self.height = height
        self.cells = [[Cell.UNKNOWN for _ in range(width)] for _ in range(height)]

    def __str__(self) -> str:
        return "\n".join(
            "".join(
                "x" if cell == Cell.FILLED else "." if cell == Cell.EMPTY else "?" for cell in row
            )
            for row in self.cells
        )

    def transpose(self) -> Matrix:
        """Return a new matrix mirrored along the main diagonal (rows become columns)."""
        result = Matrix(self.height, self.width)
        for y in range(self.height):
            for x in range(self.width):
                result.cells[x][y] = self.cells[y][x]
        return result


def load_matrix_from_string(s: str) -> Matrix:
    lines = s.splitlines()
    datalines = [line for line in lines if not line.startswith("#")]
    transpose = any(line.startswith("# transpose=true") for line in lines)

    height = len(datalines)
    if height == 0:
        return Matrix(0, 0)
    width = max(len(line.strip("\n")) for line in datalines)
    if width == 0:
        return Matrix(0, height)

    matrix = Matrix(width, height)
    for y, line in enumerate(datalines):
        for x, char in enumerate(line.strip("\n")):
            if char.lower() in ["#", "x", "1"]:
                matrix.cells[y][x] = Cell.FILLED
            elif char.lower() in [".", " ", "0"]:
                matrix.cells[y][x] = Cell.EMPTY
            elif char.lower() in ["?"]:
                matrix.cells[y][x] = Cell.UNKNOWN
            else:
                raise ValueError(f"Invalid character '{char}' at ({x}, {y})")
        else:
            for x in range(len(line.strip("\n")), width):
                matrix.cells[y][x] = Cell.EMPTY

    if transpose:
        return matrix.transpose()

    return matrix


def load_matrix_from_file(filename: str) -> Matrix:
    with open(filename, "r") as f:
        return load_matrix_from_string(f.read())
