// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

/** @file   serializer.h
*    @brief    The IoT Hub Serializer APIs allows developers to define models for
*            their devices
*
*    @details    The IoT Hub Serializer APIs allows developers to quickly and easily define
*                models for their devices directly as code, while supporting the required
*                features for modeling devices (including multiple models and multiple
*                devices within the same application). For example:
*
*        <pre>
*       BEGIN_NAMESPACE(Contoso);
*
*           DECLARE_STRUCT(SystemProperties,
*               ascii_char_ptr, DeviceID,
*               _Bool, Enabled
*           );
*
*           DECLARE_MODEL(VendingMachine,
*
*               WITH_DATA(int, SensorValue),
*
*               WITH_DATA(ascii_char_ptr, ObjectName),
*               WITH_DATA(ascii_char_ptr, ObjectType),
*               WITH_DATA(ascii_char_ptr, Version),
*               WITH_DATA(SystemProperties, SystemProperties),
*               WITH_DATA(ascii_char_ptr_no_quotes, Commands),
*
*               WITH_ACTION(SetItemPrice, ascii_char_ptr, itemId, ascii_char_ptr, price)
*           );
*
*       END_NAMESPACE(Contoso);
*       </pre>
*/

#ifndef SERIALIZER_H
#define SERIALIZER_H

#ifdef __cplusplus
#include <cstdlib>
#include <cstdarg>

#else
#include <stdlib.h>
#include <stdarg.h>
#endif

#include "azure_c_shared_utility/gballoc.h"
#include "azure_macro_utils/macro_utils.h"
#include "iotdevice.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/xlogging.h"
#include "methodreturn.h"
#include "schemalib.h"
#include "codefirst.h"
#include "agenttypesystem.h"
#include "schema.h"



#ifdef __cplusplus
extern "C"
{
#endif

    /* IOT Agent Macros */

    /**
     * @def BEGIN_NAMESPACE(schemaNamespace)
     * This macro marks the start of a section that declares IOT model
     * elements (like complex types, etc.). Declarations are typically
     * placed in header files, so that they can be shared between
     * translation units.
     */
#define BEGIN_NAMESPACE(schemaNamespace) \
    REFLECTED_END_OF_LIST

/**
* @def END_NAMESPACE(schemaNamespace)
* This macro marks the end of a section that declares IOT model
* elements.
*/
#define END_NAMESPACE(schemaNamespace) \
    REFLECTED_LIST_HEAD(schemaNamespace)

#define GLOBAL_INITIALIZE_STRUCT_FIELD(structType, destination, type, name) GlobalInitialize_##type((char*)destination+offsetof(structType, name));
#define GLOBAL_DEINITIALIZE_STRUCT_FIELD(structType, destination, type, name) GlobalDeinitialize_##type((char*)destination+offsetof(structType, name));
/**
* @def DECLARE_STRUCT(name, ...)
* This macro allows the definition of a struct type that can then be used as
* part of a model definition.
*
* @param name                      Name of the struct
* @param element1, element2...     Specifies a list of struct members
*/
#define DECLARE_STRUCT(name, ...) \
    typedef struct name##_TAG { \
        MU_FOR_EACH_2(INSERT_FIELD_INTO_STRUCT, __VA_ARGS__) \
    } name; \
    REFLECTED_STRUCT(name) \
    MU_FOR_EACH_2_KEEP_1(REFLECTED_FIELD, name, __VA_ARGS__) \
    TO_AGENT_DATA_TYPE(name, __VA_ARGS__) \
    static AGENT_DATA_TYPES_RESULT FromAGENT_DATA_TYPE_##name(const AGENT_DATA_TYPE* source, name* destination) \
    { \
        AGENT_DATA_TYPES_RESULT result; \
        if(source->type != EDM_COMPLEX_TYPE_TYPE) \
        { \
            result = AGENT_DATA_TYPES_INVALID_ARG; \
        } \
        else if(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)) != source->value.edmComplexType.nMembers) \
        { \
            /*too many or too few fields*/ \
            result = AGENT_DATA_TYPES_INVALID_ARG; \
        } \
        else \
        { \
            result = AGENT_DATA_TYPES_OK; \
            MU_FOR_EACH_2(BUILD_DESTINATION_FIELD, __VA_ARGS__); \
        } \
        return result; \
    } \
    static void MU_C2(destroyLocalParameter, name)(name * value) \
    { \
        MU_FOR_EACH_2_KEEP_1(UNBUILD_DESTINATION_FIELD, value, __VA_ARGS__); \
    } \
    static void MU_C2(GlobalInitialize_, name)(void* destination) \
    { \
        MU_FOR_EACH_2_KEEP_2(GLOBAL_INITIALIZE_STRUCT_FIELD, name, destination, __VA_ARGS__); \
    } \
    static void MU_C2(GlobalDeinitialize_, name)(void* destination) \
    { \
        MU_FOR_EACH_2_KEEP_2(GLOBAL_DEINITIALIZE_STRUCT_FIELD, name, destination, __VA_ARGS__); \
    } \


/**
 * @def     DECLARE_MODEL(name, ...)
 * This macro allows declaring a model that can be later used to instantiate
 * a device.
 *
 * @param   name                        Specifies the model name
 * @param   element1, element2...       Specifies a model element which can be
 *                                         a property or an action.
 *                                             - A property is described in a
 *                                               model by using the WITH_DATA
 *                                             - An action is described in a
 *                                               model by using the ::WITH_ACTION
 *                                               macro.
 *
 */
 /* WITH_DATA's name argument shall be one of the following data types: */

#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_ACTION(...)
#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_METHOD(...)
#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_DESIRED_PROPERTY(type, name, ...) MU_IF(MU_COUNT_ARG(__VA_ARGS__), void __VA_ARGS__ (void*);, )
#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_PROPERTY(...)
#define CREATE_DESIRED_PROPERTY_CALLBACK_MODEL_REPORTED_PROPERTY(...)

#define CREATE_DESIRED_PROPERTY_CALLBACK(...) CREATE_DESIRED_PROPERTY_CALLBACK_##__VA_ARGS__

#define SERIALIZER_REGISTER_NAMESPACE(NAMESPACE) CodeFirst_RegisterSchema(#NAMESPACE, & ALL_REFLECTED(NAMESPACE))

