/**
  This file presents Verdigris, a "fork" of CopperSpice.

  CopperSpice is a fork of Qt 4 whose specificity is to get rid of moc (Qt Meta-object compiler)
  In order to get rid of moc, they changed the Qt macros to be less optimal.
  They made a complete, incompatible fork of Qt.

  Verdigris is not a fork of CopperSpice, but rather a re-implementation of their macro in a way
  that is binary compatible with Qt. You can write your application without needing moc, and still
  use it with existing Qt 5 releases.

  CopperSpice generates the metaobjects at run-time. But moc generates them at compile time.
  Verdigris uses constexpr to generate QMetaObject at compile time.
  It is using C++14 for simplicity because it is much more easy to handle constexpr.
  The code is originally taken from my previous work
  https://woboq.com/blog/reflection-in-cpp-and-qt-moc.html
  In that experiment, I was trying to use same macros that Qt does (keeping source compatibility).
  When using more complex but uglier macros (CopperSpice style) we can do it without the moc.

 */


/** ******************************************************************************************** **/
/** INTRODUCTION **/

// In a header file you would include the wobjectdefs.h header
// This is equivalent to the qobjectdefs.h header
#include <wobjectdefs.h>
// And because you will inherit from QObject you also need to QObject header
#include <QObject>

// Now declare your class:
class MyObject : public QObject
{
    /** The W_OBJECT macro is equivalent to the Q_OBJECT macro. The difference is that it must
       contains the class name as a parameter and need to be put before any other W_ macro in the
       class. So it's the same as the CS_OBJECT macro from CopperSpice. */
    W_OBJECT(MyObject)

public /* slots */:

    // Here we declare a slot:
    void mySlot(const QString &name) { qDebug("hello %s", qPrintable(name)); }
    /* If you're going to use the new connection syntax, no need to do anything else for slots.
       But if you want to use the other connection syntax, or QML, you need to register the slot
       just like so: */
    W_SLOT(mySlot)
    /* The W_SLOT has optional arguments that we will see later. It is already much simpler than
       the two CopperSpice macros: CS_SLOT_1 and CS_SLOT_2. Also, CopperSpice slots cannot be
       declared inline in the class definition. */

public /* signals */:

    // Now a signal:
    void mySignal(const QString &name)
    W_SIGNAL(mySignal, name)
    /* Note the absence of semi colon after the signal declaration */
};

/* Here is what would go in the C++ .cpp file: */
#include <wobjectimpl.h>

// And now this is the macro you need to instantiate the meta object.
// It's an additional macro that basically does the same as the code generated by moc.
W_OBJECT_IMPL(MyObject)

// That's it! MyObject is a QObject that can be used in QML or connected.
void aaa(MyObject *obj1) {
    bool ok = true;
    // new syntax
    ok = ok && QObject::connect(obj1, &MyObject::mySignal, obj1, &MyObject::mySlot);
    // old syntax
    ok = ok && QObject::connect(obj1, SIGNAL(mySignal(QString)), obj1, SLOT(mySlot(QString)));
    Q_ASSERT(ok);
}


/** ******************************************************************************************** **/
/** SLOTS **/
class SlotTutorial : public QObject {
    W_OBJECT(SlotTutorial)

    /**
       W_SLOT( <slot name> [, (<parameters types>) ]  [, <flags>]* )

       The W_SLOT macro needs to be put after the slot declaration.
       The W_SLOT macro can have the W_Compat for deprecated methods
       (equivalent of Q_MOC_COMPAT

       The W_SLOT macro can optionally have a list of parameter types as second
       argument to disambiguate or declare types.
     */

    /* Examples: */
protected:
    // Declares a protected slot
    void protectedSlot() {}
    W_SLOT(protectedSlot)
private:
    // and a private slot
    void privateSlot() {}
    W_SLOT(privateSlot)

public:
    // Overloaded function needs a parameter list as second argument of the macro
    // to disambiguate
    void overload() {}
    W_SLOT(overload, ())

    void overload(int) {}
    W_SLOT(overload, (int))
private:
    void overload(double) {}
    W_SLOT(overload, (double))
    void overload(int, int) {}
    W_SLOT(overload, (int, int))
    // Note: for custom type that are not const reference, one must use the normalized signature
};

