from __future__ import annotations

from typing import TYPE_CHECKING

import rich.repr

from textual._types import CallbackType
from textual.geometry import Region
from textual.message import Message

if TYPE_CHECKING:
    from textual.widget import Widget


@rich.repr.auto
class CloseMessages(Message, verbose=True):
    """Requests message pump to close."""


@rich.repr.auto
class Prune(Message, verbose=True, bubble=False):
    """Ask the node to prune (remove from DOM)."""


@rich.repr.auto
class ExitApp(Message, verbose=True):
    """Exit the app."""


@rich.repr.auto
class Update(Message, verbose=True):
    """Sent by Textual to request the update of a widget."""

    def __init__(self, widget: Widget) -> None:
        super().__init__()
        self.widget = widget

    def __rich_repr__(self) -> rich.repr.Result:
        yield self.widget

    def __eq__(self, other: object) -> bool:
        if isinstance(other, Update):
            return self.widget == other.widget
        return NotImplemented

    def can_replace(self, message: Message) -> bool:
        # Update messages can replace update for the same widget
        return isinstance(message, Update) and self.widget == message.widget


@rich.repr.auto
class Layout(Message, verbose=True):
    """Sent by Textual when a layout is required."""

    def can_replace(self, message: Message) -> bool:
        return isinstance(message, Layout)


@rich.repr.auto
class UpdateScroll(Message, verbose=True):
    """Sent by Textual when a scroll update is required."""

    def can_replace(self, message: Message) -> bool:
        return isinstance(message, UpdateScroll)


@rich.repr.auto
class InvokeLater(Message, verbose=True, bubble=False):
    """Sent by Textual to invoke a callback."""

    def __init__(self, callback: CallbackType) -> None:
        self.callback = callback
        super().__init__()

    def __rich_repr__(self) -> rich.repr.Result:
        yield "callback", self.callback


@rich.repr.auto
class ScrollToRegion(Message, bubble=False):
    """Ask the parent to scroll a given region into view."""

    def __init__(self, region: Region) -> None:
        self.region = region
        super().__init__()


class Prompt(Message, no_dispatch=True):
    """Used to 'wake up' an event loop."""

    def can_replace(self, message: Message) -> bool:
        return isinstance(message, Prompt)


class TerminalSupportsSynchronizedOutput(Message):
    """
    Used to make the App aware that the terminal emulator supports synchronised output.
    @link https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
    """


@rich.repr.auto
class InBandWindowResize(Message):
    """Reports if the in-band window resize protocol is supported.

    https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83"""

    def __init__(self, supported: bool, enabled: bool) -> None:
        """Initialize message.

        Args:
            supported: Is the protocol supported?
            enabled: Is the protocol enabled.
        """
        self.supported = supported
        self.enabled = enabled
        super().__init__()

    def __rich_repr__(self) -> rich.repr.Result:
        yield "supported", self.supported
        yield "enabled", self.enabled

    @classmethod
    def from_setting_parameter(cls, setting_parameter: int) -> InBandWindowResize:
        """Construct the message from the setting parameter.

        Args:
            setting_parameter: Setting parameter from stdin.

        Returns:
            New InBandWindowResize instance.
        """

        supported = setting_parameter not in (0, 4)
        enabled = setting_parameter in (1, 3)
        return InBandWindowResize(supported, enabled)
