"""
Module with functions wich can bes used in the application
"""

import re

from PyQt5.QtCore import *
from PyQt5.QtGui import *
from PyQt5.QtWidgets import *

class QLineEditAnimated(QLineEdit):
    """Customized QLineEdit Class with the ability to trigger a blink animation for rejected Inputs"""
    def __init__(self, default: str):
        """Customized QLineEdit Class with the ability to trigger a blink animation for rejected Inputs"""

        QLineEdit.__init__(self, default)
        self.co_get = 0
        self.co_set = 0

        byar = QByteArray()
        byar.append('customColor')
        blinkAnimation = QPropertyAnimation(self, byar)
        blinkAnimation.setStartValue(QColor(255, 255, 255,))
        blinkAnimation.setKeyValueAt(0.2, QColor(255, 150, 150))
        blinkAnimation.setEndValue(QColor(255, 255, 255))
        blinkAnimation.setDuration(1500)
        blinkAnimation.setLoopCount(1)

        self.errorAnimation = blinkAnimation

    def parseStyleSheet(self):
        ss = self.styleSheet()
        sts = [s.strip() for s in ss.split(';') if len(s.strip())]
        return sts

    def getBackColor(self):
        self.co_get += 1
        # print(fuin(), self.co_get)
        return self.palette().color(self.pal_ele)

    def setBackColor(self, color):
        self.co_set += 1
        sss = self.parseStyleSheet()
        bg_new = 'background-color: rgba(%d,%d,%d,%d);' % (color.red(), color.green(), color.blue(), color.alpha())

        for k, sty in enumerate(sss):
            if re.search('\Abackground-color:', sty):
                sss[k] = bg_new
                break
        else:
            sss.append(bg_new)

        self.setStyleSheet('; '.join(sss))

    pal_ele = QPalette.Window
    customColor = pyqtProperty(QColor, getBackColor, setBackColor)


def lockedEditLine(default: str) -> QLabel:
    """Returns QLabel with styling as a locked edit Line"""
    # replaces . with , to show all numbers with ,
    editLine = QLabel(default.replace(".", ","))
    editLine.setStyleSheet("background-color: #F8F8F8; color: #555555; padding: 3px")
    return editLine

def doubleEditLine(default: str, bottom: float, top: float, decimals: int, 
    statusBar: QStatusBar = None, statusBarMessage: str = "") -> QLineEditAnimated:
    """Returns QLineEdit for double as inputs, rejects any other inputs. Limits for the Validator must be set"""
    # replaces . with , to show all numbers with ,
    editLine = QLineEditAnimated(default.replace(".", ","))
    editLine.setReadOnly(False)
    editLine.setValidator(QDoubleValidator(bottom, top, decimals))
    editLine.textChanged.connect(lambda: editLine.setText(editLine.text().replace(".", ",")))
    editLine.inputRejected.connect(lambda: handleRejectedInput(editLine, statusBar, statusBarMessage))
    return editLine

def intEditLine(default: str, bottom: float, top: float, 
    statusBar: QStatusBar = None, statusBarMessage: str = "") -> QLineEditAnimated:
    """Returns QLineEdit for integers as inputs, rejects any other inputs. Limits for the Validator must be set"""
    editLine = QLineEditAnimated(default)
    editLine.setReadOnly(False)
    editLine.setValidator(QIntValidator(bottom, top))
    locale = QLocale()
    locale.setNumberOptions(QLocale.RejectGroupSeparator)
    editLine.validator().setLocale(locale)
    editLine.inputRejected.connect(lambda: handleRejectedInput(editLine, statusBar, statusBarMessage))
    return editLine

def stringEditLine(default: str, onlyCaptial: bool = False, pattern: str = "[a-zA-Z0-9_\s\-]+",
    statusBar: QStatusBar = None, statusBarMessage: str = "") -> QLineEditAnimated:
    """Returns QLineEdit for strings as inputs, rejects any other inputs. Pattern for Regex Validation can be set"""
    editLine = QLineEditAnimated(default)
    editLine.setReadOnly(False)
    editLine.setValidator(QRegularExpressionValidator(QRegularExpression(pattern)))

    # handling for only capital letters
    if onlyCaptial:
        editLine.textChanged.connect(lambda: editLine.setText(editLine.text().upper()))

    editLine.inputRejected.connect(lambda: handleRejectedInput(editLine, statusBar, statusBarMessage))

    return editLine


