# -*- coding: utf-8 -*-
#
# Copyright 2016 Continuum Analytics, Inc.
# May be copied and distributed freely only as part of an Anaconda or
# Miniconda installation.
#

"""
"""

# Third party imports
from qtpy.QtCore import QPropertyAnimation, Qt, Signal, QRectF, QPoint
from qtpy.QtGui import (QPainter, QPainterPath, QRegion, QBrush, QPen)
from qtpy.QtWidgets import (QDialogButtonBox, QDialog, QFrame,
                            QGraphicsDropShadowEffect, QGraphicsOpacityEffect,
                            QHBoxLayout, QLabel, QPushButton,
                            QTextEdit, QVBoxLayout)

# Local imports
from anaconda_navigator.widgets import ButtonCancel
from anaconda_navigator.utils.styles import load_style_sheet


class FrameDialogBody(QFrame):
    pass


class FrameDialogTitleBar(QFrame):
    pass


class LabelDialogTitleBar(QLabel):
    pass


class ButtonDialogClose(QPushButton):
    pass


class FadingDialog(QDialog):
    """A general fade in/fade out QDialog with some builtin functions"""
    sig_key_pressed = Signal()

    def __init__(self, parent, opacity, duration, easing_curve):
        super(FadingDialog, self).__init__(parent)

        self.parent = parent
        self.opacity_min = min(opacity)
        self.opacity_max = max(opacity)
        self.duration_fadein = duration[0]
        self.duration_fadeout = duration[-1]
        self.easing_curve_in = easing_curve[0]
        self.easing_curve_out = easing_curve[-1]
        self.effect = None
        self.anim = None

        self._fade_running = False
        self._funcs_before_fade_in = []
        self._funcs_after_fade_in = []
        self._funcs_before_fade_out = []
        self._funcs_after_fade_out = []

        self.setModal(False)

    def _run(self, funcs):
        """ """
        for func in funcs:
            func()

    def _run_before_fade_in(self):
        """ """
        self._run(self._funcs_before_fade_in)

    def _run_after_fade_in(self):
        """ """
        self._run(self._funcs_after_fade_in)

    def _run_before_fade_out(self):
        """ """
        self._run(self._funcs_before_fade_out)

    def _run_after_fade_out(self):
        """ """
        self._run(self._funcs_after_fade_out)

    def _set_fade_finished(self):
        """ """
        self._fade_running = False

    def _fade_setup(self):
        """ """
        self._fade_running = True
        self.effect = QGraphicsOpacityEffect(self)
        self.setGraphicsEffect(self.effect)
        self.anim = QPropertyAnimation(self.effect, "opacity")

    # --- public api
    def fade_in(self, on_finished_connect):
        """ """
        self._run_before_fade_in()
        self._fade_setup()
        self.show()
        self.raise_()
        self.anim.setEasingCurve(self.easing_curve_in)
        self.anim.setStartValue(self.opacity_min)
        self.anim.setEndValue(self.opacity_max)
        self.anim.setDuration(self.duration_fadein)
        self.anim.finished.connect(on_finished_connect)
        self.anim.finished.connect(self._set_fade_finished)
        self.anim.finished.connect(self._run_after_fade_in)
        self.anim.start()

    def fade_out(self, on_finished_connect):
        """ """
        self._run_before_fade_out()
        self._fade_setup()
        self.anim.setEasingCurve(self.easing_curve_out)
        self.anim.setStartValue(self.opacity_max)
        self.anim.setEndValue(self.opacity_min)
        self.anim.setDuration(self.duration_fadeout)
        self.anim.finished.connect(on_finished_connect)
        self.anim.finished.connect(self._set_fade_finished)
        self.anim.finished.connect(self._run_after_fade_out)
        self.anim.start()

    def is_fade_running(self):
        """ """
        return self._fade_running

    def set_funcs_before_fade_in(self, funcs):
        """ """
        self._funcs_before_fade_in = funcs

    def set_funcs_after_fade_in(self, funcs):
        """ """
        self._funcs_after_fade_in = funcs

    def set_funcs_before_fade_out(self, funcs):
        """ """
        self._funcs_before_fade_out = funcs

    def set_funcs_after_fade_out(self, funcs):
        """ """
        self._funcs_after_fade_out = funcs


