# coding:utf-8
from enum import Enum

from PySide6.QtCore import Qt, QSize, QRectF, QPoint
from PySide6.QtGui import QPainter, QPainterPath, QColor
from PySide6.QtWidgets import (QSpinBox, QDoubleSpinBox, QToolButton, QHBoxLayout,
                               QDateEdit, QDateTimeEdit, QTimeEdit, QVBoxLayout, QApplication)

from ...common.style_sheet import FluentStyleSheet, themeColor, isDarkTheme
from ...common.icon import FluentIconBase, Theme, getIconColor
from ...common.font import setFont
from ...common.color import FluentSystemColor, autoFallbackThemeColor
from .button import TransparentToolButton
from .line_edit import LineEditMenu
from .flyout import Flyout, FlyoutViewBase, FlyoutAnimationType


class SpinIcon(FluentIconBase, Enum):
    """ Spin icon """

    UP = "Up"
    DOWN = "Down"

    def path(self, theme=Theme.AUTO):
        return f':/qfluentwidgets/images/spin_box/{self.value}_{getIconColor(theme)}.svg'



class SpinButton(QToolButton):

    def __init__(self, icon: SpinIcon, parent=None):
        super().__init__(parent=parent)
        self.isPressed = False
        self._icon = icon
        self.setFixedSize(31, 23)
        self.setIconSize(QSize(10, 10))
        FluentStyleSheet.SPIN_BOX.apply(self)

    def mousePressEvent(self, e):
        self.isPressed = True
        super().mousePressEvent(e)

    def mouseReleaseEvent(self, e):
        self.isPressed = False
        super().mouseReleaseEvent(e)

    def paintEvent(self, e):
        super().paintEvent(e)
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing |
                               QPainter.SmoothPixmapTransform)

        if not self.isEnabled():
            painter.setOpacity(0.36)
        elif self.isPressed:
            painter.setOpacity(0.7)

        self._icon.render(painter, QRectF(10, 6.5, 11, 11))


class CompactSpinButton(QToolButton):
    """ Compact spin button """

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.setFixedSize(26, 33)
        self.setCursor(Qt.IBeamCursor)

    def paintEvent(self, e):
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)

        x = (self.width() - 10) / 2
        s = 9

        SpinIcon.UP.render(painter, QRectF(x, self.height() / 2 - s + 1, s, s))
        SpinIcon.DOWN.render(painter, QRectF(x, self.height() / 2 , s, s))


class SpinFlyoutView(FlyoutViewBase):
    """ Spin flyout view """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.upButton = TransparentToolButton(SpinIcon.UP, self)
        self.downButton = TransparentToolButton(SpinIcon.DOWN, self)
        self.vBoxLayout = QVBoxLayout(self)

        self.upButton.setFixedSize(36, 36)
        self.downButton.setFixedSize(36, 36)
        self.upButton.setIconSize(QSize(13, 13))
        self.downButton.setIconSize(QSize(13, 13))

        self.vBoxLayout.setContentsMargins(6, 6, 6, 6)
        self.vBoxLayout.setSpacing(0)
        self.vBoxLayout.addWidget(self.upButton)
        self.vBoxLayout.addWidget(self.downButton)

    def paintEvent(self, e):
        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)

        painter.setBrush(
            QColor(46, 46, 46) if isDarkTheme() else QColor(249, 249, 249))
        painter.setPen(
            QColor(0, 0, 0, 51) if isDarkTheme() else QColor(0, 0, 0, 15))

        rect = self.rect().adjusted(1, 1, -1, -1)
        painter.drawRoundedRect(rect, 8, 8)


class SpinBoxBase:
    """ Spin box ui """

    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self._isError = False
        self.lightFocusedBorderColor = QColor()
        self.darkFocusedBorderColor = QColor()

        self.hBoxLayout = QHBoxLayout(self)

        self.setProperty('transparent', True)
        FluentStyleSheet.SPIN_BOX.apply(self)
        self.setButtonSymbols(QSpinBox.NoButtons)
        self.setFixedHeight(33)
        setFont(self)

        self.setAttribute(Qt.WA_MacShowFocusRect, False)
        self.setContextMenuPolicy(Qt.CustomContextMenu)
        self.customContextMenuRequested.connect(self._showContextMenu)

    def isError(self):
        return self._isError

    def setError(self, isError: bool):
        """ set the error status """
        if isError == self.isError():
            return

        self._isError = isError
        self.update()

    def setReadOnly(self, isReadOnly: bool):
        super().setReadOnly(isReadOnly)
        self.setSymbolVisible(not isReadOnly)

    def setSymbolVisible(self, isVisible: bool):
        """ set whether the spin symbol is visible """
        self.setProperty("symbolVisible", isVisible)
        self.setStyle(QApplication.style())

    def setCustomFocusedBorderColor(self, light, dark):
        """ set the border color in focused status

        Parameters
        ----------
        light, dark: str | QColor | Qt.GlobalColor
            border color in light/dark theme mode
        """
        self.lightFocusedBorderColor = QColor(light)
        self.darkFocusedBorderColor = QColor(dark)
        self.update()

    def focusedBorderColor(self):
        if self.isError():
            return FluentSystemColor.CRITICAL_FOREGROUND.color()

        return autoFallbackThemeColor(self.lightFocusedBorderColor, self.darkFocusedBorderColor)

    def _showContextMenu(self, pos):
        menu = LineEditMenu(self.lineEdit())
        menu.exec_(self.mapToGlobal(pos))

    def _drawBorderBottom(self):
        if not self.hasFocus():
            return

        painter = QPainter(self)
        painter.setRenderHints(QPainter.Antialiasing)
        painter.setPen(Qt.NoPen)

        path = QPainterPath()
        w, h = self.width(), self.height()
        path.addRoundedRect(QRectF(0, h-10, w, 10), 5, 5)

        rectPath = QPainterPath()
        rectPath.addRect(0, h-10, w, 8)
        path = path.subtracted(rectPath)

        painter.fillPath(path, self.focusedBorderColor())

    def paintEvent(self, e):
        super().paintEvent(e)
        self._drawBorderBottom()