def handleRejectedInput(editLine: QLineEditAnimated, statusBar: QStatusBar, errorMessage: str) -> str:
    """Handling for Rejected Input, shows Error in Status Bar and change apperance of input field"""

    if not statusBar == None:
        statusBar.showMessage(errorMessage, 3000)

    editLine.errorAnimation.start()

    return

def textToFloat(string: str) -> float:
    """Converts the Text to a float if possible"""
    if string == "":
        return ""

    string = string.replace(",", ".")
    
    try:
        return float(string)
    except ValueError:
        return ""

def textToInt(string: str) -> int:
    """Converts the Text to a int if possible"""
    if string == "":
        return ""

    string = string.replace(",", ".")
    
    try:
        return int(string)
    except ValueError:
        return ""

def isDictComplete(thisDict) -> bool:
    """check if a dict has empty values"""
    for (key ,value) in thisDict.items():
        if isinstance(value, dict):
            if not isDictComplete(value):
                return False
        elif value == "":
            return False

    return True

def showErrorMessage(mainWindow: QMainWindow, errorTitle: str, errorText: str) -> None:
    """Shows a Error Message, title and Message must be set"""
    errorHandling = QMessageBox(mainWindow)
    errorHandling.setWindowTitle(errorTitle)
    errorHandling.setText(errorText)
    errorHandling.setIcon(QMessageBox.Warning)
    errorHandling.setStandardButtons(QMessageBox.Ok)
    errorHandling.exec()

def mergeDicts(prevDict: dict, newDict: dict) -> dict:
    """copy the newDict into the prevDict"""
    for (key, value) in newDict.items():
        if isinstance(value, dict):
            prevDict[key] = mergeDicts(prevDict[key], value)
        else:
            prevDict.update({key: value})

    return prevDict

class QPushButtonCustom(QPushButton):
    """QPushButton with two font sizes"""

    def __init__(self, mainText: str, subText: str):
        """Create QPushButton with a mainText an detailed subText, both must be set"""
        QPushButton.__init__(self)

        self.mainText = mainText
        self.subText = subText

    

    def paintEvent(self, a0: QPaintEvent) -> None:
        """Paint Custom Button with mainText and subText"""
        QPushButton.paintEvent(self, a0)
        painterBig = QPainter(self)
        fontBig = self.font()
        fontBig.setPointSize(14)
        fontBig.setBold(True)
        painterBig.setFont(fontBig)

        painterSmall = QPainter(self)
        fontSmall = self.font()
        fontSmall.setPointSize(9)
        painterSmall.setFont(fontSmall)


        painterBig.drawText(
            QPoint(
                self.__getCenterPosWidth(painterBig, self.mainText),
                self.__getCenterPosHeight(painterBig, self.mainText) - 15
            ), 
            self.mainText)
        painterSmall.drawText(
            QPoint(
                self.__getCenterPosWidth(painterSmall, self.subText), 
                self.__getCenterPosHeight(painterSmall, self.subText) + 15
            ), 
            self.subText)

        return

    def __getCenterPosWidth(self, painter: QPainter, text: str) -> int:
        """Return position in X to center the text"""
        # calc offset to center text
        buttonCenter = self.width() / 2
        fontMetrics = QFontMetrics(painter.font())
        xOffset = fontMetrics.boundingRect(text).width() / 2

        return int(buttonCenter - xOffset)

    def __getCenterPosHeight(self, painter: QPainter, text: str) -> int:
        """Return position in y to center the text"""
        # calc offset to center text
        buttonCenter = self.height() / 2
        fontMetrics = QFontMetrics(painter.font())
        yOffset = fontMetrics.boundingRect(text).height() / 2

        return int(buttonCenter + yOffset)



        

