# coding:utf-8
from qtpy.QtCore import QEasingCurve, QPropertyAnimation, Qt, QEvent, Signal, QRect
from qtpy.QtGui import QColor, QResizeEvent, QPalette, QPaintEvent, QPainter, QMouseEvent
from qtpy.QtWidgets import (QDialog, QGraphicsDropShadowEffect, QStyleOption, QStyle, QApplication,
                             QGraphicsOpacityEffect, QWidget, QGraphicsBlurEffect, QLabel)

from Custom_Widgets.Utils import is_in_designer
from Custom_Widgets.components.python.ui_dialog import Ui_Form
from Custom_Widgets.QCustomTheme import QCustomTheme
from Custom_Widgets.QCustomComponentLoader import QCustomComponentLoader

# from Custom_Widgets.BlurWindow import GlobalBlur Deprecated due to OS security policies
from Custom_Widgets.AcrylicEffect import AcrylicEffect
from Custom_Widgets.Log import *

class QCustomQDialog(QDialog, Ui_Form):
    accepted = Signal()
    rejected = Signal()
    
    title = None
    description = None
    # Add some padding if needed
    padding = 0
    margin = 60 #for drop shadow effect
    borderRadius = 0
    windowMovable = False
    animationDuration = 500
    
    maskWidget = None
    hasBlur = False

    def __init__(
        self,
        parent=None,
        showForm=None,
        title=None,
        description=None,
        padding=0,
        yesButtonIcon=None,
        cancelButtonIcon=None,
        yesButtonText="Yes",
        cancelButtonText="Cancel",
        animationDuration=300,
        showYesButton=True,
        showCancelButton=True,
        setModal=False,
        frameless=False,
        windowMovable=False,
        addWidget=None,
        blurBackground=True,
        position="center"  # Added position parameter
    ):
        """Initialize the custom dialog with configurable options."""
        QDialog.__init__(self, parent)  # Initialize QDialog parent class
        Ui_Form.__init__(self)  # Initialize Ui_Form parent class

        self.window().installEventFilter(self)

        self.setupUi(self)

        # Mask widget setup
        self.maskWidget = None
        self.acrylicEffect = None
        
        # Store position
        self.position = position.lower() if position else "center"

        # Set title
        if title:
            self.titleLabel.setText(str(title))
        else:
            self.titleLabel.hide()

        # Set description
        if description:
            self.descriptionLabel.setText(str(description))
        else:
            self.descriptionLabel.hide()

        # Set padding
        self.padding = padding

        # Set button icons and text
        if yesButtonIcon:
            self.yesButton.setIcon(yesButtonIcon)
        self.yesButton.setText(yesButtonText)

        if cancelButtonIcon:
            self.cancelButton.setIcon(cancelButtonIcon)
        self.cancelButton.setText(cancelButtonText)

        # Set animation duration
        self.animationDuration = animationDuration

        # Show or hide buttons
        if not showYesButton:
            self.hideYesButton()

        if not showCancelButton:
            self.hideCancelButton()

        # Set modal behavior
        self.setModal(setModal)

        # Frameless and movable settings
        if frameless:
            self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
            self.setAttribute(Qt.WA_TranslucentBackground)

            if windowMovable:
                self.setMovable(True)
                self.titleBar.mousePressEvent = self.mousePressEvent
                self.titleBar.mouseMoveEvent = self.mouseMoveEvent
                self.titleBar.mouseReleaseEvent = self.mouseReleaseEvent
            else:
                self.setMovable(False)


        self.shownForm = None       
        if showForm:
            self.form = QCustomComponentLoader()
            self.form.loadComponent(formClass = showForm)
            # self.form.setObjectName("showForm")
            self.verticalLayout_2.addWidget(self.form, alignment=self._getQtAlignmentFromPosition()) 

            try:
                #older versions
                self.form.form =  self.form.ui 
                self.shownForm =  self.form.ui  
            except:
                self.shownForm = None
        
        if addWidget:
            self.addWidget(addWidget)

        self.yesButton.clicked.connect(self.__onYesButtonClicked)
        self.cancelButton.clicked.connect(self.__onCancelButtonClicked)
        self.yesButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        self.cancelButton.setAttribute(Qt.WA_LayoutUsesWidgetRect)
        
        self.yesButton.setFocus()
        self.setShadowEffect()

        self.blurBackground = blurBackground           
     
    def _getQtAlignmentFromPosition(self):
        """Convert position string to Qt alignment flags."""
        position = self.position.lower()
        
        # Map position strings to Qt alignment flags
        alignments = {
            "center": Qt.AlignCenter,
            "top": Qt.AlignTop | Qt.AlignHCenter,
            "bottom": Qt.AlignBottom | Qt.AlignHCenter,
            "left": Qt.AlignLeft | Qt.AlignVCenter,
            "right": Qt.AlignRight | Qt.AlignVCenter,
            "top-left": Qt.AlignTop | Qt.AlignLeft,
            "top-right": Qt.AlignTop | Qt.AlignRight,
            "bottom-left": Qt.AlignBottom | Qt.AlignLeft,
            "bottom-right": Qt.AlignBottom | Qt.AlignRight,
        }
        
        return alignments.get(position, Qt.AlignCenter)

    def addWidget(self, widget, alignment = None):
        if alignment:
            self.verticalLayout_2.addWidget(widget, alignment=alignment) 
        else:
            self.verticalLayout_2.addWidget(widget) 
    
    def setShadowEffect(self, blurRadius=60, offset=(0, 10), color=QColor(0,0,0,100)):
        shadowEffect = QGraphicsDropShadowEffect(self.widget)
        shadowEffect.setBlurRadius(blurRadius)
        shadowEffect.setOffset(*offset)
        shadowEffect.setColor(color)
        # self.widget.setGraphicsEffect(None)
        self.widget.setGraphicsEffect(shadowEffect)
            
    def setMovable(self, movable: bool):
        self.windowMovable = movable

    def mousePressEvent(self, event: QMouseEvent):
        if event.button() == Qt.LeftButton:
            self.offset = event.pos()
            

    def mouseMoveEvent(self, event: QMouseEvent):
        if hasattr(self, 'offset'):
            if self.windowFlags() & Qt.FramelessWindowHint and self.windowMovable:
                self.move(self.pos() + event.pos() - self.offset)

    def mouseReleaseEvent(self, event: QMouseEvent):
        if hasattr(self, 'offset'):
            del self.offset

    def __onCancelButtonClicked(self):
        self.reject()
        # self.rejected.emit()

    def __onYesButtonClicked(self):
        self.accept()
        # self.accepted.emit()

    def hideYesButton(self):
        self.yesButton.hide()

    def hideCancelButton(self):
        self.cancelButton.hide()
    
    def paintEvent(self, e: QPaintEvent):
        painter = QPainter(self)
        if not painter.isActive():
            return

        painter.setRenderHints(QPainter.Antialiasing)
        painter.setPen(Qt.NoPen)
        rect = self.rect().adjusted(1, 1, -1, -1)
        painter.drawRoundedRect(rect, 6, 6)

        # self.adjustSizeToContent()
        # painter.end()
        super().paintEvent(e)

        self.adjustSizeToContent()
        
    def showEvent(self, e):
        self.checkAppTheme()
        c = 0 if self.isDark() else 255
        if self.maskWidget is None:
            self.maskWidget = MaskWidget(parent=self.parent(), dialog=self)
            # self.maskWidget.lower()
            self.maskWidget.updateAcrylicEffect()
        
        if not self.blurBackground:
            self.maskWidget.setStyleSheet(f'background:rgba({c}, {c}, {c}, .8)')
        # else:
        #     self.maskWidget.setStyleSheet(f'background:rgba({c}, {c}, {c}, .1)')
        
        self.maskWidget.show()

        """ Fade in """
        opacityEffect = QGraphicsOpacityEffect(self)
        self.setGraphicsEffect(opacityEffect)
        opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
        opacityAni.setStartValue(0)
        opacityAni.setEndValue(1)
        opacityAni.setDuration(self.animationDuration)
        opacityAni.setEasingCurve(QEasingCurve.InSine)
        opacityAni.finished.connect(opacityEffect.deleteLater)
        opacityAni.start()
        
        super().showEvent(e)

            
    def hideEvent(self, e):
        super().hideEvent(e)
        try:
            self.maskWidget.hide()
            self.maskWidget.deleteLater()
            self.acrylicEffect = None
            self.maskWidget = None
        except:
            pass

    def done(self, code):
        """ fade out """
        self.setGraphicsEffect(None)
        opacityEffect = QGraphicsOpacityEffect(self)
        self.setGraphicsEffect(opacityEffect)
        opacityAni = QPropertyAnimation(opacityEffect, b'opacity', self)
        opacityAni.setStartValue(.9)
        opacityAni.setEndValue(0)
        opacityAni.setDuration(self.animationDuration)
        opacityAni.setEasingCurve(QEasingCurve.OutSine)
        opacityAni.finished.connect(lambda: QDialog.done(self, code))
        opacityAni.start()
        
        if self.maskWidget is not None:
            self.maskWidget.hide()
            self.maskWidget.deleteLater()
            self.maskWidget = None

    def resizeEvent(self, e):
        self.adjustSizeToContent()
        super().resizeEvent(e)

    def adjustSizeToContent(self):
        # # Calculate the size hint based on the content
        # self.verticalLayout.setContentsMargins(self.margin, self.margin, self.margin, self.margin)
        # content_size = self.layout().sizeHint()
        # self.setMinimumSize(content_size.width() + self.padding, content_size.height() + self.padding)
        # self.adjustSize()

        # Size maximized to support BG Blur
        self.adjustToParentSize()

        self.checkAppTheme()
        

    def adjustToParentSize(self):
        """Adjust the dialog to the size and position of the parent."""
        if self.parent():
            # Resize dialog to parent size
            self.resize(self.parent().size())
            
            # Position the dialog based on the position parameter
            parent_rect = self.parent().geometry()
            dialog_rect = self.geometry()
            
            if self.position == "center":
                # Center position (default)
                dialog_x = parent_rect.x() + (parent_rect.width() - dialog_rect.width()) // 2
                dialog_y = parent_rect.y() + (parent_rect.height() - dialog_rect.height()) // 2
            
            elif self.position == "top":
                # Top center
                dialog_x = parent_rect.x() + (parent_rect.width() - dialog_rect.width()) // 2
                dialog_y = parent_rect.y()
            
            elif self.position == "bottom":
                # Bottom center
                dialog_x = parent_rect.x() + (parent_rect.width() - dialog_rect.width()) // 2
                dialog_y = parent_rect.y() + parent_rect.height() - dialog_rect.height()
            
            elif self.position == "left":
                # Left center
                dialog_x = parent_rect.x()
                dialog_y = parent_rect.y() + (parent_rect.height() - dialog_rect.height()) // 2
            
            elif self.position == "right":
                # Right center
                dialog_x = parent_rect.x() + parent_rect.width() - dialog_rect.width()
                dialog_y = parent_rect.y() + (parent_rect.height() - dialog_rect.height()) // 2
            
            elif self.position == "top-left":
                # Top left corner
                dialog_x = parent_rect.x()
                dialog_y = parent_rect.y()
            
            elif self.position == "top-right":
                # Top right corner
                dialog_x = parent_rect.x() + parent_rect.width() - dialog_rect.width()
                dialog_y = parent_rect.y()
            
            elif self.position == "bottom-left":
                # Bottom left corner
                dialog_x = parent_rect.x()
                dialog_y = parent_rect.y() + parent_rect.height() - dialog_rect.height()
            
            elif self.position == "bottom-right":
                # Bottom right corner
                dialog_x = parent_rect.x() + parent_rect.width() - dialog_rect.width()
                dialog_y = parent_rect.y() + parent_rect.height() - dialog_rect.height()
            
            else:
                # Default to center if position is not recognized
                dialog_x = parent_rect.x() + (parent_rect.width() - dialog_rect.width()) // 2
                dialog_y = parent_rect.y() + (parent_rect.height() - dialog_rect.height()) // 2
            
            self.move(dialog_x, dialog_y)

    def checkAppTheme(self):
        # icons
        if self.shownForm:
            if self.form.defaultTheme != QCustomTheme().theme:
                # theme changed
                self.form.applyThemeIcons()

    def eventFilter(self, obj, e: QEvent):
        if obj is self.window():
            if e.type() in {QEvent.Resize, QEvent.Move}:
                self.adjustToParentSize()

        return super().eventFilter(obj, e)
    
    def isDark(self):
        palette = self.palette()
        background_color = palette.color(QPalette.Window)
        # Calculate the luminance of the background color
        luminance = 0.2126 * background_color.red() + 0.7152 * background_color.green() + 0.0722 * background_color.blue()

        # Determine if the background color is dark or light
        if luminance < 128:
            # Dark background
            return True
        else:
            # Light background
            return False

        
