#!/usr/bin/env python
# -*- mode:python; tab-width: 2; coding: utf-8 -*-

"""
Helper

QML Helper functions
"""

from __future__ import absolute_import

__author__  = "Carlos Martín"
__license__ = "See LICENSE file for details"

# Import here any required modules
import sys

# QML required deps
from PySide.QtCore import QObject, Signal, Property

__all__ = ['qproperty', 'Generator', 'Proxy', ]

# Project requirements

# local submodule requirements


#pylint: disable-msg=C0103,W0142,W0212
class qproperty(property):
    """A property like class decorator for QProperty"""
    def __init__(self, qtype, fget=None, *args, **kwrgs):
        def __clean_kw(kwargs, invalid=("no_notify", "notify",)):
            """removes unsuported args from kwargs"""
            [kwargs.pop(key, None) for key in invalid]
            return kwargs
        # store args
        self._pkwrgs = __clean_kw(dict(kwrgs))
        self._qkwrgs = kwrgs
        super(qproperty, self).__init__(fget, *args, **self._pkwrgs)
        # store qtype
        self._qtype = qtype
        self.__update(fget.__name__ if fget else None)

    def __get__(self, instance, stype=None):
        return getattr(instance, self._qname)

    def __set__(self, instance, value):
        setattr(instance, self._qname, value)
        getattr(instance, self._sname).emit()

    def __call__(self, fget):
        self.__update(fget.__name__)
        cls_ns = sys._getframe(1).f_locals
        cls_ns[self._qname] = Property(self._qtype, fget, **self._pkwrgs)
        return qproperty(self._qtype, fget, **self._qkwrgs)

    #pylint: disable-msg=W0201
    def __update(self, name):
        """Update property, qml property and signal notify"""
        if name is not None:
            self._pname = name
            self._qname = "qml_" + name
            self._sname = name + "_signal"

    def setter(self, fset):
        """Detter decorator"""
        cls_ns = sys._getframe(1).f_locals
        if not self._qkwrgs.pop("no_notify", False):
            cls_ns[self._sname] = self._qkwrgs.setdefault("notify", Signal())
        cls_ns[self._qname] = Property(        \
            self._qtype, self.fget, fset, **self._qkwrgs)
        return qproperty(self._qtype, self.fget, fset, **self._pkwrgs)


def Bridge(*defs, **values):
    """A QObject Generator Class"""

    #pylint: disable-msg=R0904
    class PropertyBridge(QObject):
        """Autogenerated Class"""

        def __init__(self):
            QObject.__init__(self)
            for key, val in defs:
                setattr(self, '_' + key, values.get(key, val()))

        def __repr__(self):
            name = values.get('name', 'QObject')
            func = lambda x: '%s=%r' % (x, getattr(self, '_' + x))
            vals = (func(key) for key, val in defs)
            return '<%s (%s)>' % (name, ', '.join(vals))

        #pylint: disable-msg=C0111,E0213
        for key, value in defs:
            fnotify = locals()['_nfy_' + key] = Signal()

            def _get(key):
                def f(self):
                    return self.__dict__['_' + key]
                return f

            def _set(key):
                def f(self, value):
                    setattr(self, '_' + key, value)
                    getattr(self, '_nfy_' + key).emit()
                return f

            fset = locals()['_set_' + key] = _set(key)
            fget = locals()['_get_' + key] = _get(key)
            locals()[key] = Property(value, fget, fset, notify=fnotify)

    return PropertyBridge


#pylint: disable-msg=R0903
class QProxy(object):
    """
    Proxy for accessing properties and slots as attributes

    This class acts as a proxy for the object for which it is created,
    and makes property access more Pythonic while still allowing
    access to slots (as member functions).

    Attribute names starting with '_' are not proxied.
    """

    def __init__(self, root):
        metao = root.metaObject()
        props = range(metao.propertyCount())
        self._root = root
        self._props = [metao.property(i).name() for i in props]

    def __getattr__(self, key):
        value = self._root.property(key)
        # No such property, so assume we call a slot
        if value is None and key not in self._props:
            return getattr(self._root, key)
        return value

    def __setattr__(self, key, value):
        if key.startswith('_'):
            object.__setattr__(self, key, value)
        else:
            self._root.setProperty(key, value)
