from typing import Any

from AnyQt.QtWidgets import QTextBrowser
from AnyQt.QtGui import QStatusTipEvent, QWhatsThisClickedEvent
from AnyQt.QtCore import QObject, QCoreApplication, QEvent, QTimer, QUrl
from AnyQt.QtCore import pyqtSignal as Signal


class QuickHelp(QTextBrowser):

    #: Emitted when the shown text changes.
    textChanged = Signal()

    def __init__(self, *args, **kwargs):
        # type: (Any, Any) -> None
        super().__init__(*args, **kwargs)

        self.setOpenExternalLinks(False)
        self.setOpenLinks(False)

        self.__text = ""
        self.__permanentText = ""
        self.__defaultText = ""

        self.__timer = QTimer(self, timeout=self.__on_timeout,
                              singleShot=True)
        self.anchorClicked.connect(self.__on_anchorClicked)

    def showHelp(self, text, timeout=0):
        # type: (str, int) -> None
        """
        Show help for `timeout` milliseconds. if timeout is 0 then
        show the text until it is cleared with clearHelp or showHelp is
        called with an empty string.

        """
        if self.__text != text:
            self.__text = text
            self.__update()
            self.textChanged.emit()

        if timeout > 0:
            self.__timer.start(timeout)

    def clearHelp(self):
        # type: () -> None
        """
        Clear help text previously set with `showHelp`.
        """
        self.__timer.stop()
        self.showHelp("")

    def showPermanentHelp(self, text):
        # type: (str) -> None
        """
        Set permanent help text. The text may be temporarily overridden
        by showHelp but will be shown again when that is cleared.
        """
        if self.__permanentText != text:
            self.__permanentText = text
            self.__update()
            self.textChanged.emit()

    def setDefaultText(self, text):
        # type: (str) -> None
        """
        Set default help text. The text is overriden by normal and permanent help messages,
        but is show again after such messages are cleared.
        """
        if self.__defaultText != text:
            self.__defaultText = text
            self.__update()
            self.textChanged.emit()

    def currentText(self):
        # type: () -> str
        """
        Return the current shown text.
        """
        return self.__text or self.__permanentText

    def __update(self):
        # type: () -> None
        if self.__text:
            self.setHtml(self.__text)
        elif self.__permanentText:
            self.setHtml(self.__permanentText)
        else:
            self.setHtml(self.__defaultText)

    def __on_timeout(self):
        # type: () -> None
        if self.__text:
            self.__text = ""
            self.__update()
            self.textChanged.emit()

    def __on_anchorClicked(self, anchor):
        # type: (QUrl) -> None
        ev = QuickHelpDetailRequestEvent(anchor.toString(), anchor)
        QCoreApplication.postEvent(self, ev)


class QuickHelpTipEvent(QStatusTipEvent):
    Temporary, Normal, Permanent = range(1, 4)

    def __init__(self, tip, html="", priority=Normal, timeout=0):
        # type: (str, str, int, int) -> None
        super().__init__(tip)
        self.__html = html or ""
        self.__priority = priority
        self.__timeout = timeout

    def html(self):
        # type: () -> str
        return self.__html

    def priority(self):
        # type: () -> int
        return self.__priority

    def timeout(self):
        # type: () -> int
        return self.__timeout


class QuickHelpDetailRequestEvent(QWhatsThisClickedEvent):
    def __init__(self, href, url):
        # type: (str, QUrl) -> None
        super().__init__(href)
        self.__url = QUrl(url)

    def url(self):
        # type: () -> QUrl
        return QUrl(self.__url)


class StatusTipPromoter(QObject):
    """
    Promotes `QStatusTipEvent` to `QuickHelpTipEvent` using ``whatsThis``
    property of the object.

    """
    def eventFilter(self, obj, event):
        # type: (QObject, QEvent) -> bool
        if event.type() == QEvent.StatusTip and \
                not isinstance(event, QuickHelpTipEvent) and \
                hasattr(obj, "whatsThis") and \
                callable(obj.whatsThis):
            assert isinstance(event, QStatusTipEvent)
            tip = event.tip()
            try:
                text = obj.whatsThis()
            except Exception:
                text = None

            if text:
                ev = QuickHelpTipEvent(tip, text if tip else "")
                return QCoreApplication.sendEvent(obj, ev)

        return super().eventFilter(obj, event)