W_OBJECT_IMPL(SlotTutorial)


/** ******************************************************************************************** **/
/** SIGNALS **/

class SignalTutorial : public QObject {
    W_OBJECT(SignalTutorial)

    /**
       <signal signature>
       W_SIGNAL( <signal name> [, (<parameter types>) ] , <parameter names> )

       Unlike W_SLOT, W_SIGNAL must be placed directly after the signal signature declaration.
       There should not be a semi colon after the signal signature.
     */

public:
    // Example:
    void sig1(int a , int b)
    W_SIGNAL(sig1, a, b)

    // Or on the same line
    void sig2(int a, int b) W_SIGNAL(sig2, a, b)

    // For overloaded signals:
    void overload(int a, int b)
    W_SIGNAL(overload, (int, int), a, b)
};

W_OBJECT_IMPL(SignalTutorial)


/** ******************************************************************************************** **/
/** Gadgets, invokable, constructor **/


class InvokableTutorial  {
    // Just like Qt has Q_GADGET, here we have W_GADGET
    W_GADGET(InvokableTutorial)

public:
    /** W_INVOKABLE is the same as W_SLOT.
     * It can take another flag (W_Scriptable) which corresponds to Q_SCRIPTABLE */
    void myInvokable() {}
    W_INVOKABLE(myInvokable)


    /** W_CONSTRUCTOR(<parameter types>)
        for Q_INVOKABLE constructor, just pass the parameter types to this macro.
        one can have W_CONSTRUCTOR() for the default constructor even if it is implicit
     */

    InvokableTutorial(int, int) {}
    W_CONSTRUCTOR(int, int)

    InvokableTutorial(void*, void* =nullptr) {}
    W_CONSTRUCTOR(void*, void*)
    // Because of the default argument we can also do that:  (only in this macro)
    W_CONSTRUCTOR(void*)
};

// For gadget there is also a different IMPL macro
W_GADGET_IMPL(InvokableTutorial)

/** ******************************************************************************************** **/
/** PROPERTY **/

#include <QtCore/QMap>

class PropertyTutorial  : public QObject {
    W_OBJECT(PropertyTutorial)

public:
    /** W_PROPERTY(<type>, <name> [, <flags>]*)

        There are the macro READ WRITE MEMBER and so on which have been defined so
        you can just add a comma after the type, just like in a Q_PROPERTY.

        W_PROPERTY need to be put after all the setters, getters, signals and members
        have been declared.
    */

    QString m_value;
    QString value() const { return m_value; }
    void setValue(const QString &value) {
        m_value = value;
        emit valueChanged();
    }
    void valueChanged()
    W_SIGNAL(valueChanged)

    // Just like in Qt only with one additional comma after the type
    W_PROPERTY(QString, prop1 READ value WRITE setValue NOTIFY valueChanged)

    // Is equivalent to:
    W_PROPERTY(QString, prop2, &PropertyTutorial::value, &PropertyTutorial::setValue,
               W_Notify, &PropertyTutorial::valueChanged)
    // The setter and getter are matched by signature. add W_Notify before the notify signal

    // By member:
    W_PROPERTY(QString, prop3 MEMBER m_value NOTIFY valueChanged)
    //equivalent to
    W_PROPERTY(QString, prop4, &PropertyTutorial::m_value, W_Notify, &PropertyTutorial::valueChanged)

    // Optionally, you can put parentheses around the type, useful if it contains a comma
    QMap<int, int> m_map;
    W_PROPERTY((QMap<int,int>), map  MEMBER m_map)
};

W_OBJECT_IMPL(PropertyTutorial)


/** ******************************************************************************************** **/
/** Enums **/

class EnumTutorial  {
    W_GADGET(EnumTutorial)

public:
    /** W_ENUM(<name>, <values>)
      Similar to Q_ENUM, but we also have to manually write all the values.
      Maybe in the future it could be made nicer that declares the enum in the same macro
      but now that's all we have */
    enum MyEnum { Blue, Red, Green, Yellow = 45, Violet = Blue + Green*3 };

