from qgis.PyQt.QtWidgets import QFrame, QHBoxLayout, QSizePolicy, QGraphicsDropShadowEffect, QGraphicsOpacityEffect, QStyle, QApplication, QLabel, QToolButton
from qgis.PyQt.QtCore import QTimer, QPropertyAnimation, Qt, QPoint, QEvent, QRectF, QRect, QSize
from qgis.PyQt.QtGui import QColor, QPainterPath, QTransform, QRegion, QCursor

class Toaster(QFrame):

    def __init__(self, *args, **kwargs):
        super(Toaster, self).__init__(*args, **kwargs)
        QHBoxLayout(self)
        self.setSizePolicy(QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Maximum)
        self.setStyleSheet('\n            Toaster {\n                border-radius: 5px; \n                background: palette(window);\n            }\n        ')
        self.shadow = QGraphicsDropShadowEffect(self)
        self.shadow.setBlurRadius(16)
        self.shadow.setXOffset(8)
        self.shadow.setYOffset(8)
        self.shadow.setColor(QColor('#000000'))
        self.setGraphicsEffect(self.shadow)
        self.timer = QTimer(singleShot=True, timeout=self.hide)
        if self.parent():
            self.opacityEffect = QGraphicsOpacityEffect(opacity=0)
            self.setGraphicsEffect(self.opacityEffect)
            self.opacityAni = QPropertyAnimation(self.opacityEffect, b'opacity')
            self.parent().installEventFilter(self)
        else:
            self.opacityAni = QPropertyAnimation(self, b'windowOpacity')
        self.opacityAni.setStartValue(0.0)
        self.opacityAni.setEndValue(1.0)
        self.opacityAni.setDuration(100)
        self.opacityAni.finished.connect(self.checkClosed)
        self.corner = Qt.Corner.TopLeftCorner
        self.margin = 10

    def checkClosed(self):
        if self.opacityAni.direction() == self.opacityAni.Backward:
            self.close()

    def restore(self):
        self.timer.stop()
        self.opacityAni.stop()
        if self.parent():
            self.opacityEffect.setOpacity(1)
        else:
            self.setWindowOpacity(1)

    def hide(self):
        self.opacityAni.setDirection(self.opacityAni.Backward)
        self.opacityAni.setDuration(500)
        self.opacityAni.start()

    def eventFilter(self, source, event):
        if source == self.parent() and event.type() == QEvent.Type.Resize:
            self.opacityAni.stop()
            parentRect = self.parent().rect()
            geo = self.geometry()
            if self.corner == Qt.Corner.TopLeftCorner:
                geo.moveTopLeft(parentRect.topLeft() + QPoint(self.margin, self.margin))
            elif self.corner == Qt.Corner.TopRightCorner:
                geo.moveTopRight(parentRect.topRight() + QPoint(-self.margin, self.margin))
            elif self.corner == Qt.Corner.BottomRightCorner:
                geo.moveBottomRight(parentRect.bottomRight() + QPoint(-self.margin, -self.margin))
            else:
                geo.moveBottomLeft(parentRect.bottomLeft() + QPoint(self.margin, -self.margin))
            self.setGeometry(geo)
            self.restore()
            self.timer.start()
        return super(Toaster, self).eventFilter(source, event)

    def enterEvent(self, event):
        self.restore()

    def leaveEvent(self, event):
        self.timer.start()

    def closeEvent(self, event):
        self.deleteLater()

    def resizeEvent(self, event):
        super(Toaster, self).resizeEvent(event)
        if not self.parent():
            path = QPainterPath()
            path.addRoundedRect(QRectF(self.rect()).translated(-0.5, -0.5), 4, 4)
            self.setMask(QRegion(path.toFillPolygon(QTransform()).toPolygon()))
        else:
            self.clearMask()

    @staticmethod
    def showMessage(parent, message=None, widget=None, icon=QStyle.StandardPixmap.SP_MessageBoxInformation, color=None, corner=Qt.Corner.TopLeftCorner, margin=10, closable=True, timeout=5000, desktop=False, parentWindow=True, background_color=None) -> 'Toaster':
        if parent and parentWindow:
            parent = parent.window()
        if not parent or desktop:
            self = Toaster(None)
            self.setWindowFlags(self.windowFlags() | Qt.WindowType.FramelessWindowHint | Qt.WindowType.BypassWindowManagerHint)
            self.__self = self
            currentScreen = QApplication.primaryScreen()
            if parent and parent.window().geometry().size().isValid():
                reference = parent.window().geometry()
            else:
                reference = QRect(QCursor.pos() - QPoint(1, 1), QSize(3, 3))
            maxArea = 0
            for screen in QApplication.screens():
                intersected = screen.geometry().intersected(reference)
                area = intersected.width() * intersected.height()
                if area > maxArea:
                    maxArea = area
                    currentScreen = screen
            parentRect = currentScreen.availableGeometry()
        else:
            self = Toaster(parent)
            parentRect = parent.rect()
        if isinstance(icon, QStyle.StandardPixmap):
            labelIcon = QLabel()
            self.layout().addWidget(labelIcon)
            icon = self.style().standardIcon(icon)
            size = self.style().pixelMetric(QStyle.PixelMetric.PM_SmallIconSize)
            labelIcon.setPixmap(icon.pixmap(size))
        if message is not None:
            self.label = QLabel(message)
            self.layout().addWidget(self.label)
        if widget is not None:
            self.widget = widget
            self.layout().addWidget(self.widget)
        if closable:
            self.closeButton = QToolButton()
            self.layout().addWidget(self.closeButton)
            closeIcon = self.style().standardIcon(QStyle.StandardPixmap.SP_TitleBarCloseButton)
            self.closeButton.setIcon(closeIcon)
            self.closeButton.setAutoRaise(True)
            self.closeButton.clicked.connect(self.close)
        if background_color is not None:
            self.setAttribute(Qt.WidgetAttribute.WA_StyledBackground, True)
            self.setStyleSheet(self.styleSheet() + f'Toaster {{ background-color: {background_color}; }}')
        if color is not None:
            self.setStyleSheet(self.styleSheet() + f'QLabel {{ color: {color}; }}')
        if timeout and timeout > 0:
            self.timer.setInterval(timeout)
            self.timer.start()
        self.raise_()
        self.adjustSize()
        self.corner = corner
        self.margin = margin
        geo = self.geometry()
        if corner == Qt.Corner.TopLeftCorner:
            geo.moveTopLeft(parentRect.topLeft() + QPoint(margin, margin))
        elif corner == Qt.Corner.TopRightCorner:
            geo.moveTopRight(parentRect.topRight() + QPoint(-margin, margin))
        elif corner == Qt.Corner.BottomRightCorner:
            geo.moveBottomRight(parentRect.bottomRight() + QPoint(-margin, -margin))
        else:
            geo.moveBottomLeft(parentRect.bottomLeft() + QPoint(margin, -margin))
        self.setGeometry(geo)
        self.show()
        self.opacityAni.start()
        return self