#pragma once

#ifdef __APPLE__
#include <Availability.h>

#ifdef __MAC_OS_X_VERSION_MAX_ALLOWED
#if __MAC_OS_X_VERSION_MAX_ALLOWED < 101200
#define TREX_NO_SHARED_MUTEX
#endif
#endif
#endif

#ifdef TREX_NO_SHARED_MUTEX
using StructMutex_t = std::mutex;
using ReadLock_t = std::unique_lock<StructMutex_t>;
#else
using StructMutex_t = std::shared_mutex;
using ReadLock_t = std::shared_lock<StructMutex_t>;
#endif

template<typename T>
class StructReference {
private:
    mutable ReadLock_t _lock;
    const T* ptr;
    
public:
    StructReference(StructMutex_t& mutex, const T& ptr)
        : _lock(mutex, std::defer_lock), ptr(&ptr)
    { }
    
    StructReference(const StructReference&) = delete;
    ~StructReference() { }

    const T* operator->() const {
        if(!_lock.owns_lock())
            _lock.lock();
        return ptr;
    }
    const T& operator*() const {
        if(!_lock.owns_lock())
            _lock.lock();
        return *ptr;
    }
};

#define SELECT_TYPE(TYPE) typename std::conditional< std::is_trivially_copyable< TYPE >::value, std::atomic< TYPE >, TYPE>::type

#define EVERY_PAIR(TYPE, NAME) SELECT_TYPE( TYPE ) NAME; StructMutex_t mutex_ ## NAME ;
#define STRUCT_STRING_MEMBERS(NAM, a) EVERY_PAIR a

#define EVERY_PAIR_STRING(a, b) #b,
#define STRINGIZE_MEMBERS(NAM, a) EVERY_PAIR_STRING a

#define EVERY_PAIR_GET_A(a, b) #a
#define EVERY_PAIR_GET_B(a, b) #b
#define EVERY_PLAIN_GET_A_NO_COMMA(a, b) a
#define EVERY_PLAIN_GET_B_NO_COMMA(a, b) b
#define EVERY_PLAIN_GET_B_WITH_T(a, b) b ## _t

#define IMPL_ACCESS_ENUM(NAM, TUPLE) \
template<> struct NAM :: AccessEnum<NAM :: Variables:: EVERY_PLAIN_GET_B_NO_COMMA TUPLE > { \
	template<typename T, typename std::enable_if<std::is_convertible<T, EVERY_PLAIN_GET_A_NO_COMMA TUPLE >::value, std::nullptr_t>::type = nullptr> \
	static void set(T v) { set_impl< EVERY_PLAIN_GET_A_NO_COMMA TUPLE >(v, NAM :: impl(). EVERY_PLAIN_GET_B_NO_COMMA TUPLE, NAM :: impl(). STRUCT_CONCATENATE( mutex_ , EXPAND( EVERY_PLAIN_GET_B_NO_COMMA TUPLE )) ); } \
	static auto get() { return get_impl< EVERY_PLAIN_GET_A_NO_COMMA TUPLE >( NAM :: impl(). EVERY_PLAIN_GET_B_NO_COMMA TUPLE , NAM :: impl(). STRUCT_CONCATENATE( mutex_ , EXPAND( EVERY_PLAIN_GET_B_NO_COMMA TUPLE )) ); } \
	static auto copy() { return copy_impl< EVERY_PLAIN_GET_A_NO_COMMA TUPLE >( NAM :: impl(). EVERY_PLAIN_GET_B_NO_COMMA TUPLE , NAM :: impl(). STRUCT_CONCATENATE( mutex_ , EXPAND( EVERY_PLAIN_GET_B_NO_COMMA TUPLE )) ); } \
};

#define EVERY_PAIR_UPDATE_CONDITION(a, b) else if ( key == #b )
#define UPDATE_MEMBERS(NAM, a) EVERY_PAIR_UPDATE_CONDITION a { assert( printf( "Updating %s::%s of type %s\n", #NAM , EVERY_PAIR_GET_B a , EVERY_PAIR_GET_A a ) ); NAM :: update< NAM :: EVERY_PLAIN_GET_B_NO_COMMA a > ( key, value ); NAM :: set < NAM :: EVERY_PLAIN_GET_B_NO_COMMA a > ( value.template value < EVERY_PLAIN_GET_A_NO_COMMA a >() ) ; }

