# -*- coding: UTF-8 -*-
# Copyright 2020 by Kurt Rathjen. All Rights Reserved.
#
# This library is free software: you can redistribute it and/or modify it 
# under the terms of the GNU Lesser General Public License as published by 
# the Free Software Foundation, either version 3 of the License, or 
# (at your option) any later version. This library is distributed in the 
# hope that it will be useful, but WITHOUT ANY WARRANTY; without even the 
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
# See the GNU Lesser General Public License for more details.
# You should have received a copy of the GNU Lesser General Public
# License along with this library. If not, see <http://www.gnu.org/licenses/>.

from .themesmenu import Theme

from studiovendor import six
from studiovendor.Qt import QtCore
from studiovendor.Qt import QtWidgets

import studioqt
import studiolibrary

from . import settings


def createMessageBox(
        parent,
        title,
        text,
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor=None,
        enableInputEdit=False,
        enableDontShowCheckBox=False
):
    """
    Open a question message box with the given options.

    :type parent: QWidget
    :type title: str
    :type text: str
    :type buttons: list[QMessageBox.StandardButton]
    :type headerIcon: str
    :type headerColor: str
    :type enableDontShowCheckBox: bool

    :rtype: MessageBox
    """
    mb = MessageBox(
        parent,
        width=width,
        height=height,
        enableInputEdit=enableInputEdit,
        enableDontShowCheckBox=enableDontShowCheckBox
    )

    mb.setText(text)

    buttons = buttons or [QtWidgets.QDialogButtonBox.Ok]
    mb.setButtons(buttons)

    if headerIcon:
        p = studiolibrary.resource.pixmap(headerIcon)
        mb.setPixmap(p)

    try:
        theme = parent.theme()
    except AttributeError:
        theme = Theme()

    mb.setStyleSheet(theme.styleSheet())

    headerColor = headerColor or theme.accentColor().toString()
    headerColor = headerColor or "rgb(50, 150, 200)"

    mb.setHeaderColor(headerColor)

    mb.setWindowTitle(title)
    mb.setTitleText(title)

    return mb


def showMessageBox(
        parent,
        title,
        text,
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor=None,
        enableDontShowCheckBox=False,
        force=False
):
    """
    Open a question message box with the given options.

    :type parent: QWidget
    :type title: str
    :type text: str
    :type buttons: list[QMessageBox.StandardButton]
    :type headerIcon: str
    :type headerColor: str
    :type enableDontShowCheckBox: bool
    :type force: bool

    :rtype: MessageBox
    """
    key = title.replace(" ", "") + "MessageBox"
    data = settings.get(key, {})

    clickedButton = data.get("clickedButton",  -1)
    dontShowAgain = data.get("dontShowAgain", False)

    # Force show the dialog if the user is holding the ctrl key down
    if studioqt.isControlModifier() or studioqt.isAltModifier():
        force = True

    if force or not dontShowAgain or not enableDontShowCheckBox:

        mb = createMessageBox(
            parent,
            title,
            text,
            width=width,
            height=height,
            buttons=buttons,
            headerIcon=headerIcon,
            headerColor=headerColor,
            enableDontShowCheckBox=enableDontShowCheckBox
        )

        mb.exec_()
        mb.close()

        clickedButton = mb.clickedStandardButton()
        if clickedButton != QtWidgets.QDialogButtonBox.Cancel:

            # Save the button that was clicked by the user
            settings.set(key,
                {
                    "clickedButton": mb.clickedIndex(),
                    "dontShowAgain":  bool(mb.isDontShowCheckboxChecked()),
                }
            )

    return clickedButton