class FadingCanvas(FadingDialog):
    """The black semi transparent canvas that covers the application"""
    def __init__(self, parent, opacity, duration, easing_curve, color):
        super(FadingCanvas, self).__init__(parent, opacity, duration,
                                           easing_curve)
        self.parent = parent

        self.color = color              # Canvas color
        self.color_decoration = Qt.red  # Decoration color
        self.stroke_decoration = 2      # width in pixels for decoration

        self.region_mask = None
        self.region_subtract = None
        self.region_decoration = None

        self.widgets = None             # The widget to uncover
        self.decoration = None          # The widget to draw decoration
        self.interaction_on = False

        self.path_current = None
        self.path_subtract = None
        self.path_full = None
        self.path_decoration = None

        # widget setup
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setAttribute(Qt.WA_TransparentForMouseEvents)
        self.setModal(False)
        self.setFocusPolicy(Qt.NoFocus)

        self.set_funcs_before_fade_in([self.update_canvas])
        self.set_funcs_after_fade_out([lambda: self.update_widgets(None),
                                       lambda: self.update_decoration(None)])

    def set_interaction(self, value):
        """ """
        self.interaction_on = value

    def update_canvas(self):
        """ """
        w, h = self.parent.size().width(), self.parent.size().height()

        self.path_full = QPainterPath()
        self.path_subtract = QPainterPath()
        self.path_decoration = QPainterPath()
        self.region_mask = QRegion(0, 0, w, h)

        self.path_full.addRect(0, 0, w, h)

        # Add the path
        if self.widgets is not None:
            for widget in self.widgets:
                temp_path = QPainterPath()
                # if widget is not found... find more general way to handle
                if widget is not None:
                    widget.raise_()
                    widget.show()
                    geo = widget.frameGeometry()
                    width, height = geo.width(), geo.height()
                    point = widget.mapTo(self.parent, QPoint(0, 0))
                    x, y = point.x(), point.y()

                    temp_path.addRect(QRectF(x, y, width, height))

                    temp_region = QRegion(x, y, width, height)

                if self.interaction_on:
                    self.region_mask = self.region_mask.subtracted(temp_region)
                self.path_subtract = self.path_subtract.united(temp_path)

            self.path_current = self.path_full.subtracted(self.path_subtract)
        else:
            self.path_current = self.path_full

        if self.decoration is not None:
            for widget in self.decoration:
                temp_path = QPainterPath()
                widget.raise_()
                widget.show()
                geo = widget.frameGeometry()
                width, height = geo.width(), geo.height()
                point = widget.mapTo(self.parent, QPoint(0, 0))
                x, y = point.x(), point.y()
                temp_path.addRect(QRectF(x, y, width, height))

                temp_region_1 = QRegion(x-1, y-1, width+2, height+2)
                temp_region_2 = QRegion(x+1, y+1, width-2, height-2)
                temp_region = temp_region_1.subtracted(temp_region_2)

                if self.interaction_on:
                    self.region_mask = self.region_mask.united(temp_region)

                self.path_decoration = self.path_decoration.united(temp_path)
        else:
            self.path_decoration.addRect(0, 0, 0, 0)

        # Add a decoration stroke around widget
        self.setMask(self.region_mask)
        self.update()
        self.repaint()

    def update_widgets(self, widgets):
        """ """
        self.widgets = widgets

    def update_decoration(self, widgets):
        """ """
        self.decoration = widgets

    def paintEvent(self, event):
        """Override Qt method"""
        painter = QPainter(self)
        painter.setRenderHint(QPainter.Antialiasing)
        # Decoration
        painter.fillPath(self.path_current, QBrush(self.color))
        painter.strokePath(self.path_decoration, QPen(self.color_decoration,
                                                      self.stroke_decoration))
#        decoration_fill = QColor(self.color_decoration)
#        decoration_fill.setAlphaF(0.25)
#        painter.fillPath(self.path_decoration, decoration_fill)

    def reject(self):
        """Override Qt method"""
        if not self.is_fade_running():
            key = Qt.Key_Escape
            self.key_pressed = key
            self.sig_key_pressed.emit()

    def mousePressEvent(self, event):
        """Override Qt method"""
        pass


class DialogBase(QDialog):
    def __init__(self, *args, **kwargs):
        super(DialogBase, self).__init__(*args, **kwargs)

        # Widgets
        self.frame_title_bar = FrameDialogTitleBar()
        self.frame_body = FrameDialogBody()
        self.button_close_dialog = ButtonDialogClose('X')
        self.label_title_bar = LabelDialogTitleBar('Title')

        # Widget setup
        shadow = QGraphicsDropShadowEffect()
        shadow.setBlurRadius(10)
        shadow.setOffset(5, 5)

        self.setGraphicsEffect(shadow)
        self.setWindowFlags(Qt.Dialog)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
        self.button_close_dialog.setFocusPolicy(Qt.NoFocus)

        # Signals
        self.button_close_dialog.clicked.connect(self.reject)

        self.update_style_sheet()

    def setWindowTitle(self, title):
        """
        Qt override.
        """
        self.label_title_bar.setText(title)

    def setLayout(self, body_layout):
        """
        Qt override.
        """
        title_layout = QHBoxLayout()
        title_layout.addWidget(self.label_title_bar)
        title_layout.addStretch(0)
        title_layout.addWidget(self.button_close_dialog)
        title_layout.setContentsMargins(0, 0, 0, 0)
        self.frame_title_bar.setLayout(title_layout)

        self.frame_body.setLayout(body_layout)

        layout = QVBoxLayout()
        layout.addWidget(self.frame_title_bar)
        layout.addWidget(self.frame_body)
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        super(DialogBase, self).setLayout(layout)

    def update_style_sheet(self, style_sheet=None):
        if style_sheet is None:
            style_sheet = load_style_sheet()
        self.setStyleSheet(style_sheet)


