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

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

"""
Module to get the object name, class name or signatures of a Qt form (*.ui).
"""

import sys
import json
import xml.etree.ElementTree

try:
    from PyQt5.QtCore import QMetaMethod, QByteArray
    from PyQt5.QtWidgets import QAction, QWidget, QApplication
    from PyQt5 import uic
except ImportError:
    print("PyQt5 could not be found.")
    sys.exit(1)


def objectName(formFile, projectPath):
    """
    Function to get the object name of a form.
    
    @param formFile file name of the form
    @type str
    @param projectPath directory name of the project
    @type str
    """
    app = QApplication([])      # __IGNORE_WARNING__
    try:
        dlg = uic.loadUi(formFile, package=projectPath)
        print(dlg.objectName())
        sys.exit(0)
    except (AttributeError, ImportError,
            xml.etree.ElementTree.ParseError) as err:
        print(str(err))
        sys.exit(1)


def className(formFile, projectPath):
    """
    Function to get the class name of a form.
    
    @param formFile file name of the form
    @type str
    @param projectPath directory name of the project
    @type str
    """
    app = QApplication([])      # __IGNORE_WARNING__
    try:
        dlg = uic.loadUi(formFile, package=projectPath)
        print(dlg.metaObject().className())
        sys.exit(0)
    except (AttributeError, ImportError,
            xml.etree.ElementTree.ParseError) as err:
        print(str(err))
        sys.exit(1)


def __mapType(type_):
    """
    Private function to map a type as reported by Qt's meta object to the
    correct Python type.
    
    @param type_ type as reported by Qt
    @type QByteArray or bytes
    @return mapped Python type
    @rtype str
    """
    mapped = bytes(type_).decode()
    
    # I. always check for *
    mapped = mapped.replace("*", "")
    
    # 1. check for const
    mapped = mapped.replace("const ", "")
    
    # 2. replace QString and QStringList
    mapped = (
        mapped
        .replace("QStringList", "list")
        .replace("QString", "str")
    )
    
    # 3. replace double by float
    mapped = mapped.replace("double", "float")
    
    return mapped


def signatures(formFile, projectPath):
    """
    Function to get the signatures of form elements.
    
    @param formFile file name of the form
    @type str
    @param projectPath directory name of the project
    @type str
    """
    objectsList = []
    
    app = QApplication([])      # __IGNORE_WARNING__
    try:
        dlg = uic.loadUi(formFile, package=projectPath)
        objects = dlg.findChildren(QWidget) + dlg.findChildren(QAction)
        for obj in objects:
            name = obj.objectName()
            if not name or name.startswith("qt_"):
                # ignore un-named or internal objects
                continue
            
            metaObject = obj.metaObject()
            objectDict = {
                "name": name,
                "class_name": metaObject.className(),
                "methods": [],
            }
            
            for index in range(metaObject.methodCount()):
                metaMethod = metaObject.method(index)
                if metaMethod.methodType() == QMetaMethod.Signal:
                    signatureDict = {
                        "methods": []
                    }
                    signatureDict["signature"] = "on_{0}_{1}".format(
                        name,
                        bytes(metaMethod.methodSignature()).decode()
                    )
                    
                    signatureDict["methods"].append("on_{0}_{1}".format(
                        name,
                        bytes(metaMethod.methodSignature())
                        .decode().split("(")[0]
                    ))
                    signatureDict["methods"].append("{0}({1})".format(
                        signatureDict["methods"][-1],
                        ", ".join([
                            __mapType(t)
                            for t in metaMethod.parameterTypes()
                        ])
                    ))
                    
                    returnType = __mapType(
                        metaMethod.typeName().encode())
                    if returnType == 'void':
                        returnType = ""
                    signatureDict["return_type"] = returnType
                    parameterTypesList = [
                        __mapType(t)
                        for t in metaMethod.parameterTypes()
                    ]
                    signatureDict["parameter_types"] = parameterTypesList
                    pyqtSignature = ", ".join(parameterTypesList)
                    signatureDict["pyqt_signature"] = pyqtSignature
                    
                    parameterNames = metaMethod.parameterNames()
                    if parameterNames:
                        for index in range(len(parameterNames)):
                            if not parameterNames[index]:
                                parameterNames[index] = QByteArray(
                                    "p{0:d}".format(index).encode("utf-8")
                                )
                    parameterNamesList = [bytes(n).decode()
                                          for n in parameterNames]
                    signatureDict["parameter_names"] = parameterNamesList
                    methNamesSig = ", ".join(parameterNamesList)
                    
                    if methNamesSig:
                        pythonSignature = "on_{0}_{1}(self, {2})".format(
                            name,
                            bytes(metaMethod.methodSignature())
                            .decode().split("(")[0],
                            methNamesSig)
                    else:
                        pythonSignature = "on_{0}_{1}(self)".format(
                            name,
                            bytes(metaMethod.methodSignature())
                            .decode().split("(")[0])
                    signatureDict["python_signature"] = pythonSignature
                    
                    objectDict["methods"].append(signatureDict)
            
            objectsList.append(objectDict)
        
        print(json.dumps(objectsList))
        sys.exit(0)
    except (AttributeError, ImportError,
            xml.etree.ElementTree.ParseError) as err:
        print(str(err))
        sys.exit(1)


if __name__ == "__main__":
    if len(sys.argv) != 4:
        print("Wrong number of arguments.")
        sys.exit(1)
    
    if sys.argv[1] == "object_name":
        objectName(sys.argv[2], sys.argv[3])
    elif sys.argv[1] == "class_name":
        className(sys.argv[2], sys.argv[3])
    elif sys.argv[1] == "signatures":
        signatures(sys.argv[2], sys.argv[3])
    else:
        print("Unknow operation given.")
        sys.exit(1)
    
#
# eflag: noqa = M701, M801