#define DECLARE_MODEL(name, ...)                                                             \
    REFLECTED_MODEL(name)                                                                    \
    MU_FOR_EACH_1(CREATE_DESIRED_PROPERTY_CALLBACK, __VA_ARGS__)                                \
    typedef struct name { int :1; MU_FOR_EACH_1(BUILD_MODEL_STRUCT, __VA_ARGS__) } name;        \
    MU_FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT, name, __VA_ARGS__)                               \
    TO_AGENT_DATA_TYPE(name, DROP_FIRST_COMMA_FROM_ARGS(EXPAND_MODEL_ARGS(__VA_ARGS__)))     \
    int FromAGENT_DATA_TYPE_##name(const AGENT_DATA_TYPE* source, void* destination)         \
    {                                                                                        \
        (void)source;                                                                        \
        (void)destination;                                                                   \
        LogError("SHOULD NOT GET CALLED... EVER");                                           \
        return 0;                                                                            \
    }                                                                                        \
    static void MU_C2(GlobalInitialize_, name)(void* destination)                               \
    {                                                                                        \
        (void)destination;                                                                   \
        MU_FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT_GLOBAL_INITIALIZE, name, __VA_ARGS__)         \
    }                                                                                        \
    static void MU_C2(GlobalDeinitialize_, name)(void* destination)                             \
    {                                                                                        \
        (void)destination;                                                                   \
        MU_FOR_EACH_1_KEEP_1(CREATE_MODEL_ELEMENT_GLOBAL_DEINITIALIZE, name, __VA_ARGS__)       \
    }                                                                                        \



/**
 * @def   WITH_DATA(type, name)
 * The ::WITH_DATA macro allows declaring a model property in a model. A
 * property can be published by using the ::SERIALIZE macro.
 *
 * @param   type    Specifies the property type. Can be any of the following
 *                  types:
 *                   - int
 *                   - double
 *                   - float
 *                   - long
 *                   - int8_t
 *                   - uint8_t
 *                   - int16_t
 *                   - int32_t
 *                   - int64_t
 *                   - bool
 *                   - ascii_char_ptr
 *                   - EDM_DATE_TIME_OFFSET
 *                   - EDM_GUID
 *                   - EDM_BINARY
 *                   - Any struct type previously introduced by another ::DECLARE_STRUCT.
 *
 * @param   name    Specifies the property name
 */
#define WITH_DATA(type, name) MODEL_PROPERTY(type, name)


#define WITH_REPORTED_PROPERTY(type, name) MODEL_REPORTED_PROPERTY(type, name)

#define WITH_DESIRED_PROPERTY(type, name, ...) MODEL_DESIRED_PROPERTY(type, name, __VA_ARGS__)

/**
 * @def   WITH_ACTION(name, ...)
 * The ::WITH_ACTION macro allows declaring a model action.
 *
 * @param   name                    Specifies the action name.
 * @param   argXtype, argXName...   Defines the type and name for the X<sup>th</sup>
 *                                  argument of the action. The type can be any of
 *                                  the primitive types or a struct type.
 */
#define WITH_ACTION(name, ...)  MODEL_ACTION(name, __VA_ARGS__)


/**
* @def   WITH_METHOD(name, ...)
* The ::WITH_METHOD macro allows declaring a model method.
*
* @param   name                    Specifies the method name.
* @param   argXtype, argXName...   Defines the type and name for the X<sup>th</sup>
*                                  argument of the method. The type can be any of
*                                  the primitive types or a struct type.
*/
#define WITH_METHOD(name, ...)  MODEL_METHOD(name, __VA_ARGS__)


/**
 * @def   GET_MODEL_HANDLE(schemaNamespace, modelName)
 * The ::GET_MODEL_HANDLE macro returns a model handle that can be used in
 * subsequent operations like generating the CSDL schema for the model,
 * uploading the schema, creating a device, etc.
 *
 * @param   schemaNamespace The namespace to which the model belongs.
 * @param   modelName       The name of the model.
 */
#define GET_MODEL_HANDLE(schemaNamespace, modelName) \
    Schema_GetModelByName(CodeFirst_RegisterSchema(MU_TOSTRING(schemaNamespace), &ALL_REFLECTED(schemaNamespace)), #modelName)

#define CREATE_DEVICE_WITH_INCLUDE_PROPERTY_PATH(schemaNamespace, modelName, serializerIncludePropertyPath) \
    (modelName*)CodeFirst_CreateDevice(GET_MODEL_HANDLE(schemaNamespace, modelName), &ALL_REFLECTED(schemaNamespace), sizeof(modelName), serializerIncludePropertyPath)

#define CREATE_DEVICE_WITHOUT_INCLUDE_PROPERTY_PATH(schemaNamespace, modelName) \
    (modelName*)CodeFirst_CreateDevice(GET_MODEL_HANDLE(schemaNamespace, modelName), &ALL_REFLECTED(schemaNamespace), sizeof(modelName), false)

#define CREATE_MODEL_INSTANCE(schemaNamespace, ...) \
    MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), CREATE_DEVICE_WITH_INCLUDE_PROPERTY_PATH, CREATE_DEVICE_WITHOUT_INCLUDE_PROPERTY_PATH) (schemaNamespace, __VA_ARGS__)

#define DESTROY_MODEL_INSTANCE(deviceData) \
    CodeFirst_DestroyDevice(deviceData)

/**
 * @def      SERIALIZE(destination, destinationSize,...)
 * This macro produces JSON serialized representation of the properties.
 *
 * @param   destination                  Pointer to an @c unsigned @c char* that
 *                                       will receive the serialized data.
 * @param   destinationSize              Pointer to a @c size_t that gets
 *                                       written with the size in bytes of the
 *                                       serialized data
 * @param    property1, property2...     A list of property values to send. The
 *                                       order in which the properties appear in
 *                                       the list does not matter, all values
 *                                       will be sent together.
 *
 */
#define SERIALIZE(destination, destinationSize,...) CodeFirst_SendAsync(destination, destinationSize, MU_COUNT_ARG(__VA_ARGS__) MU_FOR_EACH_1(ADDRESS_MACRO, __VA_ARGS__))

#define SERIALIZE_REPORTED_PROPERTIES(destination, destinationSize,...) CodeFirst_SendAsyncReported(destination, destinationSize, MU_COUNT_ARG(__VA_ARGS__) MU_FOR_EACH_1(ADDRESS_MACRO, __VA_ARGS__))


#define IDENTITY_MACRO(x) ,x
#define SERIALIZE_REPORTED_PROPERTIES_FROM_POINTERS(destination, destinationSize, ...) CodeFirst_SendAsyncReported(destination, destinationSize, MU_COUNT_ARG(__VA_ARGS__) MU_FOR_EACH_1(IDENTITY_MACRO, __VA_ARGS__))

/**
 * @def   EXECUTE_COMMAND(device, command)
 * Any action that is declared in a model must also have an implementation as
 * a C function.
 *
 * @param   device      Pointer to device data.
 * @param   command     Values that match the arguments declared in the model
 *                      action.
 */
#define EXECUTE_COMMAND(device, command) (CodeFirst_ExecuteCommand(device, command))

/**
* @def   EXECUTE_METHOD(device, methodName, methodPayload)
* Any method that is declared in a model must also have an implementation as
* a C function.
*
* @param   device      Pointer to device data.
* @param   methodName       The method name.
* @param   methodPayload    The method payload.
*/
#define EXECUTE_METHOD(device, methodName, methodPayload) CodeFirst_ExecuteMethod(device, methodName, methodPayload)