class MaskWidget(QWidget):
    def __init__(self, parent=None, dialog: QCustomQDialog = None):
        super().__init__(parent)
        
        self.dialog = dialog
        # Make the widget fill the entire main window
        self.setWindowFlags(Qt.FramelessWindowHint)
        self.setAttribute(Qt.WA_TranslucentBackground)
        self.setGeometry(self.parent().geometry() if parent else QApplication.primaryScreen().geometry())
        
                
        self.hide()
        
        if parent:
            parent.installEventFilter(self)
            parent.resizeEvent = self.resizeEvent
            parent.moveEvent = self.moveEvent
            
    def resizeEvent(self, event):
        try:
            if self.parent():
                parent_width = self.parent().width()
                parent_height = self.parent().height()
                self.setGeometry(0, 0, parent_width, parent_height)
        except:
            pass

    def moveEvent(self, event):
        try:
            if self.parent():
                parent_width = self.parent().width()
                parent_height = self.parent().height()
                self.setGeometry(0, 0, parent_width, parent_height)
        except:
            pass

    def eventFilter(self, obj, event):
        if obj == self.parent():
            if event.type() == QEvent.Resize:
                parent_width = self.parent().width()
                parent_height = self.parent().height()
                self.setGeometry(0, 0, parent_width, parent_height)
        return super().eventFilter(obj, event)
    
    def paintEvent(self, e: QPaintEvent):
        opt = QStyleOption()
        opt.initFrom(self)
        painter = QPainter(self)
        if not painter.isActive():
            return
        painter.setRenderHint(QPainter.Antialiasing)
        self.style().drawPrimitive(QStyle.PE_Widget, opt, painter, self)
        painter.end()
        super().paintEvent(e)
        
    def deleteLater(self):
        try:
            if self.parent():
                self.parent().destroyed.disconnect(self.onParentDestroyed)
            super().deleteLater()
        except:
            pass

    def onParentDestroyed(self):
        self.deleteLater()

    def showEvent(self, event):
        if self.parent():
            self.parent().destroyed.connect(self.onParentDestroyed)

        self.screenGrab()
        super().showEvent(event)

    def updateAcrylicEffect(self):
        if is_in_designer(self):
            return
        
        try:
            # Create new acrylic effect if enabled
            if self.dialog.blurBackground:
                self.acrylicEffect = AcrylicEffect(
                    widget=self,
                    blurRadius=10,
                    tintColor=QColor(255, 255, 255, 30) if not self.dialog.isDark() else QColor(0, 0, 0, 30),
                    luminosityColor=QColor(255, 255, 255, 10) if not self.dialog.isDark() else QColor(0, 0, 0, 10),
                    noiseOpacity=0.03
                )
                
                # Apply the acrylic effect
                self.acrylicEffect.applyToWidget()

                self.screenGrab()
        except Exception as e:
            logError(f"Error applying acrylic effect: {e}")
            
        
    def screenGrab(self):
        if is_in_designer(self):
            return None

        if self.parent():
            global_rect = self.parent().rect()
            self.acrylicEffect.grabFromScreen(global_rect)
