"""Buffered screen adapter that renders via blessed.Terminal.

Implements the same interface as MockScreen (addstr, clear, refresh,
getmaxyx, move, clrtoeol) so Activities and printers work unchanged.
"""

from .Attrs import NORMAL, BOLD, DIM, UNDERLINE, REVERSE, Style


class BlessedScreen:
    """Real terminal screen backed by a blessed Terminal."""

    def __init__(self, term):
        self._term = term
        self._width = term.width
        self._height = term.height
        self._buffer = self._make_buffer(self._height, self._width)
        self._attr_buffer = self._make_attr_buffer(self._height, self._width)
        self._prev_buffer = None
        self._prev_attr_buffer = None
        self.cursor_y = 0
        self.cursor_x = 0
        self._cursor_request = None  # (y, x) or None

    # -- buffer helpers ------------------------------------------------

    @staticmethod
    def _make_buffer(rows, cols):
        return [[" "] * cols for _ in range(rows)]

    @staticmethod
    def _make_attr_buffer(rows, cols):
        return [[NORMAL] * cols for _ in range(rows)]

    # -- curses-compatible interface -----------------------------------

    def getmaxyx(self):
        return (self._height, self._width)

    def addstr(self, y, x, text, attr=NORMAL):
        text = str(text)
        for i, ch in enumerate(text):
            col = x + i
            if col >= self._width:
                break
            if 0 <= y < self._height and 0 <= col:
                self._buffer[y][col] = ch
                self._attr_buffer[y][col] = attr
        self.cursor_y = y
        self.cursor_x = min(x + len(text), self._width)

    def request_cursor(self, y, x):
        """Request the terminal cursor be shown at (y, x) after refresh."""
        self._cursor_request = (y, x)

    def clear(self):
        # Check for terminal resize
        w = self._term.width
        h = self._term.height
        if w != self._width or h != self._height:
            self._width = w
            self._height = h
            self._prev_buffer = None
            self._prev_attr_buffer = None
        self._buffer = self._make_buffer(self._height, self._width)
        self._attr_buffer = self._make_attr_buffer(self._height, self._width)
        self._cursor_request = None

    def refresh(self):
        """Render the buffer to the terminal.

        Uses differential update — only redraws rows that changed since
        the last refresh.
        """
        term = self._term
        parts = []

        for row in range(self._height):
            # Skip unchanged rows
            if (self._prev_buffer is not None
                    and row < len(self._prev_buffer)
                    and self._buffer[row] == self._prev_buffer[row]
                    and self._attr_buffer[row] == self._prev_attr_buffer[row]):
                continue

            parts.append(term.move(row, 0))
            prev_attr = -1
            for col in range(self._width):
                a = self._attr_buffer[row][col]
                ch = self._buffer[row][col]
                if a != prev_attr:
                    parts.append(self._attr_to_blessed(a))
                    prev_attr = a
                parts.append(ch)
            parts.append(term.normal)

        # Show and position cursor if a text input requested it, hide otherwise
        if self._cursor_request:
            cy, cx = self._cursor_request
            parts.append(term.move(cy, cx))
            parts.append(term.normal_cursor)
        else:
            parts.append(term.hide_cursor)

        if parts:
            print("".join(parts), end="", flush=True)

        # Save current frame for next diff
        self._prev_buffer = [row[:] for row in self._buffer]
        self._prev_attr_buffer = [row[:] for row in self._attr_buffer]

    def move(self, y, x):
        self.cursor_y = y
        self.cursor_x = x

    def clrtoeol(self):
        if 0 <= self.cursor_y < self._height:
            for col in range(self.cursor_x, self._width):
                self._buffer[self.cursor_y][col] = " "
                self._attr_buffer[self.cursor_y][col] = NORMAL

    def timeout(self, ms):
        pass  # Not needed — blessed uses inkey(timeout=...)

    def getch(self):
        return -1  # Not used — input handled by BlessedInput

    # -- attr mapping --------------------------------------------------

    def _attr_to_blessed(self, attr):
        """Convert a Style (or legacy int) to blessed formatting string."""
        term = self._term
        if not attr:
            return term.normal
        parts = []
        if attr & BOLD:
            parts.append(term.bold)
        if attr & DIM:
            parts.append(term.dim)
        if attr & UNDERLINE:
            parts.append(term.underline)
        if attr & REVERSE:
            parts.append(term.reverse)
        # Color support (Style objects only)
        if isinstance(attr, Style):
            fg = attr.fg
            bg = attr.bg
            if fg is not None:
                if isinstance(fg, str):
                    parts.append(getattr(term, fg, ""))
                elif isinstance(fg, int):
                    parts.append(term.color(fg))
                elif isinstance(fg, tuple):
                    parts.append(term.color_rgb(*fg))
            if bg is not None:
                if isinstance(bg, str):
                    parts.append(getattr(term, "on_" + bg, ""))
                elif isinstance(bg, int):
                    parts.append(term.on_color(bg))
                elif isinstance(bg, tuple):
                    parts.append(term.on_color_rgb(*bg))
        return "".join(parts) if parts else term.normal