/**
* @def   INGEST_DESIRED_PROPERTIES(device, desiredProperties)
*
* @param   device                return of CodeFirst_CreateDevice.
* @param   desiredProperties     a null terminated string containing in JSON format the desired properties
*/
#define INGEST_DESIRED_PROPERTIES(device, jsonPayload, parseDesiredNode) (CodeFirst_IngestDesiredProperties(device, jsonPayload, parseDesiredNode))

/* Helper macros */

/* These macros remove a useless comma from the beginning of an argument list that looks like:
,x1,y1,x2,y2 */
#ifdef _MSC_VER

#define DROP_FIRST_COMMA(N, x) \
x MU_IFCOMMA_NOFIRST(N)

#define DROP_IF_EMPTY(N, x) \
MU_IF(MU_COUNT_ARG(x),DROP_FIRST_COMMA(N,x),x)

#define DROP_FIRST_COMMA_FROM_ARGS(...) \
MU_FOR_EACH_1_COUNTED(DROP_IF_EMPTY, MU_C1(__VA_ARGS__))

#else

#define DROP_FIRST_COMMA_0(N, x) \
    x MU_IFCOMMA_NOFIRST(N)

#define DROP_FIRST_COMMA_1(N, x) \
    x

#define DROP_FIRST_COMMA(empty, N, x) \
    MU_C2(DROP_FIRST_COMMA_,empty)(N,x)

#define DROP_IF_EMPTY(N, x) \
    DROP_FIRST_COMMA(MU_ISEMPTY(x),N,x)

#define DROP_FIRST_COMMA_FROM_ARGS(...) \
    MU_FOR_EACH_1_COUNTED(DROP_IF_EMPTY, __VA_ARGS__)

#endif

/* These macros expand a sequence of arguments for DECLARE_MODEL that looks like
WITH_DATA(x, y), WITH_DATA(x2, y2) to a list of arguments consumed by the macro that marshalls a struct, like:
x, y, x2, y2
Actions are discarded, since no marshalling will be done for those when sending state data */
#define TO_AGENT_DT_EXPAND_MODEL_PROPERTY(x, y) ,x,y

#define TO_AGENT_DT_EXPAND_MODEL_REPORTED_PROPERTY(x, y) ,x,y

#define TO_AGENT_DT_EXPAND_MODEL_DESIRED_PROPERTY(x, y, ...) ,x,y

#define TO_AGENT_DT_EXPAND_MODEL_ACTION(...)

#define TO_AGENT_DT_EXPAND_MODEL_METHOD(...)

#define TO_AGENT_DT_EXPAND_ELEMENT_ARGS(N, ...) TO_AGENT_DT_EXPAND_##__VA_ARGS__

#define EXPAND_MODEL_ARGS(...) \
    MU_FOR_EACH_1_COUNTED(TO_AGENT_DT_EXPAND_ELEMENT_ARGS, __VA_ARGS__)

