from __future__ import annotations

import typing

from urwid.canvas import CanvasJoin, CompositeCanvas, TextCanvas
from urwid.util import decompose_tagmarkup

from .constants import Sizing
from .widget import Widget, fixed_size

if typing.TYPE_CHECKING:
    from collections.abc import Hashable

    from urwid import Font


class BigText(Widget):
    _sizing = frozenset([Sizing.FIXED])

    def __init__(self, markup, font: Font) -> None:
        """
        markup -- same as Text widget markup
        font -- instance of a Font class
        """
        super().__init__()
        self.text: str = ""
        self.attrib = []
        self.font: Font = font
        self.set_font(font)
        self.set_text(markup)

    def set_text(self, markup):
        self.text, self.attrib = decompose_tagmarkup(markup)
        self._invalidate()

    def get_text(self):
        """
        Returns (text, attributes).
        """
        return self.text, self.attrib

    def set_font(self, font: Font) -> None:
        self.font = font
        self._invalidate()

    def pack(
        self,
        size: tuple[()] | None = (),  # type: ignore[override]
        focus: bool = False,
    ) -> tuple[int, int]:
        rows = self.font.height
        cols = 0
        for c in self.text:
            cols += self.font.char_width(c)
        return cols, rows

    def render(
        self,
        size: tuple[()],  # type: ignore[override]
        focus: bool = False,
    ) -> CompositeCanvas:
        fixed_size(size)  # complain if parameter is wrong
        a: Hashable | None = None
        ai = ak = 0
        o = []
        rows = self.font.height
        attrib = [*self.attrib, (None, len(self.text))]
        for ch in self.text:
            if not ak:
                a, ak = attrib[ai]
                ai += 1
            ak -= 1
            width = self.font.char_width(ch)
            if not width:
                # ignore invalid characters
                continue
            c: TextCanvas | CompositeCanvas = self.font.render(ch)
            if a is not None:
                c = CompositeCanvas(c)
                c.fill_attr(a)
            o.append((c, None, False, width))
        if o:
            canv = CanvasJoin(o)
        else:
            canv = CompositeCanvas(TextCanvas([b""] * rows, maxcol=0, check_width=False))
        canv.set_depends([])
        return canv