#define EVERY_PLAIN_GET_B(a, b) b,
#define PLAIN_MEMBERS(NAM, a) EVERY_PLAIN_GET_B a

#define EXPRESS_MEMBER_FUNCTIONS(NAM, TUPLE) IMPL_ACCESS_ENUM(NAM, TUPLE)

#define EXPRESS_MEMBER_TYPES(NAM, TUPLE) using EVERY_PLAIN_GET_B_WITH_T TUPLE = EVERY_PLAIN_GET_A_NO_COMMA TUPLE;
#define EXPRESS_MEMBER_GETTERS(NAM, TUPLE) []() -> auto& { if(!cmn::GlobalSettings::get( EVERY_PAIR_GET_B TUPLE ).is_type< EVERY_PLAIN_GET_A_NO_COMMA TUPLE >()) { auto type_name = cmn::GlobalSettings::get( EVERY_PAIR_GET_B TUPLE ).get().type_name(); U_EXCEPTION("Settings type '%S' is not '%s' for Variable '%s::%s'.", &type_name, EVERY_PAIR_GET_A TUPLE , #NAM, EVERY_PAIR_GET_B TUPLE); } return cmn::GlobalSettings::get( EVERY_PAIR_GET_B TUPLE ).get(); },


#define STRUCT_CONCATENATE(arg1, arg2)   STRUCT_CONCATENATE1(arg1, arg2)
#define STRUCT_CONCATENATE1(arg1, arg2)  STRUCT_CONCATENATE2(arg1, arg2)
#define STRUCT_CONCATENATE2(arg1, arg2)  arg1 ## arg2
#define STRUCT_STRINGIZE_SINGLE(ARG) #ARG

#define EXPAND(x) x

/*#define STRUCT_FOR_EACH_1(NAM, what, x) what(NAM, x)
#define STRUCT_FOR_EACH_2(NAM, what, x, ...)\
  what(NAM, x)\
  EXPAND(STRUCT_FOR_EACH_1(NAM, what,  __VA_ARGS__))
#define STRUCT_FOR_EACH_3(NAM, what, x, ...)\
  what(NAM, x)\
  EXPAND(STRUCT_FOR_EACH_2(NAM, what, __VA_ARGS__))
#define STRUCT_FOR_EACH_4(NAM, what, x, ...)\
  what(NAM, x)\
  EXPAND(STRUCT_FOR_EACH_3(NAM, what,  __VA_ARGS__))

*/



#define STRUCT_FOR_EACH_1(NAM, what, x) what(NAM, x)
#define STRUCT_FOR_EACH_2(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_1(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_3(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_2(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_4(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_3(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_5(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_4(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_6(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_5(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_7(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_6(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_8(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_7(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_9(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_8(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_10(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_9(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_11(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_10(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_12(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_11(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_13(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_12(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_14(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_13(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_15(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_14(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_16(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_15(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_17(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_16(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_18(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_17(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_19(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_18(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_20(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_19(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_21(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_20(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_22(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_21(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_23(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_22(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_24(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_23(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_25(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_24(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_26(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_25(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_27(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_26(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_28(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_27(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_29(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_28(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_30(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_29(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_31(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_30(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_32(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_31(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_33(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_32(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_34(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_33(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_35(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_34(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_36(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_35(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_37(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_36(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_38(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_37(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_39(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_38(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_40(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_39(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_41(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_40(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_42(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_41(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_43(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_42(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_44(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_43(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_45(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_44(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_46(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_45(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_47(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_46(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_48(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_47(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_49(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_48(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_50(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_49(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_51(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_50(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_52(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_51(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_53(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_52(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_54(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_53(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_55(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_54(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_56(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_55(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_57(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_56(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_58(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_57(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_59(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_58(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_60(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_59(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_61(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_60(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_62(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_61(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_63(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_62(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_64(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_63(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_65(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_64(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_66(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_65(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_67(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_66(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_68(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_67(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_69(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_68(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_70(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_69(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_71(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_70(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_72(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_71(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_73(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_72(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_74(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_73(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_75(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_74(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_76(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_75(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_77(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_76(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_78(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_77(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_79(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_78(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_80(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_79(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_81(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_80(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_82(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_81(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_83(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_82(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_84(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_83(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_85(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_84(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_86(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_85(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_87(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_86(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_88(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_87(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_89(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_88(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_90(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_89(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_91(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_90(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_92(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_91(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_93(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_92(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_94(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_93(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_95(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_94(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_96(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_95(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_97(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_96(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_98(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_97(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_99(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_98(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_100(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_99(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_101(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_100(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_102(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_101(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_103(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_102(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_104(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_103(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_105(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_104(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_106(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_105(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_107(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_106(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_108(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_107(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_109(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_108(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_110(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_109(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_111(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_110(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_112(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_111(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_113(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_112(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_114(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_113(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_115(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_114(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_116(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_115(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_117(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_116(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_118(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_117(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_119(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_118(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_120(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_119(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_121(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_120(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_122(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_121(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_123(NAM, what, x, ...)\
    what(NAM, x)\
    EXPAND(STRUCT_FOR_EACH_122(NAM, what,  __VA_ARGS__))