class MessageBox(QtWidgets.QDialog):

    @staticmethod
    def input(
        parent,
        title,
        text,
        inputText="",
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor=None,
    ):
        """
        Convenience dialog to get a single text value from the user.
        
        :type parent: QWidget
        :type title: str
        :type text: str
        :type width: int
        :type height: int
        :type buttons: list[QMessageBox.StandardButton]
        :type headerIcon: str
        :type headerColor: str
        :rtype: QMessageBox.StandardButton
        """
        buttons = buttons or [
            QtWidgets.QDialogButtonBox.Ok,
            QtWidgets.QDialogButtonBox.Cancel
        ]

        dialog = createMessageBox(
            parent,
            title,
            text,
            width=width,
            height=height,
            buttons=buttons,
            headerIcon=headerIcon,
            headerColor=headerColor,
            enableInputEdit=True,
        )

        dialog.setInputText(inputText)
        dialog.exec_()

        return dialog.inputText(), dialog.clickedText()

    @staticmethod
    def question(
        parent,
        title,
        text,
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor=None,
        enableDontShowCheckBox=False
    ):
        """
        Open a question message box with the given options.

        :type parent: QWidget
        :type title: str
        :type text: str
        :type headerIcon: str
        :type headerColor: str
        :type buttons: list[QMessageBox.StandardButton]

        :rtype: QMessageBox.StandardButton
        """
        buttons = buttons or [
            QtWidgets.QDialogButtonBox.Yes,
            QtWidgets.QDialogButtonBox.No,
            QtWidgets.QDialogButtonBox.Cancel
        ]

        clickedButton = showMessageBox(
            parent,
            title,
            text,
            width=width,
            height=height,
            buttons=buttons,
            headerIcon=headerIcon,
            headerColor=headerColor,
            enableDontShowCheckBox=enableDontShowCheckBox,
        )

        return clickedButton

    @staticmethod
    def warning(
        parent,
        title,
        text,
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor="rgb(250, 160, 0)",
        enableDontShowCheckBox=False,
        force=False,
    ):
        """
        Open a warning message box with the given options.

        :type parent: QWidget
        :type title: str
        :type text: str
        :type buttons: list[QMessageBox.StandardButton]
        :type headerIcon: str
        :type headerColor: str
        :type enableDontShowCheckBox: bool
        :type force: bool

        :rtype: (QMessageBox.StandardButton, bool)
        """
        buttons = buttons or [
                QtWidgets.QDialogButtonBox.Yes,
                QtWidgets.QDialogButtonBox.No
            ]

        clickedButton = showMessageBox(
            parent,
            title,
            text,
            width=width,
            height=height,
            buttons=buttons,
            headerIcon=headerIcon,
            headerColor=headerColor,
            enableDontShowCheckBox=enableDontShowCheckBox,
            force=force
        )

        return clickedButton

    @staticmethod
    def critical(
        parent,
        title,
        text,
        width=None,
        height=None,
        buttons=None,
        headerIcon=None,
        headerColor="rgb(230, 80, 80)"
    ):
        """
        Open a critical message box with the given options.

        :type parent: QWidget
        :type title: str
        :type text: str
        :type headerIcon: str
        :type headerColor: str
        :type buttons: list[QMessageBox.StandardButton]

        :rtype: QMessageBox.StandardButton
        """
        buttons = buttons or [QtWidgets.QDialogButtonBox.Ok]

        clickedButton = showMessageBox(
            parent,
            title,
            text,
            width=width,
            height=height,
            buttons=buttons,
            headerIcon=headerIcon,
            headerColor=headerColor
        )

        return clickedButton

    def __init__(
            self,
            parent=None,
            width=None,
            height=None,
            enableInputEdit=False,
            enableDontShowCheckBox=False
    ):
        super(MessageBox, self).__init__(parent)
        self.setObjectName("messageBox")

        self._frame = None
        self._animation = None
        self._dontShowCheckbox = False
        self._clickedButton = None
        self._clickedStandardButton = None

        if width:
            self.setMinimumWidth(width)

        if height:
            self.setMinimumHeight(height)

        parent = self.parent()

        if parent:
            parent.installEventFilter(self)
            self._frame = QtWidgets.QFrame(parent)
            self._frame.setObjectName("messageBoxFrame")
            self._frame.show()
            self.setParent(self._frame)

        self._header = QtWidgets.QFrame(self)
        self._header.setObjectName("messageBoxHeaderFrame")
        self._header.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                   QtWidgets.QSizePolicy.Fixed)

        self._icon = QtWidgets.QLabel(self._header)
        self._icon.setObjectName("messageBoxIcon")
        self._icon.hide()
        self._icon.setScaledContents(True)
        self._icon.setAlignment(QtCore.Qt.AlignTop)
        self._icon.setSizePolicy(QtWidgets.QSizePolicy.Preferred,
                                 QtWidgets.QSizePolicy.Preferred)

        self._title = QtWidgets.QLabel(self._header)
        self._title.setObjectName("messageBoxHeaderLabel")
        self._title.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding)

        hlayout = QtWidgets.QHBoxLayout(self._header)
        hlayout.setContentsMargins(15, 7, 15, 10)
        hlayout.setSpacing(10)
        hlayout.addWidget(self._icon)
        hlayout.addWidget(self._title)

        self._header.setLayout(hlayout)

        bodyLayout = QtWidgets.QVBoxLayout(self)

        self._body = QtWidgets.QFrame(self)
        self._body.setObjectName("messageBoxBody")
        self._body.setLayout(bodyLayout)

        self._message = QtWidgets.QLabel(self._body)
        self._message.setWordWrap(True)
        self._message.setMinimumHeight(15)
        self._message.setAlignment(QtCore.Qt.AlignLeft)
        self._message.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse)
        self._message.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                    QtWidgets.QSizePolicy.Expanding)

        bodyLayout.addWidget(self._message)
        bodyLayout.setContentsMargins(15, 15, 15, 15)

        if enableInputEdit:
            self._inputEdit = QtWidgets.QLineEdit(self._body)
            self._inputEdit.setObjectName("messageBoxInputEdit")
            self._inputEdit.setFocus()

            bodyLayout.addStretch(1)
            bodyLayout.addWidget(self._inputEdit)
            bodyLayout.addStretch(10)

        if enableDontShowCheckBox:
            msg = u"不再显示此消息"
            self._dontShowCheckbox = QtWidgets.QCheckBox(msg, self._body)

            bodyLayout.addStretch(10)
            bodyLayout.addWidget(self._dontShowCheckbox)
            bodyLayout.addStretch(2)

        self._buttonBox = QtWidgets.QDialogButtonBox(None, QtCore.Qt.Horizontal, self)
        self._buttonBox.clicked.connect(self._clicked)
        self._buttonBox.accepted.connect(self._accept)
        self._buttonBox.rejected.connect(self._reject)

        vlayout1 = QtWidgets.QVBoxLayout(self)
        vlayout1.setContentsMargins(0, 0, 0, 0)

        vlayout1.addWidget(self._header)
        vlayout1.addWidget(self._body)
        bodyLayout.addWidget(self._buttonBox)

        self.setLayout(vlayout1)
        self.updateGeometry()

    def buttonBox(self):
        """
        Return the button box widget for the dialog.
        
        :rtype: QtGui.QDialogButtonBox 
        """
        return self._buttonBox

    def addButton(self, *args):
        """Create a push button with the given text and role"""
        self.buttonBox().addButton(*args)

    def eventFilter(self, object, event):
        """
        Update the geometry when the parent widget changes size.
        
        :type object: QtWidget.QWidget
        :type event: QtCore.QEvent 
        :rtype: bool 
        """
        if event.type() == QtCore.QEvent.Resize:
            self.updateGeometry()
        return super(MessageBox, self).eventFilter(object, event)

    def showEvent(self, event):
        """
        Fade in the dialog on show.

        :type event: QtCore.QEvent 
        :rtype: None 
        """
        self.updateGeometry()
        self.fadeIn()

    def updateGeometry(self):
        """
        Update the geometry to be in the center of it's parent.

        :rtype: None
        """
        frame = self._frame

        if frame:
            frame.setGeometry(self._frame.parent().geometry())
            frame.move(0, 0)

            geometry = self.geometry()
            centerPoint = frame.geometry().center()
            geometry.moveCenter(centerPoint)
            geometry.setY(geometry.y() - 50)
            self.move(geometry.topLeft())

    def fadeIn(self, duration=400):
        """
        Fade in the dialog using the opacity effect.

        :type duration: int 
        :rtype: QtCore.QPropertyAnimation 
        """
        if self._frame:
            self._animation = studioqt.fadeIn(self._frame, duration=duration)
        return self._animation

    def fadeOut(self, duration=400):
        """
        Fade out the dialog using the opacity effect.
        
        :type duration: int 
        :rtype: QtCore.QPropertyAnimation 
        """
        if self._frame:
            self._animation = studioqt.fadeOut(self._frame, duration=duration)
        return self._animation

    def _accept(self):
        """
        Triggered when the DialogButtonBox has been accepted.
        
        :rtype: None 
        """
        animation = self.fadeOut()

        if animation:
            animation.finished.connect(self._acceptAnimationFinished)
        else:
            self._acceptAnimationFinished()

    def _reject(self):
        """
        Triggered when the DialogButtonBox has been rejected.

        :rtype: None 
        """
        animation = self.fadeOut()

        if animation:
            animation.finished.connect(self._rejectAnimationFinished)
        else:
            self._rejectAnimationFinished()

    def _acceptAnimationFinished(self):
        """
        Triggered when the animation has finished on accepted.

        :rtype: None 
        """
        parent = self._frame or self
        parent.close()
        self.accept()

    def _rejectAnimationFinished(self):
        """
        Triggered when the animation has finished on rejected.

        :rtype: None 
        """
        parent = self._frame or self
        parent.close()
        self.reject()

    def header(self):
        """
        Return the header frame.

        :rtype: QtWidgets.QFrame
        """
        return self._header

    def setTitleText(self, text):
        """
        Set the title text to be displayed.

        :type text: str
        :rtype: None 
        """
        self._title.setText(text)

    def setText(self, text):
        """
        Set the text message to be displayed.
        
        :type text: str
        :rtype: None 
        """
        text = six.text_type(text)
        self._message.setText(text)

    def inputText(self):
        """
        Return the text that the user has given the input edit.

        :rtype: str 
        """
        return self._inputEdit.text()

    def setInputText(self, text):
        """
        Set the input text.

        :type text: str 
        """
        self._inputEdit.setText(text)

    def setButtons(self, buttons):
        """
        Set the buttons to be displayed in message box.

        :type buttons: QMessageBox.StandardButton
        :rtype: None 
        """
        for button in buttons:
            if isinstance(button, list) or isinstance(button, tuple):
                self.buttonBox().addButton(button[0], button[1])
            else:
                self.buttonBox().addButton(button)

    def setHeaderColor(self, color):
        """
        Set the header color for the message box.

        :type color: str
        :rtype: None 
        """
        self.header().setStyleSheet("background-color:" + color)

    def setPixmap(self, pixmap):
        """
        Set the pixmap for the message box.

        :type pixmap: QWidgets.QPixmap
        :rtype: None 
        """
        self._icon.setPixmap(pixmap)
        self._icon.show()

    def _clicked(self, button):
        """
        Triggered when the user clicks a button.
        
        :type button: QWidgets.QPushButton
        :rtype: None 
        """
        self._clickedButton = button
        self._clickedStandardButton = self.buttonBox().standardButton(button)

    def clickedIndex(self):
        """
        Return the button that was clicked by its index.
        
        :rtype: int or None
        """
        for i, button in enumerate(self.buttonBox().buttons()):
            if button == self.clickedButton():
                return i

    def clickedButton(self):
        """
        Return the button that was clicked.
        
        :rtype: QtWidgets.QPushButton or None 
        """
        return self._clickedButton

    def clickedText(self):
        return self._clickedButton.text()

    def clickedStandardButton(self):
        """
        Return the button that was clicked by the user.

        :rtype: QMessageBox.StandardButton or None
        """
        return self._clickedStandardButton

    def isDontShowCheckboxChecked(self):
        """
        Return the checked state of the Dont show again checkbox.
        
        :rtype: bool 
        """
        if self._dontShowCheckbox:
            return self._dontShowCheckbox.isChecked()
        else:
            return False

    def exec_(self):
        """
        Shows the dialog as a modal dialog
        
        :rtype: int or None
        """
        QtWidgets.QDialog.exec_(self)
        return self.clickedIndex()


