# -*- coding: utf-8 -*-

# Copyright (c) 2013 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a specialized error message dialog.
"""

from __future__ import unicode_literals

from PyQt5.QtCore import qInstallMessageHandler, QtDebugMsg, QtWarningMsg, \
    QtCriticalMsg, QtFatalMsg, QThread, QMetaObject, Qt, Q_ARG, QSettings
from PyQt5.QtWidgets import QErrorMessage, qApp, QDialog

import Globals
import Utilities


_msgHandlerDialog = None
_origMsgHandler = None

_filterSettings = QSettings(
    QSettings.IniFormat,
    QSettings.UserScope,
    Globals.settingsNameOrganization,
    "eric6messagefilters")
_defaultFilters = [
    "QFont::",
    "QCocoaMenu::removeMenuItem",
    "QCocoaMenu::insertNative",
    ",type id:",
    "Remote debugging server started successfully",
    "Uncaught SecurityError:",
    "Content Security Policy",
    "QXcbClipboard:",
    "QXcbConnection: XCB error",
    "libpng warning: iCCP:",
]


def filterMessage(message):
    """
    Module function to filter messages.
    
    @param message message to be checked
    @type str
    @return flag indicating that the message should be filtered out
    @rtype bool
    """
    for filterStr in Globals.toList(_filterSettings.value(
            "MessageFilters", _defaultFilters)):
        if filterStr in message:
            return True
    
    return False


class E5ErrorMessage(QErrorMessage):
    """
    Class implementing a specialized error message dialog.
    """
    def __init__(self, parent=None):
        """
        Constructor
        
        @param parent reference to the parent widget
        @type QWidget
        """
        super(E5ErrorMessage, self).__init__(parent)
    
    def showMessage(self, message, msgType=""):
        """
        Public method to show a message.
        
        @param message error message to be shown
        @type str
        @param msgType type of the error message
        @type str
        """
        if not filterMessage(message):
            if msgType:
                super(E5ErrorMessage, self).showMessage(message, msgType)
            else:
                super(E5ErrorMessage, self).showMessage(message)
    
    def editMessageFilters(self):
        """
        Public method to edit the list of message filters.
        """
        from .E5ErrorMessageFilterDialog import E5ErrorMessageFilterDialog
        dlg = E5ErrorMessageFilterDialog(
            Globals.toList(_filterSettings.value(
                "MessageFilters", _defaultFilters)),
            _defaultFilters)
        if dlg.exec_() == QDialog.Accepted:
            filters = dlg.getFilters()
            _filterSettings.setValue("MessageFilters", filters)


def messageHandler(msgType, *args):
    """
    Module function handling messages.
    
    @param msgType type of the message
    @type  int, QtMsgType
    @param args message handler arguments, for PyQt4 message to be shown, for
        PyQt5 context information and message to be shown
    @type PyQt4: bytes; PyQt5: tuple of (QMessageLogContext, bytes)
    """
    if len(args) == 2:
        context = args[0]
        message = args[1]
    else:
        message = args[0]
    if _msgHandlerDialog:
        try:
            if msgType == QtDebugMsg:
                messageType = "Debug Message:"
            elif msgType == QtWarningMsg:
                messageType = "Warning:"
            elif msgType == QtCriticalMsg:
                messageType = "Critical:"
            elif msgType == QtFatalMsg:
                messageType = "Fatal Error:"
            if isinstance(message, bytes):
                message = Utilities.decodeBytes(message)
            if filterMessage(message):
                return
            message = message.replace("\r\n", "<br/>")\
                             .replace("\n", "<br/>")\
                             .replace("\r", "<br/>")
            if len(args) == 2 and context.file is not None:
                msg = "<p><b>{0}</b></p><p>{1}</p><p>File: {2}</p>" \
                    "<p>Line: {3}</p><p>Function: {4}</p>".format(
                        messageType, Utilities.html_uencode(message),
                        context.file, context.line, context.function)
            else:
                msg = "<p><b>{0}</b></p><p>{1}</p>".format(
                    messageType, Utilities.html_uencode(message))
            if QThread.currentThread() == qApp.thread():
                _msgHandlerDialog.showMessage(msg)
            else:
                QMetaObject.invokeMethod(
                    _msgHandlerDialog,
                    "showMessage",
                    Qt.QueuedConnection,
                    Q_ARG(str, msg))
            return
        except RuntimeError:
            pass
    elif _origMsgHandler:
        _origMsgHandler(msgType, message)
        return
    
    if msgType == QtDebugMsg:
        messageType = "Debug Message"
    elif msgType == QtWarningMsg:
        messageType = "Warning"
    elif msgType == QtCriticalMsg:
        messageType = "Critical"
    elif msgType == QtFatalMsg:
        messageType = "Fatal Error"
    if isinstance(message, bytes):
        message = message.decode()
    if len(args) == 2:
        print("{0}: {1} in {2} at line {3} ({4})".format(
            messageType, message, context.file, context.line,
            context.function))
    else:
        print("{0}: {1}".format(messageType, message))


def qtHandler():
    """
    Module function to install an E5ErrorMessage dialog as the global
    message handler.
    
    @return reference to the message handler dialog
    @rtype E5ErrorMessage
    """
    global _msgHandlerDialog, _origMsgHandler
    
    if _msgHandlerDialog is None:
        # Install an E5ErrorMessage dialog as the global message handler.
        _msgHandlerDialog = E5ErrorMessage()
        _origMsgHandler = qInstallMessageHandler(messageHandler)
    
    return _msgHandlerDialog


def editMessageFilters():
    """
    Module function to edit the list of message filters.
    """
    if _msgHandlerDialog:
        _msgHandlerDialog.editMessageFilters()
    else:
        print("No message handler installed.")


def messageHandlerInstalled():
    """
    Module function to check, if a message handler was installed.
    
    @return flag indicating an installed message handler
    @rtype bool
    """
    return _msgHandlerDialog is not None

#
# eflag: noqa = M801
