"""
Overlay Message Widget
----------------------

A Widget to display a temporary dismissable message over another widget.

"""

import sys
import enum
import functools
import operator
from collections import namedtuple

from PyQt4.QtGui import (
    QIcon, QHBoxLayout, QPushButton, QLabel, QSizePolicy, QStyle,
    QAbstractButton, QStyleOptionButton, QStylePainter, QFocusFrame,
    QWidget, QPixmap, QStyleOption, QPainter
)
from PyQt4.QtCore import Qt, QSize, QRect, QPoint, QEvent, QTimer
from PyQt4.QtCore import pyqtSignal as Signal


class OverlayWidget(QWidget):
    """
    A widget positioned on top of another widget.
    """
    def __init__(self, parent=None, alignment=Qt.AlignCenter, **kwargs):
        super().__init__(parent, **kwargs)
        self.setContentsMargins(0, 0, 0, 0)
        self.__alignment = alignment
        self.__widget = None

    def setWidget(self, widget):
        """
        Set the widget over which this overlay should be displayed (anchored).

        :type widget: QWidget
        """
        if self.__widget is not None:
            self.__widget.removeEventFilter(self)
            self.__widget.destroyed.disconnect(self.__on_destroyed)
        self.__widget = widget
        if self.__widget is not None:
            self.__widget.installEventFilter(self)
            self.__widget.destroyed.connect(self.__on_destroyed)

        if self.__widget is None:
            self.hide()
        else:
            self.__layout()

    def widget(self):
        """
        Return the overlaid widget.

        :rtype: QWidget | None
        """
        return self.__widget

    def setAlignment(self, alignment):
        """
        Set overlay alignment.

        :type alignment: Qt.Alignment
        """
        if self.__alignment != alignment:
            self.__alignment = alignment
            if self.__widget is not None:
  
              self.__layout()

    def alignment(self):
        """
        Return the overlay alignment.

        :rtype: Qt.Alignment
        """
        return self.__alignment

    def eventFilter(self, recv, event):
        # reimplemented
        if recv is self.__widget:
  
          if event.type() == QEvent.Resize or event.type() == QEvent.Move:
                self.__layout()
            elif event.type() == QEvent.Show:
                self.show()
            elif event.type() == QEvent.Hide:
                self.hide()
        return super().eventFilter(recv, event)

    def event(self, event):
        # reimplemented
        if event.type() == QEvent.LayoutRequest:
            self.__layout()
            return True
        else:
            return super().event(event)

    def paintEvent(self, event):
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)

    def __layout(self):
        # position itself over `widget`
        widget = self.__widget
        if widget is None:
            return

        alignment = self.__alignment
        policy = self.sizePolicy()

        if widget.isWindow():
            bounds = widget.geometry()
        else:

            bounds = QRect(widget.mapToGlobal(QPoint(0, 0)),
                           widget.size())
        if self.isWindow():
            bounds = bounds
        else:
            bounds = QRect(self.parent().mapFromGlobal(bounds.topLeft()),
                           bounds.size())

        sh = self.sizeHint()
        minsh = self.minimumSizeHint()
        minsize = self.minimumSize()
        if minsize.isNull():
            minsize = minsh
        maxsize = bounds.size().boundedTo(self.maximumSize())
        minsize = minsize.boundedTo(maxsize)
        effectivesh = sh.expandedTo(minsize).boundedTo(maxsize)

        hpolicy = policy.horizontalPolicy()
        vpolicy = policy.verticalPolicy()

        def getsize(hint, minimum, maximum, policy):
            if policy == QSizePolicy.Ignored:
                return maximum
            elif policy & QSizePolicy.ExpandFlag:
                return maximum
            else:
                return max(hint, minimum)

        width = getsize(effectivesh.width(), minsize.width(),
                        maxsize.width(), hpolicy)

        heightforw = self.heightForWidth(width)
        if heightforw > 0:
            height = getsize(heightforw, minsize.height(),
                             maxsize.height(), vpolicy)
        else:
            height = getsize(effectivesh.height(), minsize.height(),
                             maxsize.height(), vpolicy)

        size = QSize(width, height)
        if alignment & Qt.AlignLeft:
            x = bounds.x()
        elif alignment & Qt.AlignRight:
            x = bounds.right() - size.width()
        else:
            x = bounds.x() + max(0, bounds.width() - size.width()) // 2

        if alignment & Qt.AlignTop:
            y = bounds.y()
        elif alignment & Qt.AlignBottom:
            y = bounds.bottom() - size.height()
        else:
            y = bounds.y() + max(0, bounds.height() - size.height()) // 2

        geom = QRect(QPoint(x, y), size)
        self.setGeometry(geom)

    def __on_destroyed(self):
        self.__widget = None
        if self.isVisible():
            self.hide()


class SimpleButton(QAbstractButton):
    """
    A simple icon button widget.
    """
    def __init__(self, parent=None, **kwargs):
        super().__init__(parent, **kwargs)
        self.__focusframe = None

    def focusInEvent(self, event):
        # reimplemented
        event.accept()
        self.__focusframe = QFocusFrame(self)
        self.__focusframe.setWidget(self)

    def focusOutEvent(self, event):
        # reimplemented
        event.accept()
        self.__focusframe.deleteLater()
        self.__focusframe = None

    def sizeHint(self):
        # reimplemented
        self.ensurePolished()
        iconsize = self.iconSize()
        icon = self.icon()
        if not icon.isNull():
            iconsize = icon.actualSize(iconsize)
        return iconsize

    def minimumSizeHint(self):
        # reimplemented
        return self.sizeHint()

    def paintEvent(self, event):
        # reimplemented
        painter = QStylePainter(self)
        option = QStyleOptionButton()
        option.initFrom(self)
        option.icon = self.icon()
        option.iconSize = self.iconSize()

        icon = self.icon()

        if not icon.isNull():
            if option.state & QStyle.State_Active:
                mode = (QIcon.Normal if option.state & QStyle.State_MouseOver
                        else QIcon.Active)
            else:
                mode = QIcon.Disabled
            pixmap = icon.pixmap(option.iconSize, mode, )

            painter.drawItemPixmap(option.rect, Qt.AlignCenter, pixmap)


class MessageWidget(QWidget):
    """
    A widget displaying a simple message to the user.

    This is an alternative to a full QMessageBox intended for inline
    modeless messages.

    [[icon] {Message text} (Ok) (Cancel)]
    """
    #: Emitted when a button with the AcceptRole is clicked
    accepted = Signal()
    #: Emitted when a button with the RejectRole is clicked
    rejected = Signal()
    #: Emitted when a button with the HelpRole is clicked
    helpRequested = Signal()
    #: Emitted when a button is clicked
    clicked = Signal(QAbstractButton)

    class StandardButton(enum.IntEnum):
        NoButton, Ok, Close, Help = 0x0, 0x1, 0x2, 0x4
    NoButton, Ok, Close, Help = list(StandardButton)

    class ButtonRole(enum.IntEnum):
        InvalidRole, AcceptRole, RejectRole, HelpRole = 0, 1, 2, 3

    InvalidRole, AcceptRole, RejectRole, HelpRole = list(ButtonRole)

    _Button = namedtuple("_Button", ["button", "role", "stdbutton"])

    def __init__(self, parent=None, icon=QIcon(), text="", wordWrap=False,
                 textFormat=Qt.AutoText, standardButtons=NoButton, **kwargs):
        super().__init__(parent, **kwargs)
        self.__text = text
        self.__icon = QIcon()
        self.__wordWrap = wordWrap
        self.__standardButtons = MessageWidget.NoButton
        self.__buttons = []

        layout = QHB