#!/usr/bin/python3
# -*- coding: utf-8 -*-

#  Copyright © 2016-2017  B. Clausius <barcc@gmx.de>
#
#  This program is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 3 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program.  If not, see <http://www.gnu.org/licenses/>.

try:
    from PyQt5.QtCore import (pyqtProperty, pyqtSignal, pyqtSlot, Qt, QObject, QVariant, QUrl, QStandardPaths,
                            QElapsedTimer, QFileSystemWatcher, QTimer, QByteArray,
                            QLocale, QTranslator, QLibraryInfo, QMetaObject, QPointF)
    from PyQt5.QtGui import (QColor, QImage, QIcon, QKeySequence, QTextDocumentFragment, QSurfaceFormat,
                            QOpenGLTexture, QOpenGLFramebufferObject, QTransform, QPixmap, QCursor,
                            QOpenGLContext, QOpenGLDebugLogger, QIconEngine)
    from PyQt5.QtQml import QQmlEngine, QQmlComponent
    from PyQt5.QtWidgets import QApplication, QMainWindow, QLineEdit, QLabel, QOpenGLWidget, QDialog, QStyledItemDelegate
    from PyQt5.QtQuick import QQuickView, QQuickItem
except ImportError as e:
    print('Need PyQt5 in pure python mode:', e)
    raise SystemExit(1)
    
import OpenGL.GL as gl

NULL = None
Format_RGBA8888 = QImage.Format_RGBA8888
PortableText = QKeySequence.PortableText
MoveToNextWord = QKeySequence.MoveToNextWord
MoveToPreviousWord = QKeySequence.MoveToPreviousWord
fromString = QKeySequence.fromString
standardLocations = QStandardPaths.standardLocations
PicturesLocation = QStandardPaths.PicturesLocation
HomeLocation = QStandardPaths.HomeLocation
fromPlainText = QTextDocumentFragment.fromPlainText
setDefaultFormat = QSurfaceFormat.setDefaultFormat
defaultFormat = QSurfaceFormat.defaultFormat
Target2D = QOpenGLTexture.Target2D
RGBA32F = QOpenGLTexture.RGBA32F
Linear = QOpenGLTexture.Linear
Depth = QOpenGLFramebufferObject.Depth
fromLocalFile = QUrl.fromLocalFile
Asynchronous = QQmlComponent.Asynchronous
fromImage = QPixmap.fromImage
location = QLibraryInfo.location
TranslationsPath = QLibraryInfo.TranslationsPath
QLocale_system = QLocale.system
arguments = QApplication.arguments
setAttribute = QApplication.setAttribute
OpenGLES = QSurfaceFormat.OpenGLES
DebugContext = QSurfaceFormat.DebugContext
openGLModuleType = QOpenGLContext.openGLModuleType
LibGL = QOpenGLContext.LibGL
LibGLES = QOpenGLContext.LibGLES
currentContext = QOpenGLContext.currentContext
SynchronousLogging = QOpenGLDebugLogger.SynchronousLogging
invokeMethod = QMetaObject.invokeMethod
SizeRootObjectToView = QQuickView.SizeRootObjectToView
GL_TEXTURE_2D = gl.GL_TEXTURE_2D


class MetaSlot(type(QObject)):
    def __new__(cls, name, bases, namespace):
        import inspect
        qtypemap = {'QString': str, 'QList<QObject*>': QVariant, 'QVariantList': QVariant}
        def member(proptype, propname, notify):
            def getter(self):
                try:
                    returnv = getattr(self, propname)
                except AttributeError:
                    returnv = proptype()
                return returnv
            def setter(self, v):
                setattr(self, propname, v)
                notify and notify.__get__(self).emit()
            return getter, setter
            
        for k, v in sorted(namespace.items()):
            if k.startswith('slot_'):
                spec = inspect.getfullargspec(v)
                argtypes = [spec.annotations[a] for a in spec.args[1:]]
                rtype = spec.annotations.get('return')
                fname = k.split('_', 1)[1]
                if rtype is None:
                    slot = pyqtSlot(*argtypes, name=fname)
                else:
                    slot = pyqtSlot(*argtypes, name=fname, result=rtype)
                assert fname not in namespace
                namespace[fname] = slot(v)
                del namespace[k]
            elif k.startswith('action_'):
                action = k.split('_', 1)[1]
                fname = 'on_action_%s_triggered' % action
                slot = pyqtSlot(name=fname)
                assert fname not in namespace
                def _mkactionfunc(action, pydata):
                    def on_action_X_triggered(self):
                        getattr(pydata.app, 'on_action_%s_triggered' % action)()
                    return on_action_X_triggered
                namespace[fname] = slot(_mkactionfunc(action, v))
                del namespace[k]
            elif k.startswith('prop_'):
                pname = k.split('_', 1)[1]
                ptype, *flags = v.split()
                try:
                    ptype = qtypemap.get(ptype) or eval(ptype.rstrip('*'))
                except NameError:
                    print('{}: name={}, type={!r}, flags={!r}'.format(k, pname, ptype, flags))
                    raise
                assert type(ptype) is not str or ptype.isalpha(), ptype
                pargs = {}
                getter = setter = notify = None
                def pop(flag):
                    if flag in flags:
                        flags.remove(flag)
                        return True
                    return False
                if pop('NOTIFY'):
                    fname = pname + '_changed'
                    assert fname not in namespace, fname
                    notify = pyqtSignal()
                    pargs['notify'] = notify
                    namespace[fname] = notify
                if pop('MEMBER'):
                    fname = 'm_' + pname
                    assert fname not in namespace, fname
                    getter, setter = member(ptype, fname, notify)
                    pargs['fget'], pargs['fset'] = getter, setter
                if pop('READ'):
                    fname = 'get_' + pname
                    assert fname in namespace, fname
                    getter = namespace[fname]
                    pargs['fget'] = getter
                if pop('WRITE'):
                    fname = 'set_' + pname
                    assert fname in namespace, fname
                    setter = namespace[fname]
                    pargs['fset'] = setter
                if flags:
                    assert False, flags
                assert pname not in namespace, pname
                namespace[pname] = pyqtProperty(ptype, **pargs)
                del namespace[k]
        return type(QObject)(name, bases, namespace)
        
    