#define STRUCT_FOR_EACH_ARG_N( _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, _21, _22, _23, _24, _25, _26, _27, _28, _29, _30, _31, _32, _33, _34, _35, _36, _37, _38, _39, _40, _41, _42, _43, _44, _45, _46, _47, _48, _49, _50, _51, _52, _53, _54, _55, _56, _57, _58, _59, _60, _61, _62, _63, _64, _65, _66, _67, _68, _69, _70, _71, _72, _73, _74, _75, _76, _77, _78, _79, _80, _81, _82, _83, _84, _85, _86, _87, _88, _89, _90, _91, _92, _93, _94, _95, _96, _97, _98, _99, _100, _101, _102, _103, _104, _105, _106, _107, _108, _109, _110, _111, _112, _113, _114, _115, _116, _117, _118, _119, _120, _121, _122, _123, _124,  N, ...) N
#define STRUCT_FOR_EACH_RSEQ_N() 124, 123, 122, 121, 120, 119, 118, 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, 107, 106, 105, 104, 103, 102, 101, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0

#define STRUCT_FOR_EACH_NARG(...) STRUCT_FOR_EACH_NARG_(__VA_ARGS__, STRUCT_FOR_EACH_RSEQ_N())
#define STRUCT_FOR_EACH_NARG_(...) EXPAND(STRUCT_FOR_EACH_ARG_N(__VA_ARGS__))

#define STRUCT_FOR_EACH_(N, NAM, what, ...) EXPAND(STRUCT_CONCATENATE(STRUCT_FOR_EACH_, N)(NAM, what, __VA_ARGS__))
#define STRUCT_FOR_EACH(NAM, what, ...) STRUCT_FOR_EACH_( STRUCT_FOR_EACH_NARG( __VA_ARGS__ ) , NAM, what, __VA_ARGS__ )
/*enum Variables {
    test,
    bed
};

constexpr const char* VariableName[] = {
    "test",
    "bed"
};

template<Variables v>
void update() {
    printf("%s\n", VariableName[v]);
}*/