    W_ENUM(MyEnum, Blue, Red, Green, Yellow)

    // CS_ENUM is a bit better, but i don't believe CopperSpice works with complex expressions
    // such as "Blue + Green*3".

    // W_ENUM is currently limited to 16 enum values.
    // enum class are not yet supported

    // There is a W_FLAG  which is the same as Q_FLAG
};

W_GADGET_IMPL(EnumTutorial)

/** ******************************************************************************************** **/
/** TYPE REGISTRATION **/

/* Here is where the thing gets a bit more awkward:
   The types of parameters of signals and slots need to be registered so that we can generate the
   function signature

   To use a type as a return type or signal slot parameter, it needs to:
    - be a builtin QMetaType; or
    - be registered with W_REGISTER_ARGTYPE; or
    - use the overload syntax, but not with const reference.
*/

struct CustomType1 {};
struct CustomType2 {};
struct CustomType3 {};

/** W_REGISTER_ARGTYPE(TYPE)
   register  TYPE so it can be used as a parameter of a signal/slot or return value
   One must use the normalized signature.
   Note: This does not imply Q_DECLARE_METATYPE, and Q_DECLARE_METATYPE does not imply this.
  */
W_REGISTER_ARGTYPE(CustomType1)
W_REGISTER_ARGTYPE(CustomType1*)
W_REGISTER_ARGTYPE(CustomType2)

class ArgTypes : public QObject {
    W_OBJECT(ArgTypes)
public:
    void slot1(CustomType1, CustomType2) {}
    W_SLOT(slot1) // OK, all arguments register with W_REGISTER_ARGTYPE

    void slot2(CustomType1 *, CustomType2 *) {}
    W_SLOT(slot2, (CustomType1*,CustomType2*)) // Need to use the overload syntax because
                                               // CustomType2* is not registered

    typedef int MyInt;
    typedef CustomType1 MyCustomType1;

    void slot3(ArgTypes::MyInt, ArgTypes::MyCustomType1) {}
    W_SLOT(slot3, (ArgTypes::MyInt,ArgTypes::MyCustomType1)) // Need to use the overload syntax to use
                                                         // different type name (typedefs)

};

W_OBJECT_IMPL(ArgTypes)

/** ******************************************************************************************** **/
/** TEMPLATES **/

#include <QtCore/QDebug>

// We can have templated class:
template<typename T>
class MyTemplate : public QObject {
    W_OBJECT(MyTemplate)
public:
    // Template class can have slots and signals that depends on the parameter:
    void slot(T t) { qDebug() << "templated slot" << t; }
    W_SLOT(slot)

    void signal(T t)
    W_SIGNAL(signal, t)
};

//The syntax of W_OBJECT_IMPL changes a bit: as a second parameter you need to specify the template
//prefix:
W_OBJECT_IMPL(MyTemplate<T>, template <typename T>)

// When you have several template arguments:
template<typename A, typename B> class MyTemplate2 : public QObject {
    W_OBJECT(MyTemplate2)
};
// The first argument of W_OBJECT_IMPL need to be within parentheses:
W_OBJECT_IMPL((MyTemplate2<A,B>), template<typename A, typename B>)


void templ() {
    // This shows that it is possible;
    bool ok = true;
    MyTemplate<QString> obj;
    // old syntax
    ok = ok && QObject::connect(&obj, SIGNAL(signal(QString)), &obj, SLOT(slot(QString)));
    // new syntax
    ok = ok && QObject::connect(&obj, &MyTemplate<QString>::signal, &obj, &MyTemplate<QString>::slot);
    Q_ASSERT(ok);
    emit obj.signal("Hallo"); // Will show the qDebug twice
}

/** ******************************************************************************************** **/
// Nested classes are possible:
struct MyStruct {
    class Nested : public QObject {
        W_OBJECT(Nested)
    public:
        int foobar() const { return 0; }
        W_INVOKABLE(foobar)
    };
};
W_OBJECT_IMPL(MyStruct::Nested)

/** ******************************************************************************************** **/

int main() {
    MyObject o;
    aaa(&o);
    templ();
}