class ActionsDialog(DialogBase):
    def __init__(self, text, parent=None):
        super(ActionsDialog, self).__init__(parent=parent)

        self.label = QLabel(text)
        self.bbox = QDialogButtonBox()
        self.button_accept = QPushButton('Ok')
        self.button_cancel = ButtonCancel('Cancel')

        self.bbox.addButton(self.button_accept, QDialogButtonBox.AcceptRole)
        self.bbox.addButton(self.button_cancel, QDialogButtonBox.RejectRole)
        self.setWindowTitle('Proceed with the following actions?')

        layout = QVBoxLayout()
        layout.addWidget(self.label)
        layout.addSpacing(5)
        layout.addWidget(self.bbox)
        self.setLayout(layout)

        self.button_accept.clicked.connect(self.accept)
        self.button_cancel.clicked.connect(self.reject)


class MessageBox(DialogBase):
    QuestionBox = 100
    InformationBox = 101
    ErrorBox = 102

    def __init__(self, type_, error='', title='', text=''):
        super(MessageBox, self).__init__()
        self.label_text = QLabel(text)
        self.textbox_error = QTextEdit()
        self.button_ok = QPushButton('Ok')
        self.button_yes = QPushButton('Yes')
        self.button_no = ButtonCancel('No')
        self.button_copy = QPushButton('Copy text')
        self.bbox = QDialogButtonBox(Qt.Horizontal)

        self.textbox_error.setReadOnly(True)
        self.textbox_error.setFrameStyle(QTextEdit.Plain)
        self.textbox_error.setFrameShape(QTextEdit.NoFrame)
        self.setMinimumWidth(260)
        self.textbox_error.verticalScrollBar().show()
        self.setWindowTitle(title)

        error = error.split('\n')
        error = '<br>'.join(error)
        self.textbox_error.setText(error)

        # Layouts
        layout = QVBoxLayout()
        layout.addWidget(self.label_text)
        layout.addSpacing(24)
        layout.addWidget(self.textbox_error)
        layout.addWidget(self.button_copy)
        layout.addSpacing(24)
        layout.addWidget(self.bbox)
        self.setLayout(layout)

        # Signals
        self.button_copy.clicked.connect(self.copy_text)
        self.button_ok.clicked.connect(self.accept)
        self.button_yes.clicked.connect(self.accept)
        self.button_no.clicked.connect(self.reject)

        # Setup
        if type_ == self.ErrorBox:
            print('Error')
            self.bbox.addButton(self.button_ok, QDialogButtonBox.AcceptRole)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
        elif type_ == self.InformationBox:
            print('Info')
            self.bbox.addButton(self.button_ok, QDialogButtonBox.AcceptRole)
            self.button_yes.setVisible(False)
            self.button_no.setVisible(False)
            self.textbox_error.setVisible(False)
            self.button_copy.setVisible(False)
        elif type_ == self.QuestionBox:
            print('Question')
            self.bbox.addButton(self.button_yes, QDialogButtonBox.AcceptRole)
            self.bbox.addButton(self.button_no, QDialogButtonBox.RejectRole)
            self.textbox_error.setVisible(False)
            self.button_ok.setVisible(False)
            self.button_copy.setVisible(False)

    def copy_text(self):
        """
        """
        self.textbox_error.selectAll()
        self.textbox_error.copy()


class MessageBoxQuestion(MessageBox):
    def __init__(self, text='', title=''):
        super(MessageBoxQuestion, self).__init__(text=text, title=title,
                                                 type_=self.QuestionBox)


class MessageBoxInformation(MessageBox):
    def __init__(self, text='', error='', title=''):
        super(MessageBoxInformation, self).__init__(text=text, title=title,
                                                    type_=self.InformationBox)


class MessageBoxError(MessageBox):
    def __init__(self, text='', error='', title=''):
        super(MessageBoxError, self).__init__(text=text, title=title,
                                              error=error, type_=self.ErrorBox)


def test():
    from anaconda_navigator.utils.qthelpers import qapplication
    app = qapplication(test_time=3)
    dialog = MessageBoxInformation(text='SomeRandomText')
    dialog = MessageBoxError(text='SomeRandomText', error='Some error')
    dialog.update_style_sheet()
    dialog.show()
    app.exec_()


if __name__ == '__main__':
    test()