def testMessageBox():

    with studioqt.app():

        title = "Test question dialog"
        text = "Would you like to create a snapshot icon?"

        buttons = [
            QtWidgets.QDialogButtonBox.Yes,
            QtWidgets.QDialogButtonBox.Ignore,
            QtWidgets.QDialogButtonBox.Cancel
        ]

        result = MessageBox.question(None, title, text, buttons=buttons)
        print(result)

        title = "Test long text message"
        text = "This is to test a very long message. " \
               "This is to test a very long message. " \
               "This is to test a very long message. " \
               "This is to test a very long message. " \
               "This is to test a very long message. "

        buttons = [
            QtWidgets.QDialogButtonBox.Yes,
            QtWidgets.QDialogButtonBox.Ignore,
            QtWidgets.QDialogButtonBox.Cancel
        ]

        result = MessageBox.question(None, title, text, buttons=buttons)
        print(result)

        title = "Test checkbox"
        text = "Testing the don't show check box. "

        buttons = [
            QtWidgets.QDialogButtonBox.Ok,
            QtWidgets.QDialogButtonBox.Cancel
        ]

        print(studiolibrary.widgets.MessageBox.input(
            None,
            u"重命名",
            u"重命名所选项目?",
            inputText="face.anim",
        ))

        result = MessageBox.question(
            None,
            title,
            text,
            buttons=buttons,
            enableDontShowCheckBox=True
        )
        print(result)

        title = u"创建缩略图"
        text = u"这将会覆盖现有缩略图. " \
               u"确认覆盖现有缩略图么?"

        buttons = [
            QtWidgets.QDialogButtonBox.Yes,
            QtWidgets.QDialogButtonBox.No
        ]

        result = MessageBox.warning(
            None,
            title,
            text,
            buttons=buttons,
            enableDontShowCheckBox=True
        )
        print(result)

        title = u"错误!"
        text = u"保存项目时发生错误."
        result = MessageBox.critical(None, title, text)
        print(result)

        if result == QtWidgets.QDialogButtonBox.Yes:
            title = u"错误!"
            text = u"保存时出现错误"
            MessageBox.critical(None, title, text)


if __name__ == "__main__":
    testMessageBox()
