"""Style attributes for pyos rendering.

Provides a ``Style`` class that carries text-decoration flags *and*
foreground/background colors.  The class is backward-compatible with the
old integer bitmask API — ``BOLD | REVERSE``, ``(actual & attr) == attr``,
and ``style == 0`` all continue to work.

Color usage::

    screen.addstr(y, x, text, BOLD | RED)
    screen.addstr(y, x, text, fg("bright_red") | bg("blue"))
    screen.addstr(y, x, text, fg(196) | bg((30, 30, 30)))
"""


class Style:
    """Immutable text style carrying decoration flags and optional colors."""

    __slots__ = ("_flags", "fg", "bg")

    def __init__(self, flags=0, fg=None, bg=None):
        object.__setattr__(self, "_flags", flags)
        object.__setattr__(self, "fg", fg)
        object.__setattr__(self, "bg", bg)

    def __setattr__(self, name, value):
        raise AttributeError("Style objects are immutable")

    def __delattr__(self, name):
        raise AttributeError("Style objects are immutable")

    # -- combining --------------------------------------------------------

    def __or__(self, other):
        if isinstance(other, Style):
            return Style(
                self._flags | other._flags,
                other.fg if other.fg is not None else self.fg,
                other.bg if other.bg is not None else self.bg,
            )
        if isinstance(other, int):
            return Style(self._flags | other, self.fg, self.bg)
        return NotImplemented

    def __ror__(self, other):
        if isinstance(other, int):
            return Style(other | self._flags, self.fg, self.bg)
        return NotImplemented

    # -- intersection (for the ``(actual & attr) == attr`` pattern) -------

    def __and__(self, other):
        if isinstance(other, Style):
            flags = self._flags & other._flags
            fg = self.fg if self.fg == other.fg else None
            bg = self.bg if self.bg == other.bg else None
            return Style(flags, fg, bg)
        if isinstance(other, int):
            return Style(self._flags & other, self.fg, self.bg)
        return NotImplemented

    def __rand__(self, other):
        if isinstance(other, int):
            return Style(other & self._flags, self.fg, self.bg)
        return NotImplemented

    # -- equality ---------------------------------------------------------

    def __eq__(self, other):
        if isinstance(other, Style):
            return (
                self._flags == other._flags
                and self.fg == other.fg
                and self.bg == other.bg
            )
        if isinstance(other, int):
            return self._flags == other and self.fg is None and self.bg is None
        return NotImplemented

    def __ne__(self, other):
        result = self.__eq__(other)
        if result is NotImplemented:
            return result
        return not result

    def __hash__(self):
        return hash((self._flags, self.fg, self.bg))

    def __bool__(self):
        return self._flags != 0 or self.fg is not None or self.bg is not None

    def __repr__(self):
        parts = []
        names = {1: "BOLD", 2: "DIM", 4: "UNDERLINE", 8: "REVERSE"}
        for bit, name in names.items():
            if self._flags & bit:
                parts.append(name)
        if self.fg is not None:
            parts.append(f"fg={self.fg!r}")
        if self.bg is not None:
            parts.append(f"bg={self.bg!r}")
        return f"Style({' | '.join(parts)})" if parts else "Style(NORMAL)"

    # -- int compatibility ------------------------------------------------

    def __int__(self):
        return self._flags

    def __index__(self):
        return self._flags

    @property
    def flags(self):
        """The raw decoration bitmask (read-only)."""
        return self._flags


# -- decoration constants ------------------------------------------------

NORMAL    = Style(0)
BOLD      = Style(1 << 0)  # 1
DIM       = Style(1 << 1)  # 2
UNDERLINE = Style(1 << 2)  # 4
REVERSE   = Style(1 << 3)  # 8


# -- color constants (foreground) ----------------------------------------

BLACK   = Style(fg="black")
RED     = Style(fg="red")
GREEN   = Style(fg="green")
YELLOW  = Style(fg="yellow")
BLUE    = Style(fg="blue")
MAGENTA = Style(fg="magenta")
CYAN    = Style(fg="cyan")
WHITE   = Style(fg="white")


# -- helpers -------------------------------------------------------------

def fg(color):
    """Return a Style with only a foreground color.

    *color* may be a blessed color name (str), 256-color index (int),
    or an (r, g, b) tuple for true color.
    """
    return Style(fg=color)


def bg(color):
    """Return a Style with only a background color.

    *color* may be a blessed color name (str), 256-color index (int),
    or an (r, g, b) tuple for true color.
    """
    return Style(bg=color)