#define TO_AGENT_DATA_TYPE(name, ...) \
    static AGENT_DATA_TYPES_RESULT ToAGENT_DATA_TYPE_##name(AGENT_DATA_TYPE *destination, const name value) \
    { \
        AGENT_DATA_TYPES_RESULT result = AGENT_DATA_TYPES_OK; \
        size_t iMember = 0; \
        const char* memberNames[MU_IF(MU_DIV2(MU_C1(MU_COUNT_ARG(__VA_ARGS__))), MU_DIV2(MU_C1(MU_COUNT_ARG(__VA_ARGS__))), 1)] = { 0 }; \
        size_t memberCount = sizeof(memberNames) / sizeof(memberNames[0]); \
        (void)value; \
        if (memberCount == 0) \
        { \
            result = AGENT_DATA_TYPES_OK; \
        } \
        else \
        { \
            AGENT_DATA_TYPE members[sizeof(memberNames) / sizeof(memberNames[0])]; \
            MU_FOR_EACH_2(FIELD_AS_STRING, MU_EXPAND_TWICE(__VA_ARGS__)) \
            iMember = 0; \
            { \
                MU_FOR_EACH_2(CREATE_AGENT_DATA_TYPE, MU_EXPAND_TWICE(__VA_ARGS__)) \
                result = ((result == AGENT_DATA_TYPES_OK) && (Create_AGENT_DATA_TYPE_from_Members(destination, #name, sizeof(memberNames) / sizeof(memberNames[0]), memberNames, members) == AGENT_DATA_TYPES_OK)) \
                            ? AGENT_DATA_TYPES_OK \
                            : AGENT_DATA_TYPES_ERROR; \
                { \
                    size_t jMember; \
                    for (jMember = 0; jMember < iMember; jMember++) \
                    { \
                        Destroy_AGENT_DATA_TYPE(&members[jMember]); \
                    } \
                } \
            } \
        } \
        return result; \
    }

#define FIELD_AS_STRING(x,y) memberNames[iMember++] = #y;

#define REFLECTED_LIST_HEAD(name) \
    static const REFLECTED_DATA_FROM_DATAPROVIDER ALL_REFLECTED(name) = { &MU_C2(REFLECTED_, MU_C1(MU_DEC(__COUNTER__))) };
#define REFLECTED_STRUCT(name) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_STRUCT_TYPE,               &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {0}, {MU_TOSTRING(name)}, {0}, {0}, {0}, {0}} };
#define REFLECTED_FIELD(XstructName, XfieldType, XfieldName) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_FIELD_TYPE,                &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {MU_TOSTRING(XfieldName), MU_TOSTRING(XfieldType), MU_TOSTRING(XstructName)}, {0}, {0}, {0} } };
#define REFLECTED_MODEL(name) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_MODEL_TYPE,                &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {0}, {0}, {MU_TOSTRING(name)} } };
#define REFLECTED_PROPERTY(type, name, modelName) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_PROPERTY_TYPE,             &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {MU_TOSTRING(name), MU_TOSTRING(type), Create_AGENT_DATA_TYPE_From_Ptr_##modelName##name, offsetof(modelName, name), sizeof(type), MU_TOSTRING(modelName)}, {0}, {0} } };
#define REFLECTED_REPORTED_PROPERTY(type, name, modelName) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_REPORTED_PROPERTY_TYPE,    &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {MU_TOSTRING(name), MU_TOSTRING(type), Create_AGENT_DATA_TYPE_From_Ptr_##modelName##name, offsetof(modelName, name), sizeof(type), MU_TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0} } };


#define REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE(type, name, modelName, COUNTER, onDesiredPropertyChange) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(COUNTER))) =      { REFLECTION_DESIRED_PROPERTY_TYPE,     &MU_C2(REFLECTED_, MU_C1(MU_DEC(COUNTER))),         { {0}, {onDesiredPropertyChange, DesiredPropertyInitialize_##modelName##name, DesiredPropertyDeinitialize_##modelName##name, MU_TOSTRING(name), MU_TOSTRING(type), (int(*)(const AGENT_DATA_TYPE*, void*))FromAGENT_DATA_TYPE_##type, offsetof(modelName, name), sizeof(type), MU_TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0}, {0}} };

#define REFLECTED_DESIRED_PROPERTY(type, name, modelName, ...)                                                              \
    MU_IF(MU_COUNT_ARG(__VA_ARGS__),                                                                                              \
        MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,__COUNTER__, __VA_ARGS__),  \
        MACRO_UTILS_DELAY(REFLECTED_DESIRED_PROPERTY_WITH_ON_DESIRED_PROPERTY_CHANGE)(type, name, modelName,MU_DEC(__COUNTER__), NULL)     \
    )                                                                                                                       \

#define REFLECTED_ACTION(name, argc, argv, fn, modelName) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_ACTION_TYPE,               &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {0}, {0}, {0}, {0}, {0}, {0}, {MU_TOSTRING(name), argc, argv, fn, MU_TOSTRING(modelName)}, {0}} };

#define REFLECTED_METHOD(name, argc, argv, fn, modelName) \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, MU_C1(MU_INC(__COUNTER__))) = { REFLECTION_METHOD_TYPE,               &MU_C2(REFLECTED_, MU_C1(MU_DEC(MU_DEC(__COUNTER__)))), { {MU_TOSTRING(name), argc, argv, fn, MU_TOSTRING(modelName)}, {0}, {0}, {0}, {0}, {0}, {0}, {0}} };


#define REFLECTED_END_OF_LIST \
    static const REFLECTED_SOMETHING MU_C2(REFLECTED_, __COUNTER__) = {          REFLECTION_NOTHING,                   NULL,                                       { {0},{0}, {0}, {0}, {0}, {0}, {0}, {0}} };

#define EXPAND_MODEL_PROPERTY(type, name) MU_EXPAND_ARGS(MODEL_PROPERTY, type, name)

#define EXPAND_MODEL_REPORTED_PROPERTY(type, name) MU_EXPAND_ARGS(MODEL_REPORTED_PROPERTY, type, name)

#define EXPAND_MODEL_DESIRED_PROPERTY(type, name, ...) MU_EXPAND_ARGS(MODEL_DESIRED_PROPERTY, type, name, __VA_ARGS__)

#define EXPAND_MODEL_ACTION(...) MU_EXPAND_ARGS(MODEL_ACTION, __VA_ARGS__)

#define EXPAND_MODEL_METHOD(...) MU_EXPAND_ARGS(MODEL_METHOD, __VA_ARGS__)

#define BUILD_MODEL_STRUCT(elem) INSERT_FIELD_FOR_##elem

#define CREATE_MODEL_ENTITY(modelName, callType, ...) MU_EXPAND_ARGS(CREATE_##callType(modelName, __VA_ARGS__))
#define CREATE_SOMETHING(modelName, ...) MU_EXPAND_ARGS(CREATE_MODEL_ENTITY(modelName, __VA_ARGS__))
#define CREATE_ELEMENT(modelName, elem) MU_EXPAND_ARGS(CREATE_SOMETHING(modelName, MU_EXPAND_ARGS(EXPAND_##elem)))

#define CREATE_MODEL_ELEMENT(modelName, elem) MU_EXPAND_ARGS(CREATE_ELEMENT(modelName, elem))


#define CREATE_MODEL_ENTITY_GLOBAL_INITIALIZATION(modelName, callType, ...) MU_EXPAND_ARGS(CREATE_GLOBAL_INITIALIZE_##callType(modelName, __VA_ARGS__))
#define CREATE_SOMETHING_GLOBAL_INITIALIZATION(modelName, ...) MU_EXPAND_ARGS(CREATE_MODEL_ENTITY_GLOBAL_INITIALIZATION(modelName, __VA_ARGS__))
#define CREATE_ELEMENT_GLOBAL_INITIALIZATION(modelName, elem) MU_EXPAND_ARGS(CREATE_SOMETHING_GLOBAL_INITIALIZATION(modelName, MU_EXPAND_ARGS(EXPAND_##elem)))
#define CREATE_MODEL_ELEMENT_GLOBAL_INITIALIZE(modelName, elem) MU_EXPAND_ARGS(CREATE_ELEMENT_GLOBAL_INITIALIZATION(modelName, elem))

#define CREATE_MODEL_ENTITY_GLOBAL_DEINITIALIZATION(modelName, callType, ...) MU_EXPAND_ARGS(CREATE_GLOBAL_DEINITIALIZE_##callType(modelName, __VA_ARGS__))
#define CREATE_SOMETHING_GLOBAL_DEINITIALIZATION(modelName, ...) MU_EXPAND_ARGS(CREATE_MODEL_ENTITY_GLOBAL_DEINITIALIZATION(modelName, __VA_ARGS__))
#define CREATE_ELEMENT_GLOBAL_DEINITIALIZATION(modelName, elem) MU_EXPAND_ARGS(CREATE_SOMETHING_GLOBAL_DEINITIALIZATION(modelName, MU_EXPAND_ARGS(EXPAND_##elem)))
#define CREATE_MODEL_ELEMENT_GLOBAL_DEINITIALIZE(modelName, elem) MU_EXPAND_ARGS(CREATE_ELEMENT_GLOBAL_DEINITIALIZATION(modelName, elem))

#define INSERT_FIELD_INTO_STRUCT(x, y) x y;


#define INSERT_FIELD_FOR_MODEL_PROPERTY(type, name) INSERT_FIELD_INTO_STRUCT(type, name)
#define CREATE_GLOBAL_INITIALIZE_MODEL_PROPERTY(modelName, type, name) /*do nothing, this is written by user*/
#define CREATE_GLOBAL_DEINITIALIZE_MODEL_PROPERTY(modelName, type, name) /*do nothing, this is user's stuff*/

/*REPORTED_PROPERTY is not different than regular WITH_DATA*/
#define INSERT_FIELD_FOR_MODEL_REPORTED_PROPERTY(type, name) INSERT_FIELD_INTO_STRUCT(type, name)
#define CREATE_GLOBAL_INITIALIZE_MODEL_REPORTED_PROPERTY(modelName, type,name) GlobalInitialize_##type((char*)destination+offsetof(modelName, name));
#define CREATE_GLOBAL_DEINITIALIZE_MODEL_REPORTED_PROPERTY(modelName, type,name) GlobalDeinitialize_##type((char*)destination+offsetof(modelName, name));

/*DESIRED_PROPERTY is not different than regular WITH_DATA*/
#define INSERT_FIELD_FOR_MODEL_DESIRED_PROPERTY(type, name, ...) INSERT_FIELD_INTO_STRUCT(type, name)
#define CREATE_GLOBAL_INITIALIZE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) /*do nothing*/
#define CREATE_GLOBAL_DEINITIALIZE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) /*do nothing*/

#define INSERT_FIELD_FOR_MODEL_ACTION(name, ...) /* action isn't a part of the model struct */
#define INSERT_FIELD_FOR_MODEL_METHOD(name, ...) /* method isn't a part of the model struct */

#define CREATE_GLOBAL_INITIALIZE_MODEL_ACTION(...) /*do nothing*/
#define CREATE_GLOBAL_DEINITIALIZE_MODEL_ACTION(...) /*do nothing*/

#define CREATE_GLOBAL_INITIALIZE_MODEL_METHOD(...) /*do nothing*/
#define CREATE_GLOBAL_DEINITIALIZE_MODEL_METHOD(...) /*do nothing*/

#define CREATE_MODEL_PROPERTY(modelName, type, name) \
    IMPL_PROPERTY(type, name, modelName)

#define CREATE_MODEL_REPORTED_PROPERTY(modelName, type, name) \
    IMPL_REPORTED_PROPERTY(type, name, modelName)

#define CREATE_MODEL_DESIRED_PROPERTY(modelName, type, name, ...) \
    IMPL_DESIRED_PROPERTY(type, name, modelName, __VA_ARGS__)

#define IMPL_PROPERTY(propertyType, propertyName, modelName) \
    static int Create_AGENT_DATA_TYPE_From_Ptr_##modelName##propertyName(void* param, AGENT_DATA_TYPE* dest) \
    { \
        return MU_C1(ToAGENT_DATA_TYPE_##propertyType)(dest, *(propertyType*)param); \
    } \
    REFLECTED_PROPERTY(propertyType, propertyName, modelName)

#define IMPL_REPORTED_PROPERTY(propertyType, propertyName, modelName) \
    static int Create_AGENT_DATA_TYPE_From_Ptr_##modelName##propertyName(void* param, AGENT_DATA_TYPE* dest) \
    { \
        return MU_C1(ToAGENT_DATA_TYPE_##propertyType)(dest, *(propertyType*)param); \
    } \
    REFLECTED_REPORTED_PROPERTY(propertyType, propertyName, modelName)

#define IMPL_DESIRED_PROPERTY(propertyType, propertyName, modelName, ...)           \
    static void DesiredPropertyInitialize_##modelName##propertyName(void* destination)                             \
    {                                                                                                   \
        GlobalInitialize_##propertyType(destination);                                                   \
    }                                                                                                   \
    static void DesiredPropertyDeinitialize_##modelName##propertyName(void* destination)                           \
    {                                                                                                   \
       GlobalDeinitialize_##propertyType(destination);                                                  \
    }                                                                                                   \
    REFLECTED_DESIRED_PROPERTY(propertyType, propertyName, modelName, __VA_ARGS__)          \

#define CREATE_MODEL_ACTION(modelName, actionName, ...) \
    DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(modelName##actionName, 1); \
    EXECUTE_COMMAND_RESULT actionName (modelName* device MU_FOR_EACH_2(DEFINE_FUNCTION_PARAMETER, __VA_ARGS__)); \
    static EXECUTE_COMMAND_RESULT MU_C2(actionName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); \
    /*for macro purposes, this array always has at least 1 element*/ \
    DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 1); \
    static const WRAPPER_ARGUMENT MU_C2(actionName, WRAPPERARGUMENTS)[MU_DIV2(MU_INC(MU_INC(MU_COUNT_ARG(__VA_ARGS__))))] = { MU_FOR_EACH_2_COUNTED(MAKE_WRAPPER_ARGUMENT, __VA_ARGS__) MU_IFCOMMA(MU_INC(MU_INC(MU_COUNT_ARG(__VA_ARGS__)))) {0} }; \
    REFLECTED_ACTION(actionName, MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), MU_C2(actionName, WRAPPERARGUMENTS), MU_C2(actionName, WRAPPER), modelName) \
    static EXECUTE_COMMAND_RESULT MU_C2(actionName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values) \
    { \
        EXECUTE_COMMAND_RESULT result; \
        DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 2); \
        if(ParameterCount != MU_DIV2(MU_COUNT_ARG(__VA_ARGS__))) \
        { \
            result = EXECUTE_COMMAND_ERROR; \
        } \
        else \
        { \
            /*the below line takes care of initialized but not referenced parameter warning*/ \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 3); \
            MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), size_t iParameter = 0;, ) \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 4); \
            /*the below line takes care of an unused parameter when values is really never questioned*/ \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 5); \
            MU_FOR_EACH_2(DEFINE_LOCAL_PARAMETER, __VA_ARGS__) \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 6); \
            MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), , (void)values;) \
            { \
               DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 7); \
            } \
            MU_FOR_EACH_2_KEEP_1(START_BUILD_LOCAL_PARAMETER, EXECUTE_COMMAND_ERROR, __VA_ARGS__) \
            { \
               DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 8); \
            } \
            result = actionName((modelName*)device MU_FOR_EACH_2(PUSH_LOCAL_PARAMETER, __VA_ARGS__)); \
            MU_FOR_EACH_2_REVERSE(END_BUILD_LOCAL_PARAMETER, __VA_ARGS__) \
        } \
        return result; \
    }

