#ifndef OBJECTVISITOR_H
#define OBJECTVISITOR_H


#include <QObject>
#include <QVariant>
#include <algorithm>
#include <cstdint>


class Visitor
{
public:
    Visitor() {}
    virtual ~Visitor() {}

    virtual void enterObject(const char* className, const char* objectName) = 0;
    virtual void leaveObject(const char* className, const char* objectName) = 0;

    // Reads or writes a property value
    virtual void visit(const char* className,
                        const char* propertyName, QVariant::Type typ, QVariant& value) = 0;

    // By default, it is a reader, but implementing this enables a visitor that sets values
    virtual bool isWriting() { return false; }
};


void Visit(Visitor& visitor, QObject* object)
{
    std::string objectName = object->objectName().toLocal8Bit().data();
    if (objectName.size() <= 1) {
        objectName = std::to_string((uint64_t)(void*)object);
    }
    visitor.enterObject(object->metaObject()->className(), objectName.c_str());
    for (int i = 0; i < object->metaObject()->propertyCount(); i++)
    {
        const QMetaProperty& property = object->metaObject()->property(i);
        if (visitor.isWriting()) {
            QVariant value;
            visitor.visit(property.enclosingMetaObject()->className(),
                            property.name(), property.type(), value);
            property.write(object, value);

        } else {
            QVariant value = property.read(object);
            visitor.visit(property.enclosingMetaObject()->className(),
                            property.name(), property.type(), value);
        }
    }
    for (int i = 0; i < object->children().count(); i++)
    {
        Visit(visitor, object->children()[i]);
    }
    visitor.leaveObject(object->metaObject()->className(), objectName.c_str());
}


#endif // OBJECTVISITOR_H
