#pragma once

#include <memory>

#ifdef Q_OBJECT
#include <QProperty>

#define MPL_BINDABLE_PROPERTY(c, t, n) \
private: \
Q_PROPERTY(t n READ get##n WRITE set##n NOTIFY n##Changed BINDABLE n##Bindable) \
QBindable<t> n##Bindable() { return QBindable<t>(&n); } \
t get##n() const { return n; } \
void set##n(t v) { n = v; } \
public: \
Q_SIGNAL void n##Changed(); \
Q_OBJECT_BINDABLE_PROPERTY(c, t, n, &c::n##Changed)
#endif


#define MPL_DISABLE_COPY(Claz) \
Claz(const Claz&) = delete; \
    Claz& operator=(const Claz&) = delete;

#define MPL_DISABLE_MOVE(Claz) \
Claz(const Claz&&) = delete; \
    Claz& operator=(const Claz&&) = delete;

#define MPL_DISABLE_COPY_MOVE(Claz) \
MPL_DISABLE_COPY(Claz) \
    MPL_DISABLE_MOVE(Claz)

#ifdef _MSC_VER
#define MPL_DECLARE_PRIVATE(Claz) \
    friend class Claz##Private; \
    public: \
    using pri_type = Claz##Private; \
    private: \
    std::unique_ptr<pri_type> pri;
#else
#define MPL_DECLARE_PRIVATE(Claz) \
    friend class Claz##Private; \
    public: \
    using pri_type = class Claz##Private; \
    private: \
    std::unique_ptr<class Claz##Private> pri;
#endif

#define MPL_DECLARE_PUBLIC(Claz) \
friend class Claz; \
    Claz* pub; \
    static inline Claz##Private* r_ptr(const Claz* p) { return p->pri.get(); }

template<class T>
inline typename T::pri_type* PriPtr(const T* p) {
    return T::pri_type::r_ptr(p);
}

#define G_P(p) auto const d = PriPtr(p)

#define MPL_SINGLETON_DECL(claz) \
MPL_DISABLE_COPY_MOVE(claz)\
public:\
static claz* inst();


#define MPL_SINGLETON_IMPL(claz) \
claz* claz::inst() {\
    static claz __inst__;\
    return &__inst__;\
}

#ifdef _NDEBUG
#define M_ASSERT(expr) if (!expr) { \
auto msg = std::format("unexpected error at'{}:{}'", __FILE__, __LINE__); \
    throw std::runtime_error(msg); \
}
#else
#define M_ASSERT(expr) assert(expr)
#endif