#define CREATE_MODEL_METHOD(modelName, methodName, ...) \
    DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(modelName##methodName, 1); \
    METHODRETURN_HANDLE methodName (modelName* device MU_FOR_EACH_2(DEFINE_FUNCTION_PARAMETER, __VA_ARGS__)); \
    static METHODRETURN_HANDLE MU_C2(methodName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values); \
    /*for macro purposes, this array always has at least 1 element*/ \
    DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 1); \
    static const WRAPPER_ARGUMENT MU_C2(methodName, WRAPPERARGUMENTS)[MU_DIV2(MU_INC(MU_INC(MU_COUNT_ARG(__VA_ARGS__))))] = { MU_FOR_EACH_2_COUNTED(MAKE_WRAPPER_ARGUMENT, __VA_ARGS__) MU_IFCOMMA(MU_INC(MU_INC(MU_COUNT_ARG(__VA_ARGS__)))) {0} }; \
    REFLECTED_METHOD(methodName, MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), MU_C2(methodName, WRAPPERARGUMENTS), MU_C2(methodName, WRAPPER), modelName) \
    static METHODRETURN_HANDLE MU_C2(methodName, WRAPPER)(void* device, size_t ParameterCount, const AGENT_DATA_TYPE* values) \
    { \
        METHODRETURN_HANDLE result; \
        DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 2); \
        if(ParameterCount != MU_DIV2(MU_COUNT_ARG(__VA_ARGS__))) \
        { \
            LogError("expected parameter count (%lu) does not match the actual parameter count (%lu)", (unsigned long)ParameterCount, (unsigned long)MU_COUNT_ARG(__VA_ARGS__)); \
            result = NULL; \
        } \
        else \
        { \
            /*the below line takes care of initialized but not referenced parameter warning*/ \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 3); \
            MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), size_t iParameter = 0;, ) \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 4); \
            /*the below line takes care of an unused parameter when values is really never questioned*/ \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 5); \
            MU_FOR_EACH_2(DEFINE_LOCAL_PARAMETER, __VA_ARGS__) \
            DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(methodName, 6); \
            MU_IF(MU_DIV2(MU_COUNT_ARG(__VA_ARGS__)), , (void)values;) \
            { \
               DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 7); \
            } \
            MU_FOR_EACH_2_KEEP_1(START_BUILD_LOCAL_PARAMETER, NULL,__VA_ARGS__) \
            { \
               DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(actionName, 8); \
            } \
            result = methodName((modelName*)device MU_FOR_EACH_2(PUSH_LOCAL_PARAMETER, __VA_ARGS__)); \
            MU_FOR_EACH_2_REVERSE(END_BUILD_LOCAL_PARAMETER, __VA_ARGS__) \
        } \
        return result; \
    }

