#pragma once
#include "wobjectdefs.h"

#ifndef Q_MOC_RUN
// Macro for signals exported with the preprocessor
#if defined(Q_CC_MSVC)
#define E_SIGNAL3(bit, export, ...) W_MACRO_MSVC_EXPAND(W_SIGNAL2(__VA_ARGS__, 0))
#define E_SIGNAL2(EXPORT_MACRO, EXPORTS_MACRO, ...) E_SIGNAL3(EXPORT_MACRO, EXPORTS_MACRO,  __VA_ARGS__)
#define E_SIGNAL(EXPORT_MACRO, ...) E_SIGNAL2(EXPORT_MACRO ## S, EXPORT_MACRO, __VA_ARGS__)
#else

// When the EXPORTS_MACRO isn't defined or is 0
#define E_SIGNAL_0(...) ;

// When the EXPORTS_MACRO is defined
#define E_SIGNAL_1(EXPORT_MACRO, ...) EXPORT_MACRO W_MACRO_MSVC_EXPAND(W_SIGNAL2(__VA_ARGS__, 0))

#define E_SIGNAL3(bit, export, ...) W_MACRO_MSVC_EXPAND(E_SIGNAL_ ## bit(export, __VA_ARGS__))
#define E_SIGNAL2(EXPORT_MACRO, EXPORTS_MACRO, ...) E_SIGNAL3(EXPORT_MACRO, EXPORTS_MACRO,  __VA_ARGS__)
#define E_SIGNAL(EXPORT_MACRO, ...) E_SIGNAL2(EXPORT_MACRO ## S, EXPORT_MACRO, __VA_ARGS__)

#endif

// Advanced "PROPERTY" macro which defines an additional compile-time reflection struct
#define PROPERTY(...) W_MACRO_MSVC_EXPAND(PROPERTY2(__VA_ARGS__))

#define W_GET_ARG_0(_0, ...) _0
#define W_GET_ARG_1(_0, _1, ...) _1
#define W_GET_ARG_2(_0, _1, _2, ...) _2
#define W_GET_ARG_3(_0, _1, _2, _3, ...) _3

#define PROPERTY2(TYPE, NAME, ...)                                 \
  W_PROPERTY(TYPE, NAME, __VA_ARGS__)                              \
  struct p_ ## NAME {                                              \
    using param_type = TYPE;                                       \
    using model_type = W_ThisType;                                 \
    static const constexpr auto name = #NAME ;                     \
    static const constexpr auto get = W_MACRO_MSVC_EXPAND(W_GET_ARG_0(__VA_ARGS__, 0));   \
    static const constexpr auto set = W_MACRO_MSVC_EXPAND(W_GET_ARG_1(__VA_ARGS__, 0));   \
    static const constexpr auto notify = W_MACRO_MSVC_EXPAND(W_GET_ARG_3(__VA_ARGS__, 0)); \
  };



#define W_INLINE_PROPERTY_VALUE(Type, Name, Init, Get, Set, Notify) \
  private:                                         \
  Type m_ ## Name Init;                            \
  public:                                          \
  Type Get() const noexcept { return m_ ## Name; } \
  void Set(Type m)                                 \
  {                                                \
    if (m != m_ ## Name)                           \
    {                                              \
      m_ ## Name = m;                              \
      Notify(m);                                   \
    }                                              \
  } W_SLOT(Set)                                    \
  void Notify(Type v) W_SIGNAL(Notify, v)          \
  W_PROPERTY(Type, Name, &W_ThisType::Get, &W_ThisType::Set, W_Notify, &W_ThisType::Notify)

#define W_INLINE_PROPERTY_CREF(Type, Name, Init, Get, Set, Notify) \
  private:                                                \
  Type m_ ## Name Init;                                   \
  public:                                                 \
  const Type& Get() const noexcept { return m_ ## Name; } \
  void Set(const Type& m)                                 \
  {                                                       \
    if (m != m_ ## Name)                                  \
    {                                                     \
      m_ ## Name = m;                                     \
      Notify(m);                                          \
    }                                                     \
  } W_SLOT(Set)                                           \
  void Notify(const Type& v) W_SIGNAL(Notify, v)          \
  W_PROPERTY(Type, Name, &W_ThisType::Get, &W_ThisType::Set, W_Notify, &W_ThisType::Notify)

#define INLINE_PROPERTY_VALUE(Type, Name, Init, Get, Set, Notify) \
  private:                                         \
  Type m_ ## Name Init;                            \
  public:                                          \
  Type Get() const noexcept { return m_ ## Name; } \
  void Set(Type m)                                 \
  {                                                \
    if (m != m_ ## Name)                           \
    {                                              \
      m_ ## Name = m;                              \
      Notify(m);                                   \
    }                                              \
  } W_SLOT(Set)                                    \
  void Notify(Type v) W_SIGNAL(Notify, v)          \
  PROPERTY(Type, Name, &W_ThisType::Get, &W_ThisType::Set, W_Notify, &W_ThisType::Notify)

#define INLINE_PROPERTY_CREF(Type, Name, Init, Get, Set, Notify) \
  private:                                                \
  Type m_ ## Name Init;                                   \
  public:                                                 \
  const Type& Get() const noexcept { return m_ ## Name; } \
  void Set(const Type& m)                                 \
  {                                                       \
    if (m != m_ ## Name)                                  \
    {                                                     \
      m_ ## Name = m;                                     \
      Notify(m);                                          \
    }                                                     \
  } W_SLOT(Set)                                           \
  void Notify(const Type& v) W_SIGNAL(Notify, v)          \
  PROPERTY(Type, Name, &W_ThisType::Get, &W_ThisType::Set, W_Notify, &W_ThisType::Notify)


#endif