class InlineSpinBoxBase(SpinBoxBase):
    """ Inline spin box base """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.upButton = SpinButton(SpinIcon.UP, self)
        self.downButton = SpinButton(SpinIcon.DOWN, self)

        self.hBoxLayout.setContentsMargins(0, 4, 4, 4)
        self.hBoxLayout.setSpacing(5)
        self.hBoxLayout.addWidget(self.upButton, 0, Qt.AlignRight)
        self.hBoxLayout.addWidget(self.downButton, 0, Qt.AlignRight)
        self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        self.upButton.clicked.connect(self.stepUp)
        self.downButton.clicked.connect(self.stepDown)

    def setSymbolVisible(self, isVisible: bool):
        super().setSymbolVisible(isVisible)
        self.upButton.setVisible(isVisible)
        self.downButton.setVisible(isVisible)

    def setAccelerated(self, on: bool):
        super().setAccelerated(on)
        self.upButton.setAutoRepeat(on)
        self.downButton.setAutoRepeat(on)


class CompactSpinBoxBase(SpinBoxBase):
    """ Compact spin box base """

    def __init__(self, parent=None):
        super().__init__(parent)
        self.compactSpinButton = CompactSpinButton(self)
        self.spinFlyoutView = SpinFlyoutView(self)
        self.spinFlyout = Flyout(self.spinFlyoutView, self, False)

        self.hBoxLayout.setContentsMargins(0, 0, 0, 0)
        self.hBoxLayout.addWidget(self.compactSpinButton, 0, Qt.AlignRight)
        self.hBoxLayout.setAlignment(Qt.AlignRight | Qt.AlignVCenter)

        self.compactSpinButton.clicked.connect(self._showFlyout)
        self.spinFlyoutView.upButton.clicked.connect(self.stepUp)
        self.spinFlyoutView.downButton.clicked.connect(self.stepDown)

        self.spinFlyout.hide()

    def setAccelerated(self, on: bool):
        super().setAccelerated(on)
        self.spinFlyoutView.upButton.setAutoRepeat(on)
        self.spinFlyoutView.downButton.setAutoRepeat(on)

    def focusInEvent(self, e):
        super().focusInEvent(e)
        self._showFlyout()

    def setSymbolVisible(self, isVisible: bool):
        super().setSymbolVisible(isVisible)
        self.compactSpinButton.setVisible(isVisible)

    def _showFlyout(self):
        if self.spinFlyout.isVisible() or self.isReadOnly():
            return

        y = int(self.compactSpinButton.height() / 2 - 46)
        pos = self.compactSpinButton.mapToGlobal(QPoint(-12, y))

        self.spinFlyout.exec(pos, FlyoutAnimationType.FADE_IN)


class SpinBox(InlineSpinBoxBase, QSpinBox):
    """ Spin box """


class CompactSpinBox(CompactSpinBoxBase, QSpinBox):
    """ Compact spin box """


class DoubleSpinBox(InlineSpinBoxBase, QDoubleSpinBox):
    """ Double spin box """


class CompactDoubleSpinBox(CompactSpinBoxBase, QDoubleSpinBox):
    """ Compact double spin box """


class TimeEdit(InlineSpinBoxBase, QTimeEdit):
    """ Time edit """


class CompactTimeEdit(CompactSpinBoxBase, QTimeEdit):
    """ Compact time edit """


class DateTimeEdit(InlineSpinBoxBase, QDateTimeEdit):
    """ Date time edit """


class CompactDateTimeEdit(CompactSpinBoxBase, QDateTimeEdit):
    """ Compact date time edit """


class DateEdit(InlineSpinBoxBase, QDateEdit):
    """ Date edit """


class CompactDateEdit(CompactSpinBoxBase, QDateEdit):
    """ Compact date edit """