#define CREATE_STRUCT(NAM, ...) \
class NAM { \
public: \
    enum Variables { STRUCT_FOR_EACH(NAM, PLAIN_MEMBERS, __VA_ARGS__) }; \
    struct Members { \
        Members() = default; \
        STRUCT_FOR_EACH(NAM, STRUCT_STRING_MEMBERS, __VA_ARGS__) \
    }; \
    STRUCT_FOR_EACH(NAM, EXPRESS_MEMBER_TYPES, __VA_ARGS__) \
private: \
    static constexpr const char * VariableNames[] { STRUCT_FOR_EACH(NAM, STRINGIZE_MEMBERS, __VA_ARGS__) }; \
    inline static Members _members; \
    inline static std::unordered_map<Variables, std::function<void(const std::string&, const sprite::PropertyType&)>> _callbacks; \
    template<Variables M> struct AccessEnum { }; \
\
public: \
    static inline const std::array<std::function<const cmn::sprite::PropertyType&()>, STRUCT_FOR_EACH_NARG(__VA_ARGS__)> _getters { \
        STRUCT_FOR_EACH(NAM, EXPRESS_MEMBER_GETTERS, __VA_ARGS__) \
    }; \
    static auto& get(Variables name) { return _getters[name](); } \
    template<Variables M> static auto get() { return AccessEnum<M>::get(); } \
    template<Variables M> static auto copy() { return AccessEnum<M>::copy(); } \
    template<Variables M, typename T> static void set(T obj) { AccessEnum<M>::set(obj); } \
private: \
 \
    template<typename T, typename K> \
    static inline auto get_impl(const K& obj, StructMutex_t& mutex) { \
        if constexpr (std::is_same<std::atomic<T>, K>::value) { \
            return obj.load(); \
        } \
        else { \
            return StructReference<T>(mutex, obj); \
        } \
    } \
 \
    template<typename T, typename K> \
    static inline T copy_impl(const K& obj, StructMutex_t& mutex) { \
        if constexpr (std::is_same<std::atomic<T>, K>::value) { \
            return obj.load(); \
        } \
        else { \
            ReadLock_t guard(mutex); \
            return obj; \
        } \
    } \
 \
    template<typename T, typename K> \
    static inline void set_impl(T v, K& obj, StructMutex_t& mutex) { \
        if constexpr (std::is_same<std::atomic<T>, K>::value) { \
            obj = v; \
        } \
        else { \
            std::unique_lock guard(mutex); \
            obj = v; \
        } \
    } \
public: \
    inline static NAM :: Members & impl() { return NAM :: _members; } \
    template<Variables M> static void update(const std::string &key, const sprite::PropertyType& value) { auto it = _callbacks.find(M); if(it != _callbacks.end()) it->second(key, value); } \
    template<Variables M> \
    static const char* name() { \
        return VariableNames[M]; \
    } \
    static void set_callback(Variables v, decltype(_callbacks)::mapped_type f) { _callbacks[v] = f; } \
    static std::vector<std::string> names() { return std::vector<std::string>{ STRUCT_FOR_EACH(NAM, STRINGIZE_MEMBERS, __VA_ARGS__) }; } \
    static void variable_changed (const sprite::Map &map, const std::string &key, const sprite::PropertyType& value) { \
        if(false); STRUCT_FOR_EACH(NAM, UPDATE_MEMBERS, __VA_ARGS__) \
    } \
    static inline void init() { \
        static std::once_flag flag; \
        std::call_once(flag, [](){ \
        cmn::GlobalSettings::map().register_callback((void*) STRUCT_STRINGIZE_SINGLE(EXPAND(STRUCT_CONCATENATE(NAM, __COUNTER__))), NAM :: variable_changed ); \
            for(auto &n : NAM :: names()) \
                variable_changed(cmn::GlobalSettings::map(), n, cmn::GlobalSettings::get(n).get()); \
        }); \
    } \
}; \
STRUCT_FOR_EACH(NAM, EXPRESS_MEMBER_FUNCTIONS, __VA_ARGS__)

#define STRUCT_META_EXTENSIONS(NAM) namespace cmn { \
namespace Meta { \
template<> inline std::string toStr<NAM :: Variables>(const NAM :: Variables& value, const NAM :: Variables* ) { return NAM :: names()[value]; } \
template<> inline std::string name<enum NAM :: Variables>(const enum NAM :: Variables*) { return #NAM ; } \
template<> inline enum NAM :: Variables fromStr<enum NAM :: Variables>(const std::string& str, const enum NAM :: Variables* ) { \
    size_t index = 0; \
    for(auto &name : NAM :: names()) { \
        if(str == name) { \
            return (NAM :: Variables)index; \
        } \
        ++index; \
    } \
    \
    throw CustomException<std::invalid_argument>("Cannot find variable '%s::%s'.", #NAM, str.c_str()); \
} \
} \
}