#define CREATE_AGENT_DATA_TYPE(type, name) \
    result = (( result==AGENT_DATA_TYPES_OK) && (ToAGENT_DATA_TYPE_##type( &(members[iMember]), value.name) == AGENT_DATA_TYPES_OK))?AGENT_DATA_TYPES_OK:AGENT_DATA_TYPES_ERROR; \
    iMember+= ((result==AGENT_DATA_TYPES_OK)?1:0);

#define BUILD_DESTINATION_FIELD(type, name) \
    if(result == AGENT_DATA_TYPES_OK) \
    { \
        size_t i; \
        bool wasFieldConverted = false; \
        for (i = 0; i < source->value.edmComplexType.nMembers; i++) \
        { \
            /*the name of the field of the complex type must match the name of the field of the structure (parameter name here)*/ \
            if (strcmp(source->value.edmComplexType.fields[i].fieldName, MU_TOSTRING(name)) == 0) \
            { \
                wasFieldConverted = (MU_C2(FromAGENT_DATA_TYPE_, type)(source->value.edmComplexType.fields[i].value, &(destination->name)) == AGENT_DATA_TYPES_OK); \
                break; \
            } \
        } \
        result = (wasFieldConverted == true)? AGENT_DATA_TYPES_OK: AGENT_DATA_TYPES_INVALID_ARG; \
    } \
    else \
    { \
        /*fallthrough*/ \
    }

#define UNBUILD_DESTINATION_FIELD(value, type, name) \
    MU_C2(destroyLocalParameter, type)(&(value->name));


#define ADDRESS_MACRO(x) ,&x

#define KEEP_FIRST_(X, ...) X
#ifdef _MSC_VER
#define KEEP_FIRST(X) KEEP_FIRST_ LPAREN X)
#else
#define KEEP_FIRST(X) KEEP_FIRST_(X)
#endif

#define PROMOTIONMAP_float double, double
#define PROMOTIONMAP_int8_t int, int
#define PROMOTIONMAP_uint8_t int, int
#define PROMOTIONMAP_int16_t int, int
#define PROMOTIONMAP__Bool int, int
#define PROMOTIONMAP_bool int, int

#define CASTMAP_float (float), (float)
#define CASTMAP_int8_t (int8_t), (int8_t)
#define CASTMAP_uint8_t (uint8_t), (uint8_t)
#define CASTMAP_int16_t (int16_t), (int16_t)
#define CASTMAP__Bool 0!=, 0!=
#define CASTMAP_bool 0!=, 0!=

#define EMPTY_TOKEN

#define ANOTHERIF(x) MU_C2(ANOTHERIF,x)
#define ANOTHERIF0(a,b) a
#define ANOTHERIF1(a,b) b
#define ANOTHERIF2(a,b) b
#define ANOTHERIF3(a,b) b
#define ANOTHERIF4(a,b) b
#define ANOTHERIF5(a,b) b
#define ANOTHERIF6(a,b) b
#define ANOTHERIF7(a,b) b
#define ANOTHERIF8(a,b) b
#define ANOTHERIF9(a,b) b
#define ANOTHERIF10(a,b) b
#define ANOTHERIF11(a,b) b
#define ANOTHERIF12(a,b) b

#define MAP_PROMOTED_TYPE(X) ANOTHERIF(MU_DEC(MU_COUNT_ARG(PROMOTIONMAP_##X))) (X, KEEP_FIRST(PROMOTIONMAP_##X))
#define MAP_CAST_TYPE(X) ANOTHERIF(MU_DEC(MU_COUNT_ARG(CASTMAP_##X)))    (EMPTY_TOKEN, KEEP_FIRST(CASTMAP_##X)  )

#define MU_IFCOMMA(N) MU_C2(MU_IFCOMMA_, N)
#define MU_IFCOMMA_0
#define MU_IFCOMMA_2
#define MU_IFCOMMA_4 ,
#define MU_IFCOMMA_6 ,
#define MU_IFCOMMA_8 ,
#define MU_IFCOMMA_10 ,
#define MU_IFCOMMA_12 ,
#define MU_IFCOMMA_14 ,
#define MU_IFCOMMA_16 ,
#define MU_IFCOMMA_18 ,
#define MU_IFCOMMA_20 ,
#define MU_IFCOMMA_22 ,
#define MU_IFCOMMA_24 ,
#define MU_IFCOMMA_26 ,
#define MU_IFCOMMA_28 ,
#define MU_IFCOMMA_30 ,
#define MU_IFCOMMA_32 ,
#define MU_IFCOMMA_34 ,
#define MU_IFCOMMA_36 ,
#define MU_IFCOMMA_38 ,
#define MU_IFCOMMA_40 ,
#define MU_IFCOMMA_42 ,
#define MU_IFCOMMA_44 ,
#define MU_IFCOMMA_46 ,
#define MU_IFCOMMA_48 ,
#define MU_IFCOMMA_50 ,
#define MU_IFCOMMA_52 ,
#define MU_IFCOMMA_54 ,
#define MU_IFCOMMA_56 ,
#define MU_IFCOMMA_58 ,
#define MU_IFCOMMA_60 ,
#define MU_IFCOMMA_62 ,
#define MU_IFCOMMA_64 ,
#define MU_IFCOMMA_66 ,
#define MU_IFCOMMA_68 ,
#define MU_IFCOMMA_70 ,
#define MU_IFCOMMA_72 ,
#define MU_IFCOMMA_74 ,
#define MU_IFCOMMA_76 ,
#define MU_IFCOMMA_78 ,
#define MU_IFCOMMA_80 ,
#define MU_IFCOMMA_82 ,
#define MU_IFCOMMA_84 ,
#define MU_IFCOMMA_86 ,
#define MU_IFCOMMA_88 ,
#define MU_IFCOMMA_90 ,
#define MU_IFCOMMA_92 ,
#define MU_IFCOMMA_94 ,
#define MU_IFCOMMA_96 ,
#define MU_IFCOMMA_98 ,
#define MU_IFCOMMA_100 ,
#define MU_IFCOMMA_102 ,
#define MU_IFCOMMA_104 ,
#define MU_IFCOMMA_106 ,
#define MU_IFCOMMA_108 ,
#define MU_IFCOMMA_110 ,
#define MU_IFCOMMA_112 ,
#define MU_IFCOMMA_114 ,
#define MU_IFCOMMA_116 ,
#define MU_IFCOMMA_118 ,
#define MU_IFCOMMA_120 ,
#define MU_IFCOMMA_122 ,
#define MU_IFCOMMA_124 ,
#define MU_IFCOMMA_126 ,
#define MU_IFCOMMA_128 ,

#define DEFINE_LOCAL_PARAMETER(type, name) type MU_C2(name,_local); GlobalInitialize_##type(& MU_C2(name, _local));

#define START_BUILD_LOCAL_PARAMETER(errorWhenItFails, type, name) \
    if (MU_C2(FromAGENT_DATA_TYPE_, type)(&values[iParameter], &MU_C2(name, _local)) != AGENT_DATA_TYPES_OK) \
    { \
        result = errorWhenItFails; \
    }\
    else \
    { \
        iParameter++;

#define END_BUILD_LOCAL_PARAMETER(type, name) \
    (void)MU_C2(destroyLocalParameter, type)(&MU_C2(name, _local)); \
    }

/*The following constructs have been devised to work around the precompiler bug of Visual Studio 2005, version 14.00.50727.42*/
/* The bug is explained in https://connect.microsoft.com/VisualStudio/feedback/details/278752/comma-missing-when-using-va-args */
/*A short description is: preprocessor is mysteriously eating commas ','.
In order to feed the appetite of the preprocessor, several constructs have
been devised that can sustain a missing ',' while still compiling and while still doing nothing
and while hopefully being eliminated from the code based on "doesn't do anything" so no code size penalty
*/

/*the reason why all these constructs work is:
if two strings separated by a comma will lose the comma (myteriously) then they will become just one string:
"a", "b" ------Preprocessor------> "a" "b" -----Compiler----> "ab"
*/

#define LOTS_OF_COMMA_TO_BE_EATEN /*there were witnesses where as many as THREE commas have been eaten!*/ \
"0" "1", "2", "3", "4", "5", "6", "7", "8", "9"
#define DEFINITION_THAT_CAN_SUSTAIN_A_COMMA_STEAL(name, instance) static const char* eatThese_COMMA_##name##instance[] = {LOTS_OF_COMMA_TO_BE_EATEN}

#define PUSH_LOCAL_PARAMETER(type, name) , MU_C2(name, _local)
#define DEFINE_FUNCTION_PARAMETER(type, name) , type name
#define MAKE_WRAPPER_ARGUMENT(N, type, name) {MU_TOSTRING(type), MU_TOSTRING(name)} MU_IFCOMMA(N)

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, double)(AGENT_DATA_TYPE* dest, double source)
{
    return Create_AGENT_DATA_TYPE_from_DOUBLE(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, double)(const AGENT_DATA_TYPE* agentData, double* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_DOUBLE_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmDouble.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, double)(void* dest)
{
    *(double*)dest = 0.0;
}

static void MU_C2(GlobalDeinitialize_, double)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, float)(AGENT_DATA_TYPE* dest, float source)
{
    return Create_AGENT_DATA_TYPE_from_FLOAT(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, float)(const AGENT_DATA_TYPE* agentData, float* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_SINGLE_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmSingle.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, float)(void* dest)
{
    *(float*)dest = 0.0f;
}

static void MU_C2(GlobalDeinitialize_, float)(void* dest)
{
    (void)(dest);
}


static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, int)(AGENT_DATA_TYPE* dest, int source)
{
    return Create_AGENT_DATA_TYPE_from_SINT32(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, int)(const AGENT_DATA_TYPE* agentData, int* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_INT32_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmInt32.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, int)(void* dest)
{
    *(int*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, int)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, long)(AGENT_DATA_TYPE* dest, long source)
{
    return Create_AGENT_DATA_TYPE_from_SINT64(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, long)(const AGENT_DATA_TYPE* agentData, long* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_INT64_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = (long)agentData->value.edmInt64.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, long)(void* dest)
{
    *(long*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, long)(void* dest)
{
    (void)(dest);
}


static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, int8_t)(AGENT_DATA_TYPE* dest, int8_t source)
{
    return Create_AGENT_DATA_TYPE_from_SINT8(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, int8_t)(const AGENT_DATA_TYPE* agentData, int8_t* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_SBYTE_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmSbyte.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, int8_t)(void* dest)
{
    *(int8_t*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, int8_t)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, uint8_t)(AGENT_DATA_TYPE* dest, uint8_t source)
{
    return Create_AGENT_DATA_TYPE_from_UINT8(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, uint8_t)(const AGENT_DATA_TYPE* agentData, uint8_t* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_BYTE_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmByte.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, uint8_t)(void* dest)
{
    *(uint8_t*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, uint8_t)(void* dest)
{
    (void)(dest);
}


static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, int16_t)(AGENT_DATA_TYPE* dest, int16_t source)
{
    return Create_AGENT_DATA_TYPE_from_SINT16(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, int16_t)(const AGENT_DATA_TYPE* agentData, int16_t* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_INT16_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmInt16.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, int16_t)(void* dest)
{
    *(int16_t*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, int16_t)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, int32_t)(AGENT_DATA_TYPE* dest, int32_t source)
{
    return Create_AGENT_DATA_TYPE_from_SINT32(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, int32_t)(const AGENT_DATA_TYPE* agentData, int32_t* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_INT32_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmInt32.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, int32_t)(void* dest)
{
    *(int32_t*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, int32_t)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, int64_t)(AGENT_DATA_TYPE* dest, int64_t source)
{
    return Create_AGENT_DATA_TYPE_from_SINT64(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, int64_t)(const AGENT_DATA_TYPE* agentData, int64_t* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_INT64_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmInt64.value;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, int64_t)(void* dest)
{
    *(int64_t*)dest = 0;
}

static void MU_C2(GlobalDeinitialize_, int64_t)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, bool)(AGENT_DATA_TYPE* dest, bool source)
{
    return Create_EDM_BOOLEAN_from_int(dest, source == true);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, bool)(const AGENT_DATA_TYPE* agentData, bool* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_BOOLEAN_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = (agentData->value.edmBoolean.value == EDM_TRUE) ? true : false;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, bool)(void* dest)
{
    *(bool*)dest = false;
}

static void MU_C2(GlobalDeinitialize_, bool)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, ascii_char_ptr)(AGENT_DATA_TYPE* dest, ascii_char_ptr source)
{
    return Create_AGENT_DATA_TYPE_from_charz(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, ascii_char_ptr)(const AGENT_DATA_TYPE* agentData, ascii_char_ptr* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_STRING_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        if (*dest != NULL)
        {
            free(*dest);
            *dest = NULL;
        }

        if (mallocAndStrcpy_s(dest, agentData->value.edmString.chars) != 0)
        {
            LogError("failure in mallocAndStrcpy_s");
            result = AGENT_DATA_TYPES_ERROR;
        }
        else
        {
            result = AGENT_DATA_TYPES_OK;
        }

    }
    return result;
}

static void MU_C2(GlobalInitialize_, ascii_char_ptr)(void* dest)
{
    *(ascii_char_ptr*)dest = NULL;
}

static void MU_C2(GlobalDeinitialize_, ascii_char_ptr)(void* dest)
{
    if (*(ascii_char_ptr*)dest != NULL)
    {
        free(*(ascii_char_ptr*)dest);
    }
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, ascii_char_ptr_no_quotes)(AGENT_DATA_TYPE* dest, ascii_char_ptr_no_quotes source)
{
    return Create_AGENT_DATA_TYPE_from_charz_no_quotes(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, ascii_char_ptr_no_quotes)(const AGENT_DATA_TYPE* agentData, ascii_char_ptr_no_quotes* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_STRING_NO_QUOTES_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        if (*dest != NULL)
        {
            free(*dest);
            *dest = NULL;
        }

        if (mallocAndStrcpy_s(dest, agentData->value.edmStringNoQuotes.chars) != 0)
        {
            LogError("failure in mallocAndStrcpy_s");
            result = AGENT_DATA_TYPES_ERROR;
        }
        else
        {
            result = AGENT_DATA_TYPES_OK;
        }
    }
    return result;
}

static void MU_C2(GlobalInitialize_, ascii_char_ptr_no_quotes)(void* dest)
{
    *(ascii_char_ptr_no_quotes*)dest = NULL;
}

static void MU_C2(GlobalDeinitialize_, ascii_char_ptr_no_quotes)(void* dest)
{
    if (*(ascii_char_ptr_no_quotes*)dest != NULL)
    {
        free(*(ascii_char_ptr_no_quotes*)dest);
    }
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, EDM_DATE_TIME_OFFSET)(AGENT_DATA_TYPE* dest, EDM_DATE_TIME_OFFSET source)
{
    return Create_AGENT_DATA_TYPE_from_EDM_DATE_TIME_OFFSET(dest, source);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, EDM_DATE_TIME_OFFSET)(const AGENT_DATA_TYPE* agentData, EDM_DATE_TIME_OFFSET* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_DATE_TIME_OFFSET_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        *dest = agentData->value.edmDateTimeOffset;
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, EDM_DATE_TIME_OFFSET)(void* dest)
{
    memset(dest, 0, sizeof(EDM_DATE_TIME_OFFSET));
}

static void MU_C2(GlobalDeinitialize_, EDM_DATE_TIME_OFFSET)(void* dest)
{
    (void)(dest);
}

static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, EDM_GUID)(AGENT_DATA_TYPE* dest, EDM_GUID guid)
{
    return Create_AGENT_DATA_TYPE_from_EDM_GUID(dest, guid);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, EDM_GUID)(const AGENT_DATA_TYPE* agentData, EDM_GUID* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_GUID_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        (void)memcpy(dest->GUID, agentData->value.edmGuid.GUID, 16);
        result = AGENT_DATA_TYPES_OK;
    }
    return result;
}

static void MU_C2(GlobalInitialize_, EDM_GUID)(void* dest)
{
    memset(dest, 0, sizeof(EDM_GUID));
}

static void MU_C2(GlobalDeinitialize_, EDM_GUID)(void* dest)
{
    (void)(dest);
}


static AGENT_DATA_TYPES_RESULT MU_C2(ToAGENT_DATA_TYPE_, EDM_BINARY)(AGENT_DATA_TYPE* dest, EDM_BINARY edmBinary)
{
    return Create_AGENT_DATA_TYPE_from_EDM_BINARY(dest, edmBinary);
}

static AGENT_DATA_TYPES_RESULT MU_C2(FromAGENT_DATA_TYPE_, EDM_BINARY)(const AGENT_DATA_TYPE* agentData, EDM_BINARY* dest)
{
    AGENT_DATA_TYPES_RESULT result;
    if (agentData->type != EDM_BINARY_TYPE)
    {
        result = AGENT_DATA_TYPES_INVALID_ARG;
    }
    else
    {
        if ((dest->data = (unsigned char *)malloc(agentData->value.edmBinary.size)) == NULL) /*cast because this get included in a C++ file.*/
        {
            result = AGENT_DATA_TYPES_ERROR;
        }
        else
        {
            (void)memcpy(dest->data, agentData->value.edmBinary.data, agentData->value.edmBinary.size);
            dest->size = agentData->value.edmBinary.size;
            result = AGENT_DATA_TYPES_OK;
        }
    }
    return result;
}

static void MU_C2(GlobalInitialize_, EDM_BINARY)(void* dest)
{
    ((EDM_BINARY*)dest)->data = NULL;
    ((EDM_BINARY*)dest)->size = 0;
}

static void MU_C2(GlobalDeinitialize_, EDM_BINARY)(void* dest)
{
    if ((((EDM_BINARY*)dest)->data) != NULL)
    {
        free(((EDM_BINARY*)dest)->data);
    }
}

static void MU_C2(destroyLocalParameter, EDM_BINARY)(EDM_BINARY* value)
{
    if (value != NULL)
    {
        free(value->data);
        value->data = NULL;
        value->size = 0;
    }
}

static void MU_C2(destroyLocalParameter, EDM_BOOLEAN)(EDM_BOOLEAN* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_BYTE)(EDM_BYTE* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_DATE)(EDM_DATE* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_DATE_TIME_OFFSET)(EDM_DATE_TIME_OFFSET* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_DECIMAL)(EDM_DECIMAL* value)
{
    if (value != NULL)
    {
        STRING_delete(value->value);
        value->value = NULL;
    }
}

static void MU_C2(destroyLocalParameter, EDM_DOUBLE)(EDM_DOUBLE* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_DURATION)(EDM_DURATION* value)
{
    if (value != NULL)
    {
        free(value->digits);
        value->digits = NULL;
        value->nDigits = 0;
    }
}

static void MU_C2(destroyLocalParameter, EDM_GUID)(EDM_GUID* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_INT16)(EDM_INT16* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_INT32)(EDM_INT32* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_INT64)(EDM_INT64* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_SBYTE)(EDM_SBYTE* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_SINGLE)(EDM_SINGLE* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, EDM_STRING)(EDM_STRING* value)
{
    (void)value;
}


static void MU_C2(destroyLocalParameter, EDM_TIME_OF_DAY)(EDM_TIME_OF_DAY* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, int)(int* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, float)(float* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, double)(double* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, long)(long* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, int8_t)(int8_t* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, uint8_t)(uint8_t* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, int16_t)(int16_t* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, int32_t)(int32_t* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, int64_t)(int64_t* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, bool)(bool* value)
{
    (void)value;
}

static void MU_C2(destroyLocalParameter, ascii_char_ptr)(ascii_char_ptr* value)
{
    if (value != NULL)
    {
        free(*value);
    }

}

static void MU_C2(destroyLocalParameter, ascii_char_ptr_no_quotes)(ascii_char_ptr_no_quotes* value)
{
    if (value != NULL)
    {
        free(*value);
    }
}

#ifdef __cplusplus
    }
#endif

#endif /*SERIALIZER_H*/


