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

#include <stdlib.h>
#include "azure_c_shared_utility/gballoc.h"

#include "schema.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/vector.h"
#include "azure_c_shared_utility/safe_math.h"


MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(SCHEMA_RESULT, SCHEMA_RESULT_VALUES);

typedef struct SCHEMA_PROPERTY_HANDLE_DATA_TAG
{
    const char* PropertyName;
    const char* PropertyType;
} SCHEMA_PROPERTY_HANDLE_DATA;

typedef struct SCHEMA_REPORTED_PROPERTY_HANDLE_DATA_TAG
{
    const char* reportedPropertyName;
    const char* reportedPropertyType;
} SCHEMA_REPORTED_PROPERTY_HANDLE_DATA;

typedef struct SCHEMA_DESIRED_PROPERTY_HANDLE_DATA_TAG
{
    pfOnDesiredProperty onDesiredProperty;
    pfDesiredPropertyInitialize desiredPropertInitialize;
    pfDesiredPropertyDeinitialize desiredPropertDeinitialize;
    const char* desiredPropertyName;
    const char* desiredPropertyType;
    pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE;
    size_t offset;
} SCHEMA_DESIRED_PROPERTY_HANDLE_DATA;

typedef struct SCHEMA_ACTION_ARGUMENT_HANDLE_DATA_TAG
{
    const char* Name;
    const char* Type;
} SCHEMA_ACTION_ARGUMENT_HANDLE_DATA;

typedef struct SCHEMA_METHOD_ARGUMENT_HANDLE_DATA_TAG
{
    char* Name;
    char* Type;
} SCHEMA_METHOD_ARGUMENT_HANDLE_DATA;

typedef struct SCHEMA_ACTION_HANDLE_DATA_TAG
{
    const char* ActionName;
    size_t ArgumentCount;
    SCHEMA_ACTION_ARGUMENT_HANDLE* ArgumentHandles;
} SCHEMA_ACTION_HANDLE_DATA;

typedef struct SCHEMA_METHOD_HANDLE_DATA_TAG
{
    char* methodName;
    VECTOR_HANDLE methodArguments; /*holds SCHEMA_METHOD_ARGUMENT_HANDLE*/
} SCHEMA_METHOD_HANDLE_DATA;

typedef struct MODEL_IN_MODEL_TAG
{
    pfOnDesiredProperty onDesiredProperty; /*is NULL if not specified or if the model in model is not WITH_DESIRED_PROPERTY*/
    size_t offset; /*offset of the model in model (offsetof)*/
    const char* propertyName;
    SCHEMA_MODEL_TYPE_HANDLE modelHandle;
} MODEL_IN_MODEL;

typedef struct SCHEMA_MODEL_TYPE_HANDLE_DATA_TAG
{
    VECTOR_HANDLE methods; /*holds SCHEMA_METHOD_HANDLE*/
    VECTOR_HANDLE desiredProperties; /*holds SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*/
    const char* Name;
    SCHEMA_HANDLE SchemaHandle;
    SCHEMA_PROPERTY_HANDLE* Properties;
    size_t PropertyCount;
    VECTOR_HANDLE reportedProperties; /*holds SCHEMA_REPORTED_PROPERTY_HANDLE*/
    SCHEMA_ACTION_HANDLE* Actions;
    size_t ActionCount;
    VECTOR_HANDLE models;
    size_t DeviceCount;
} SCHEMA_MODEL_TYPE_HANDLE_DATA;

typedef struct SCHEMA_STRUCT_TYPE_HANDLE_DATA_TAG
{
    const char* Name;
    SCHEMA_PROPERTY_HANDLE* Properties;
    size_t PropertyCount;
} SCHEMA_STRUCT_TYPE_HANDLE_DATA;

typedef struct SCHEMA_HANDLE_DATA_TAG
{
    void* metadata;
    const char* Namespace;
    SCHEMA_MODEL_TYPE_HANDLE* ModelTypes;
    size_t ModelTypeCount;
    SCHEMA_STRUCT_TYPE_HANDLE* StructTypes;
    size_t StructTypeCount;
} SCHEMA_HANDLE_DATA;

static VECTOR_HANDLE g_schemas = NULL;

static void DestroyProperty(SCHEMA_PROPERTY_HANDLE propertyHandle)
{
    SCHEMA_PROPERTY_HANDLE_DATA* propertyType = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle;
    free((void*)propertyType->PropertyName);
    free((void*)propertyType->PropertyType);
    free(propertyType);
}

static void DestroyActionArgument(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle)
{
    SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle;
    if (actionArgument != NULL)
    {
        free((void*)actionArgument->Name);
        free((void*)actionArgument->Type);
        free(actionArgument);
    }
}

static void DestroyMethodArgument(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle)
{
    free(methodArgumentHandle->Name);
    free(methodArgumentHandle->Type);
    free(methodArgumentHandle);
}

static void DestroyAction(SCHEMA_ACTION_HANDLE actionHandle)
{
    SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;
    if (action != NULL)
    {
        size_t j;

        for (j = 0; j < action->ArgumentCount; j++)
        {
            DestroyActionArgument(action->ArgumentHandles[j]);
        }
        free(action->ArgumentHandles);

        free((void*)action->ActionName);
        free(action);
    }
}

static void DestroyMethod(SCHEMA_METHOD_HANDLE methodHandle)
{
    size_t nArguments = VECTOR_size(methodHandle->methodArguments);

    for (size_t j = 0; j < nArguments; j++)
    {
        SCHEMA_METHOD_ARGUMENT_HANDLE* methodArgumentHandle = VECTOR_element(methodHandle->methodArguments, j);
        if (methodArgumentHandle != NULL)
        {
            DestroyMethodArgument(*methodArgumentHandle);
        }
    }
    free(methodHandle->methodName);
    VECTOR_destroy(methodHandle->methodArguments);
    free(methodHandle);
}

static void DestroyMethods(SCHEMA_MODEL_TYPE_HANDLE modelHandle)
{
    size_t nMethods = VECTOR_size(modelHandle->methods);

    for (size_t j = 0; j < nMethods; j++)
    {
        SCHEMA_METHOD_HANDLE* methodHandle = VECTOR_element(modelHandle->methods, j);
        if (methodHandle != NULL)
        {
            DestroyMethod(*methodHandle);
        }
    }
    VECTOR_destroy(modelHandle->methods);
}


static void DestroyStruct(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle)
{
    size_t i;
    SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle;
    if (structType != NULL)
    {
        for (i = 0; i < structType->PropertyCount; i++)
        {
            DestroyProperty(structType->Properties[i]);
        }
        free(structType->Properties);

        free((void*)structType->Name);

        free(structType);
    }
}

static void DestroyModel(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
    size_t i, nReportedProperties;

    free((void*)modelType->Name);
    modelType->Name = NULL;

    for (i = 0; i < modelType->PropertyCount; i++)
    {
        DestroyProperty(modelType->Properties[i]);
    }

    free(modelType->Properties);

    for (i = 0; i < modelType->ActionCount; i++)
    {
        DestroyAction(modelType->Actions[i]);
    }

    DestroyMethods(modelType);

    /*destroy the vector holding the added models. This does not destroy the said models, however, their names shall be*/
    for (i = 0; i < VECTOR_size(modelType->models); i++)
    {
        MODEL_IN_MODEL* temp = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i);
        if (temp != NULL)
        {
            free((void*)temp->propertyName);
        }
    }

    nReportedProperties = VECTOR_size(modelType->reportedProperties);
    for (i = 0;i < nReportedProperties;i++)
    {
        SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = *(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA **)VECTOR_element(modelType->reportedProperties, i);
        free((void*)reportedProperty->reportedPropertyName);
        free((void*)reportedProperty->reportedPropertyType);
        free(reportedProperty);
    }
    VECTOR_destroy(modelType->reportedProperties);

    size_t nDesiredProperties = VECTOR_size(modelType->desiredProperties);
    for (i = 0;i < nDesiredProperties;i++)
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = *(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA **)VECTOR_element(modelType->desiredProperties, i);
        free((void*)desiredProperty->desiredPropertyName);
        free((void*)desiredProperty->desiredPropertyType);
        free(desiredProperty);
    }
    VECTOR_destroy(modelType->desiredProperties);

    VECTOR_clear(modelType->models);
    VECTOR_destroy(modelType->models);

    free(modelType->Actions);
    free(modelType);
}

static SCHEMA_RESULT AddModelProperty(SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType, const char* name, const char* type)
{
    SCHEMA_RESULT result;

    if ((modelType == NULL) ||
        (name == NULL) ||
        (type == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        size_t i;

        for (i = 0; i < modelType->PropertyCount; i++)
        {
            SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i];
            if (strcmp(property->PropertyName, name) == 0)
            {
                break;
            }
        }

        if (i < modelType->PropertyCount)
        {
            result = SCHEMA_DUPLICATE_ELEMENT;
            LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
        else
        {
            SCHEMA_PROPERTY_HANDLE* newProperties;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_PROPERTY_HANDLE), safe_add_size_t(modelType->PropertyCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(modelType->Properties, realloc_size)) == NULL)
            {
                result = SCHEMA_ERROR;
                LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, result), realloc_size);
            }
            else
            {
                SCHEMA_PROPERTY_HANDLE_DATA* newProperty;

                modelType->Properties = newProperties;
                if ((newProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_PROPERTY_HANDLE_DATA))) == NULL)
                {
                    result = SCHEMA_ERROR;
                    LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&newProperty->PropertyName, name) != 0)
                    {
                        free(newProperty);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else if (mallocAndStrcpy_s((char**)&newProperty->PropertyType, type) != 0)
                    {
                        free((void*)newProperty->PropertyName);
                        free(newProperty);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else
                    {
                        modelType->Properties[modelType->PropertyCount] = (SCHEMA_PROPERTY_HANDLE)newProperty;
                        modelType->PropertyCount++;

                        result = SCHEMA_OK;
                    }
                }

                /* If possible, reduce the memory of over allocation */
                if (result != SCHEMA_OK)
                {
                    if (modelType->PropertyCount > 0)
                    {
                        SCHEMA_PROPERTY_HANDLE* oldProperties;
                        realloc_size = safe_multiply_size_t(sizeof(SCHEMA_PROPERTY_HANDLE), modelType->PropertyCount);
                        if (realloc_size == SIZE_MAX ||
                            (oldProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(modelType->Properties, realloc_size)) == NULL)
                        {
                            result = SCHEMA_ERROR;
                            LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, result), realloc_size);
                        }
                        else
                        {
                            modelType->Properties = oldProperties;
                        }
                    }
                    else
                    {
                        modelType->Properties = NULL;
                    }
                }
            }
        }
    }

    return result;
}

static bool SchemaHandlesMatch(const SCHEMA_HANDLE* handle, const SCHEMA_HANDLE* otherHandle)
{
    return (*handle == *otherHandle);
}

static bool SchemaNamespacesMatch(const SCHEMA_HANDLE* handle, const char* schemaNamespace)
{
    const SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)*handle;
    return (strcmp(schema->Namespace, schemaNamespace) == 0);
}

SCHEMA_HANDLE Schema_Create(const char* schemaNamespace, void* metadata)
{
    SCHEMA_HANDLE_DATA* result;
    if (
        (schemaNamespace == NULL)||
        (metadata == NULL)
        )
    {
        LogError("invalid arg const char* schemaNamespace=%p, void* metadata=%p",schemaNamespace, metadata);
        result = NULL;
    }
    else
    {
        if (g_schemas == NULL && (g_schemas = VECTOR_create(sizeof(SCHEMA_HANDLE_DATA*) ) ) == NULL)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
        }
        else if ((result = (SCHEMA_HANDLE_DATA*)calloc(1, sizeof(SCHEMA_HANDLE_DATA))) == NULL)
        {
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
        }
        else if (mallocAndStrcpy_s((char**)&result->Namespace, schemaNamespace) != 0)
        {
            free(result);
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
        }
        else if (VECTOR_push_back(g_schemas, &result, 1) != 0)
        {
            free((void*)result->Namespace);
            free(result);
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
        }
        else
        {
            result->ModelTypes = NULL;
            result->ModelTypeCount = 0;
            result->StructTypes = NULL;
            result->StructTypeCount = 0;
            result->metadata = metadata;
        }
    }

    return (SCHEMA_HANDLE)result;
}

size_t Schema_GetSchemaCount(void)
{
    return VECTOR_size(g_schemas);
}

SCHEMA_HANDLE Schema_GetSchemaByNamespace(const char* schemaNamespace)
{
    SCHEMA_HANDLE result = NULL;

    if (schemaNamespace != NULL)
    {
        SCHEMA_HANDLE* handle = (g_schemas==NULL)?NULL:(SCHEMA_HANDLE*)VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaNamespacesMatch, schemaNamespace);
        if (handle != NULL)
        {
            result = *handle;
        }
    }

    return result;
}

const char* Schema_GetSchemaNamespace(SCHEMA_HANDLE schemaHandle)
{
    const char* result;

    if (schemaHandle == NULL)
    {
        result = NULL;
    }
    else
    {
        result = ((SCHEMA_HANDLE_DATA*)schemaHandle)->Namespace;
    }

    return result;
}

void Schema_Destroy(SCHEMA_HANDLE schemaHandle)
{
    if (schemaHandle != NULL)
    {
        SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;
        size_t i;

        for (i = 0; i < schema->ModelTypeCount; i++)
        {
            DestroyModel(schema->ModelTypes[i]);
        }

        free(schema->ModelTypes);

        for (i = 0; i < schema->StructTypeCount; i++)
        {
            DestroyStruct(schema->StructTypes[i]);
        }

        free(schema->StructTypes);
        free((void*)schema->Namespace);
        free(schema);

        schema = (SCHEMA_HANDLE_DATA*)VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaHandlesMatch, &schemaHandle);
        if (schema != NULL)
        {
            VECTOR_erase(g_schemas, schema, 1);
        }
        // If the g_schema is empty then destroy it
        if (VECTOR_size(g_schemas) == 0)
        {
            VECTOR_destroy(g_schemas);
            g_schemas = NULL;
        }
    }
}

SCHEMA_RESULT Schema_DestroyIfUnused(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    SCHEMA_RESULT result;

    if (modelTypeHandle != NULL)
    {
        SCHEMA_HANDLE schemaHandle = Schema_GetSchemaForModelType(modelTypeHandle);
        if (schemaHandle == NULL)
        {
            result = SCHEMA_ERROR;
        }
        else
        {
            SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;
            size_t nIndex;

            for (nIndex = 0; nIndex < schema->ModelTypeCount; nIndex++)
            {
                SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)schema->ModelTypes[nIndex];
                if (modelType->DeviceCount > 0)
                    break;
            }
            if (nIndex == schema->ModelTypeCount)
            {
                Schema_Destroy(schemaHandle);
                result = SCHEMA_OK;
            }
            else
            {
                result = SCHEMA_MODEL_IN_USE;
            }
        }
    }
    else
    {
        result = SCHEMA_INVALID_ARG;
    }
    return result;
}

SCHEMA_RESULT Schema_AddDeviceRef(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    SCHEMA_RESULT result;
    if (modelTypeHandle == NULL)
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        model->DeviceCount++;
        result = SCHEMA_OK;
    }
    return result;
}

SCHEMA_RESULT Schema_ReleaseDeviceRef(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    SCHEMA_RESULT result;
    if (modelTypeHandle == NULL)
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        if (model->DeviceCount > 0)
        {
            model->DeviceCount--;
            result = SCHEMA_OK;
        }
        else
        {
result = SCHEMA_DEVICE_COUNT_ZERO;
LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
    }
    return result;
}

SCHEMA_MODEL_TYPE_HANDLE Schema_CreateModelType(SCHEMA_HANDLE schemaHandle, const char* modelName)
{
    SCHEMA_MODEL_TYPE_HANDLE result;

    if ((schemaHandle == NULL) ||
        (modelName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;

        size_t i;
        for (i = 0; i < schema->ModelTypeCount; i++)
        {
            SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)(schema->ModelTypes[i]);
            if (strcmp(model->Name, modelName) == 0)
            {
                break;
            }
        }

        if (i < schema->ModelTypeCount)
        {
            result = NULL;
            LogError("%s Model Name already exists", modelName);
        }

        else
        {
            SCHEMA_MODEL_TYPE_HANDLE* newModelTypes;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_MODEL_TYPE_HANDLE), safe_add_size_t(schema->ModelTypeCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newModelTypes = (SCHEMA_MODEL_TYPE_HANDLE*)realloc(schema->ModelTypes, realloc_size)) == NULL)
            {
                result = NULL;
                LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
            }
            else
            {
                SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType;
                schema->ModelTypes = newModelTypes;

                if ((modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)calloc(1, sizeof(SCHEMA_MODEL_TYPE_HANDLE_DATA))) == NULL)
                {

                    result = NULL;
                    LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                }
                else if (mallocAndStrcpy_s((char**)&modelType->Name, modelName) != 0)
                {
                    result = NULL;
                    free(modelType);
                    LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                }
                else
                {
                    modelType->models = VECTOR_create(sizeof(MODEL_IN_MODEL));
                    if (modelType->models == NULL)
                    {
                        LogError("unable to VECTOR_create");
                        free((void*)modelType->Name);
                        free((void*)modelType);
                        result = NULL;
                    }
                    else
                    {
                        if ((modelType->reportedProperties = VECTOR_create(sizeof(SCHEMA_REPORTED_PROPERTY_HANDLE))) == NULL)
                        {
                            LogError("failed to VECTOR_create (reported properties)");
                            VECTOR_destroy(modelType->models);
                            free((void*)modelType->Name);
                            free((void*)modelType);
                            result = NULL;

                        }
                        else
                        {
                            if ((modelType->desiredProperties = VECTOR_create(sizeof(SCHEMA_DESIRED_PROPERTY_HANDLE))) == NULL)
                            {
                                LogError("failure in VECTOR_create (desired properties)");
                                VECTOR_destroy(modelType->reportedProperties);
                                VECTOR_destroy(modelType->models);
                                free((void*)modelType->Name);
                                free((void*)modelType);
                                result = NULL;
                            }
                            else
                            {
                                if ((modelType->methods = VECTOR_create(sizeof(SCHEMA_METHOD_HANDLE))) == NULL)
                                {
                                    LogError("failure in VECTOR_create (desired properties)");
                                    VECTOR_destroy(modelType->desiredProperties);
                                    VECTOR_destroy(modelType->reportedProperties);
                                    VECTOR_destroy(modelType->models);
                                    free((void*)modelType->Name);
                                    free((void*)modelType);
                                    result = NULL;
                                }
                                else
                                {
                                    modelType->PropertyCount = 0;
                                    modelType->Properties = NULL;
                                    modelType->ActionCount = 0;
                                    modelType->Actions = NULL;
                                    modelType->SchemaHandle = schemaHandle;
                                    modelType->DeviceCount = 0;

                                    schema->ModelTypes[schema->ModelTypeCount] = modelType;
                                    schema->ModelTypeCount++;
                                    result = (SCHEMA_MODEL_TYPE_HANDLE)modelType;
                                }
                            }
                        }
                    }
                }

                /* If possible, reduce the memory of over allocation */
                if ((result == NULL) &&(schema->ModelTypeCount>0))
                {
                    SCHEMA_MODEL_TYPE_HANDLE* oldModelTypes;
                    realloc_size = safe_multiply_size_t(sizeof(SCHEMA_MODEL_TYPE_HANDLE), schema->ModelTypeCount);
                    if (realloc_size == SIZE_MAX ||
                        (oldModelTypes = (SCHEMA_MODEL_TYPE_HANDLE*)realloc(schema->ModelTypes, realloc_size)) == NULL)
                    {
                        LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
                    }
                    else
                    {
                        schema->ModelTypes = oldModelTypes;
                    }
                }
            }
        }
    }

    return result;
}

SCHEMA_HANDLE Schema_GetSchemaForModelType(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    SCHEMA_HANDLE result;

    if (modelTypeHandle == NULL)
    {
        result = NULL;
    }
    else
    {
        result = ((SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle)->SchemaHandle;
    }

    return result;
}

SCHEMA_RESULT Schema_AddModelProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName, const char* propertyType)
{
    return AddModelProperty((SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle, propertyName, propertyType);
}

static bool reportedPropertyExists(const void* element, const void* value)
{
    SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = *(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA**)element;
    return (strcmp(reportedProperty->reportedPropertyName, value) == 0);
}

SCHEMA_RESULT Schema_AddModelReportedProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyName, const char* reportedPropertyType)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL) ||
        (reportedPropertyName == NULL) ||
        (reportedPropertyType == NULL)
        )
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyName=%p, const char* reportedPropertyType=%p", modelTypeHandle, reportedPropertyName, reportedPropertyType);
        result = SCHEMA_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        if (VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyName) != NULL)
        {
            LogError("unable to add reportedProperty %s because it already exists", reportedPropertyName);
            result = SCHEMA_DUPLICATE_ELEMENT;
        }
        else
        {
            SCHEMA_REPORTED_PROPERTY_HANDLE_DATA* reportedProperty = (SCHEMA_REPORTED_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_REPORTED_PROPERTY_HANDLE_DATA));
            if (reportedProperty == NULL)
            {
                LogError("unable to malloc");
                result = SCHEMA_ERROR;
            }
            else
            {
                if (mallocAndStrcpy_s((char**)&reportedProperty->reportedPropertyName, reportedPropertyName) != 0)
                {
                    LogError("unable to mallocAndStrcpy_s");
                    free(reportedProperty);
                    result = SCHEMA_ERROR;
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&reportedProperty->reportedPropertyType, reportedPropertyType) != 0)
                    {
                        LogError("unable to mallocAndStrcpy_s");
                        free((void*)reportedProperty->reportedPropertyName);
                        free(reportedProperty);
                        result = SCHEMA_ERROR;
                    }
                    else
                    {
                        if (VECTOR_push_back(modelType->reportedProperties, &reportedProperty, 1) != 0)
                        {
                            LogError("unable to VECTOR_push_back");
                            free((void*)reportedProperty->reportedPropertyType);
                            free((void*)reportedProperty->reportedPropertyName);
                            free(reportedProperty);
                            result = SCHEMA_ERROR;
                        }
                        else
                        {
                            result = SCHEMA_OK;
                        }
                    }
                }
            }
        }
    }
    return result;
}

SCHEMA_ACTION_HANDLE Schema_CreateModelAction(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* actionName)
{
    SCHEMA_ACTION_HANDLE result;

    if ((modelTypeHandle == NULL) ||
        (actionName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        size_t i;

        for (i = 0; i < modelType->ActionCount; i++)
        {
            SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)modelType->Actions[i];
            if (strcmp(action->ActionName, actionName) == 0)
            {
                break;
            }
        }

        if (i < modelType->ActionCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_DUPLICATE_ELEMENT));
        }
        else
        {
            SCHEMA_ACTION_HANDLE* newActions;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_ACTION_HANDLE), safe_add_size_t(modelType->ActionCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newActions = (SCHEMA_ACTION_HANDLE*)realloc(modelType->Actions, realloc_size)) == NULL)
            {
                result = NULL;
                LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
            }
            else
            {
                SCHEMA_ACTION_HANDLE_DATA* newAction;
                modelType->Actions = newActions;

                if ((newAction = (SCHEMA_ACTION_HANDLE_DATA*)calloc(1, sizeof(SCHEMA_ACTION_HANDLE_DATA))) == NULL)
                {
                    result = NULL;
                    LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&newAction->ActionName, actionName) != 0)
                    {
                        free(newAction);
                        result = NULL;
                        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                    }
                    else
                    {
                        newAction->ArgumentCount = 0;
                        newAction->ArgumentHandles = NULL;

                        modelType->Actions[modelType->ActionCount] = newAction;
                        modelType->ActionCount++;
                        result = (SCHEMA_ACTION_HANDLE)(newAction);
                    }

                    /* If possible, reduce the memory of over allocation */
                    if (result == NULL)
                    {
                        if (modelType->ActionCount > 0)
                        {
                            SCHEMA_ACTION_HANDLE* oldActions;
                            realloc_size = safe_multiply_size_t(sizeof(SCHEMA_ACTION_HANDLE), modelType->ActionCount);
                            if (realloc_size == SIZE_MAX ||
                                (oldActions = (SCHEMA_ACTION_HANDLE*)realloc(modelType->Actions, realloc_size)) == NULL)
                            {
                                LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
                            }
                            else
                            {
                                modelType->Actions = oldActions;
                            }
                        }
                        else
                        {
                            modelType->Actions = NULL;
                        }
                    }
                }
            }
        }
    }
    return result;
}


static bool methodExists(const void* element, const void* value)
{
    const SCHEMA_METHOD_HANDLE* method = (const SCHEMA_METHOD_HANDLE*)element;
    return (strcmp((*method)->methodName, value) == 0);
}


SCHEMA_METHOD_HANDLE Schema_CreateModelMethod(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* methodName)
{
    SCHEMA_METHOD_HANDLE result;

    if ((modelTypeHandle == NULL) ||
        (methodName == NULL))
    {
        result = NULL;
        LogError("invalid argument: SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* methodName=%p", modelTypeHandle, methodName);
    }
    else
    {
        if (VECTOR_find_if(modelTypeHandle->methods, methodExists, methodName) != NULL)
        {
            LogError("method %s already exists", methodName);
            result = NULL;
        }
        else
        {
            result = malloc(sizeof(SCHEMA_METHOD_HANDLE_DATA));
            if (result == NULL)
            {
                LogError("failed to malloc");
                /*return as is*/
            }
            else
            {
                result->methodArguments = VECTOR_create(sizeof(SCHEMA_METHOD_ARGUMENT_HANDLE));
                if (result->methodArguments == NULL)
                {
                    LogError("failure in VECTOR_create");
                    free(result);
                    result = NULL;
                }
                else
                {
                    if (mallocAndStrcpy_s(&result->methodName, methodName) != 0)
                    {
                        LogError("failure in mallocAndStrcpy_s");
                        VECTOR_destroy(result->methodArguments);
                        free(result);
                        result = NULL;
                    }
                    else
                    {
                        if (VECTOR_push_back(modelTypeHandle->methods, &result, 1) != 0)
                        {
                            LogError("failure in VECTOR_push_back");
                            free(result->methodName);
                            VECTOR_destroy(result->methodArguments);
                            free(result);
                            result = NULL;
                        }
                        else
                        {
                            /*return as is*/
                        }
                    }
                }
            }
        }
    }
    return result;
}


SCHEMA_RESULT Schema_AddModelActionArgument(SCHEMA_ACTION_HANDLE actionHandle, const char* argumentName, const char* argumentType)
{
    SCHEMA_RESULT result;

    if ((actionHandle == NULL) ||
        (argumentName == NULL) ||
        (argumentType == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;

        size_t i;
        for (i = 0; i < action->ArgumentCount; i++)
        {
            SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)action->ArgumentHandles[i];
            if (strcmp((actionArgument->Name), argumentName) == 0)
            {
                break;
            }
        }

        if (i < action->ArgumentCount)
        {
            result = SCHEMA_DUPLICATE_ELEMENT;
            LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
        else
        {
            SCHEMA_ACTION_ARGUMENT_HANDLE* newArguments;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE), safe_add_size_t(action->ArgumentCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newArguments = (SCHEMA_ACTION_ARGUMENT_HANDLE*)realloc(action->ArgumentHandles, realloc_size)) == NULL)
            {
                result = SCHEMA_ERROR;
                LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, result), realloc_size);
            }
            else
            {
                action->ArgumentHandles = newArguments;

                SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* newActionArgument;

                action->ArgumentHandles = newArguments;
                if ((newActionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)malloc(sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE_DATA))) == NULL)
                {
                    result = SCHEMA_ERROR;
                    LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&newActionArgument->Name, argumentName) != 0)
                    {
                        free(newActionArgument);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else if (mallocAndStrcpy_s((char**)&newActionArgument->Type, argumentType) != 0)
                    {
                        free((void*)newActionArgument->Name);
                        free(newActionArgument);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else
                    {
                        action->ArgumentHandles[action->ArgumentCount] = newActionArgument;
                        action->ArgumentCount++;

                        result = SCHEMA_OK;
                    }
                }

                /* If possible, reduce the memory of over allocation */
                if (result == SCHEMA_ERROR)
                {
                    if (action->ArgumentCount > 0)
                    {
                        SCHEMA_ACTION_ARGUMENT_HANDLE* oldArguments;
                        realloc_size = safe_multiply_size_t(sizeof(SCHEMA_ACTION_ARGUMENT_HANDLE), action->ArgumentCount);
                        if (realloc_size == SIZE_MAX ||
                            (oldArguments = (SCHEMA_ACTION_ARGUMENT_HANDLE*)realloc(action->ArgumentHandles, realloc_size)) == NULL)
                        {
                            LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
                        }
                        else
                        {
                            action->ArgumentHandles = oldArguments;
                        }
                    }
                    else
                    {
                        action->ArgumentHandles = NULL;
                    }
                }
            }
        }
    }
    return result;
}

static bool methodFindArgumentByBame(const void* element, const void* value)
{
    /*element is a pointer to SCHEMA_METHOD_ARGUMENT_HANDLE*/
    const SCHEMA_METHOD_ARGUMENT_HANDLE* decodedElement = (const SCHEMA_METHOD_ARGUMENT_HANDLE*)element;
    const char* name = (const char*)value;
    return (strcmp((*decodedElement)->Name, name) == 0);
}

SCHEMA_RESULT Schema_AddModelMethodArgument(SCHEMA_METHOD_HANDLE methodHandle, const char* argumentName, const char* argumentType)
{
    SCHEMA_RESULT result;
    if ((methodHandle == NULL) ||
        (argumentName == NULL) ||
        (argumentType == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        if (VECTOR_find_if(methodHandle->methodArguments, methodFindArgumentByBame, argumentName) != NULL)
        {
            LogError("an argument with name %s already exists", argumentName);
            result = SCHEMA_INVALID_ARG;
        }
        else
        {
            SCHEMA_METHOD_ARGUMENT_HANDLE_DATA* argument = malloc(sizeof(SCHEMA_METHOD_ARGUMENT_HANDLE_DATA));
            if (argument == NULL)
            {
                LogError("failure to malloc");
                result = SCHEMA_ERROR;
            }
            else
            {
                if (mallocAndStrcpy_s(&argument->Name, argumentName) != 0)
                {
                    LogError("failure in mallocAndStrcpy_s");
                    free(argument);
                    result = SCHEMA_ERROR;
                }
                else
                {
                    if (mallocAndStrcpy_s(&argument->Type, argumentType) != 0)
                    {
                        LogError("failure in mallocAndStrcpy_s");
                        free(argument->Name);
                        free(argument);
                        result = SCHEMA_ERROR;
                    }
                    else
                    {
                        if (VECTOR_push_back(methodHandle->methodArguments, &argument, 1) != 0)
                        {
                            LogError("failure in VECTOR_push_back");
                            free(argument->Name);
                            free(argument->Type);
                            free(argument);
                            result = SCHEMA_ERROR;
                        }
                        else
                        {
                            result = SCHEMA_OK;
                        }
                    }
                }
            }
        }
    }
    return result;
}

SCHEMA_PROPERTY_HANDLE Schema_GetModelPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName)
{
    SCHEMA_PROPERTY_HANDLE result;

    if ((modelTypeHandle == NULL) ||
        (propertyName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        size_t i;
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        for (i = 0; i < modelType->PropertyCount; i++)
        {
            SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i];
            if (strcmp(modelProperty->PropertyName, propertyName) == 0)
            {
                break;
            }
        }

        if (i == modelType->PropertyCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND));
        }
        else
        {
            result = (SCHEMA_PROPERTY_HANDLE)(modelType->Properties[i]);
        }
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* propertyCount)
{
    SCHEMA_RESULT result;

    if ((modelTypeHandle == NULL) ||
        (propertyCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        *propertyCount = modelType->PropertyCount;

        result = SCHEMA_OK;
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelReportedPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* reportedPropertyCount)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL)||
        (reportedPropertyCount==NULL)
        )
    {
        LogError("SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t* reportedPropertyCount=%p", modelTypeHandle, reportedPropertyCount);
        result = SCHEMA_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        *reportedPropertyCount = VECTOR_size(modelType->reportedProperties);
        result = SCHEMA_OK;
    }
    return result;
}

SCHEMA_REPORTED_PROPERTY_HANDLE Schema_GetModelReportedPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyName)
{
    SCHEMA_REPORTED_PROPERTY_HANDLE result;
    if(
        (modelTypeHandle == NULL) ||
        (reportedPropertyName == NULL)
        )
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyName=%p", modelTypeHandle, reportedPropertyName);
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        if((result = VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyName))==NULL)
        {
            LogError("a reported property with name \"%s\" does not exist", reportedPropertyName);
        }
        else
        {
            /*return as is*/
        }
    }
    return result;
}

SCHEMA_REPORTED_PROPERTY_HANDLE Schema_GetModelReportedPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    SCHEMA_REPORTED_PROPERTY_HANDLE result;
    if (modelTypeHandle == NULL)
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t index=%lu", modelTypeHandle, (unsigned long)index);
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        if ((result = VECTOR_element(modelType->reportedProperties, index)) == NULL)
        {
            LogError("index %lu is invalid", (unsigned long)index);
        }
        else
        {
            /*return as is*/
        }
    }
    return result;
}

SCHEMA_PROPERTY_HANDLE Schema_GetModelPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    SCHEMA_PROPERTY_HANDLE result;
    SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

    if ((modelTypeHandle == NULL) ||
        (index >= modelType->PropertyCount))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = modelType->Properties[index];
    }

    return result;
}

SCHEMA_ACTION_HANDLE Schema_GetModelActionByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* actionName)
{
    SCHEMA_ACTION_HANDLE result;

    if ((modelTypeHandle == NULL) ||
        (actionName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        size_t i;
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        for (i = 0; i < modelType->ActionCount; i++)
        {
            SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)modelType->Actions[i];
            if (strcmp(modelAction->ActionName, actionName) == 0)
            {
                break;
            }
        }

        if (i == modelType->ActionCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND));
        }
        else
        {
            result = modelType->Actions[i];
        }
    }

    return result;
}

static bool matchModelMethod(const void* element, const void* value)
{
    /*element is a pointer to SCHEMA_METHOD_HANDLE_DATA*/
    const SCHEMA_METHOD_HANDLE* decodedElement = (const SCHEMA_METHOD_HANDLE* )element;
    const char* name = (const char*)value;
    return (strcmp((*decodedElement)->methodName, name) == 0);
}

SCHEMA_METHOD_HANDLE Schema_GetModelMethodByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* methodName)
{
    SCHEMA_METHOD_HANDLE result;

    if ((modelTypeHandle == NULL) ||
        (methodName == NULL))
    {
        result = NULL;
        LogError("invalid arguments SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* methodName=%p", modelTypeHandle, methodName);
    }
    else
    {
        SCHEMA_METHOD_HANDLE* found = VECTOR_find_if(modelTypeHandle->methods, matchModelMethod, methodName);
        if (found == NULL)
        {
            LogError("no such method by name = %s", methodName);
            result = NULL;
        }
        else
        {
            result = *found;
        }
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelActionCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* actionCount)
{
    SCHEMA_RESULT result;

    if ((modelTypeHandle == NULL) ||
        (actionCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result=%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        *actionCount = modelType->ActionCount;

        result = SCHEMA_OK;
    }

    return result;
}

SCHEMA_ACTION_HANDLE Schema_GetModelActionByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    SCHEMA_ACTION_HANDLE result;
    SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

    if ((modelType == NULL) ||
        (index >= modelType->ActionCount))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = modelType->Actions[index];
    }

    return result;
}

const char* Schema_GetModelActionName(SCHEMA_ACTION_HANDLE actionHandle)
{
    const char* result;

    if (actionHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_ACTION_HANDLE_DATA* action = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;
        result = action->ActionName;
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelActionArgumentCount(SCHEMA_ACTION_HANDLE actionHandle, size_t* argumentCount)
{
    SCHEMA_RESULT result;

    if ((actionHandle == NULL) ||
        (argumentCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result=%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;

        *argumentCount = modelAction->ArgumentCount;

        result = SCHEMA_OK;
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelMethodArgumentCount(SCHEMA_METHOD_HANDLE methodHandle, size_t* argumentCount)
{
    SCHEMA_RESULT result;

    if ((methodHandle == NULL) ||
        (argumentCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result=%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        *argumentCount = VECTOR_size(methodHandle->methodArguments);
        result = SCHEMA_OK;
    }

    return result;
}


SCHEMA_ACTION_ARGUMENT_HANDLE Schema_GetModelActionArgumentByName(SCHEMA_ACTION_HANDLE actionHandle, const char* actionArgumentName)
{
    SCHEMA_ACTION_ARGUMENT_HANDLE result;

    if ((actionHandle == NULL) ||
        (actionArgumentName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;
        size_t i;

        for (i = 0; i < modelAction->ArgumentCount; i++)
        {
            SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)modelAction->ArgumentHandles[i];
            if (strcmp(actionArgument->Name, actionArgumentName) == 0)
            {
                break;
            }
        }

        if (i == modelAction->ArgumentCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND));
        }
        else
        {
            result = modelAction->ArgumentHandles[i];
        }
    }

    return result;
}

SCHEMA_ACTION_ARGUMENT_HANDLE Schema_GetModelActionArgumentByIndex(SCHEMA_ACTION_HANDLE actionHandle, size_t argumentIndex)
{
    SCHEMA_ACTION_ARGUMENT_HANDLE result;
    SCHEMA_ACTION_HANDLE_DATA* modelAction = (SCHEMA_ACTION_HANDLE_DATA*)actionHandle;

    if ((actionHandle == NULL) ||
        (argumentIndex >= modelAction->ArgumentCount))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = modelAction->ArgumentHandles[argumentIndex];
    }

    return result;
}

SCHEMA_METHOD_ARGUMENT_HANDLE Schema_GetModelMethodArgumentByIndex(SCHEMA_METHOD_HANDLE methodHandle, size_t argumentIndex)
{
    SCHEMA_METHOD_ARGUMENT_HANDLE result;

    if (methodHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_METHOD_ARGUMENT_HANDLE *temp = VECTOR_element(methodHandle->methodArguments, argumentIndex);
        if (temp == NULL)
        {
            result = NULL;
        }
        else
        {
            result = *temp;
        }
    }

    return result;
}


const char* Schema_GetActionArgumentName(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle)
{
    const char* result;
    if (actionArgumentHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle;
        result = actionArgument->Name;
    }
    return result;
}

const char* Schema_GetMethodArgumentName(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle)
{
    const char* result;
    if (methodArgumentHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = methodArgumentHandle->Name;
    }
    return result;
}


const char* Schema_GetActionArgumentType(SCHEMA_ACTION_ARGUMENT_HANDLE actionArgumentHandle)
{
    const char* result;
    if (actionArgumentHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_ACTION_ARGUMENT_HANDLE_DATA* actionArgument = (SCHEMA_ACTION_ARGUMENT_HANDLE_DATA*)actionArgumentHandle;
        result = actionArgument->Type;
    }
    return result;
}

const char* Schema_GetMethodArgumentType(SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle)
{
    const char* result;
    if (methodArgumentHandle == NULL)
    {
        result = NULL;
        LogError("invalid argument SCHEMA_METHOD_ARGUMENT_HANDLE methodArgumentHandle=%p", methodArgumentHandle);
    }
    else
    {
        result = methodArgumentHandle->Type;
    }
    return result;
}


SCHEMA_STRUCT_TYPE_HANDLE Schema_CreateStructType(SCHEMA_HANDLE schemaHandle, const char* typeName)
{
    SCHEMA_STRUCT_TYPE_HANDLE result;
    SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;

    if ((schema == NULL) ||
        (typeName == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType;
        size_t i;

        for (i = 0; i < schema->StructTypeCount; i++)
        {
            structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)schema->StructTypes[i];
            if (strcmp(structType->Name, typeName) == 0)
            {
                break;
            }
        }

        if (i < schema->StructTypeCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_DUPLICATE_ELEMENT));
        }
        else
        {
            SCHEMA_STRUCT_TYPE_HANDLE* newStructTypes;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_STRUCT_TYPE_HANDLE), safe_add_size_t(schema->StructTypeCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newStructTypes = (SCHEMA_STRUCT_TYPE_HANDLE*)realloc(schema->StructTypes, realloc_size)) == NULL)
            {
                result = NULL;
                LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
            }
            else
            {
                schema->StructTypes = newStructTypes;
                if ((structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)calloc(1, sizeof(SCHEMA_STRUCT_TYPE_HANDLE_DATA))) == NULL)
                {
                    result = NULL;
                    LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                }
                else if (mallocAndStrcpy_s((char**)&structType->Name, typeName) != 0)
                {
                    result = NULL;
                    free(structType);
                    LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR));
                }
                else
                {
                    schema->StructTypes[schema->StructTypeCount] = structType;
                    schema->StructTypeCount++;
                    structType->PropertyCount = 0;
                    structType->Properties = NULL;

                    result = (SCHEMA_STRUCT_TYPE_HANDLE)structType;
                }

                /* If possible, reduce the memory of over allocation */
                if (result == NULL)
                {
                    if (schema->StructTypeCount > 0)
                    {
                        SCHEMA_STRUCT_TYPE_HANDLE *oldStructTypes;
                        realloc_size = safe_multiply_size_t(sizeof(SCHEMA_STRUCT_TYPE_HANDLE), schema->StructTypeCount);
                        if (realloc_size == SIZE_MAX ||
                            (oldStructTypes = (SCHEMA_STRUCT_TYPE_HANDLE*)realloc(schema->StructTypes, realloc_size)) == NULL)
                        {
                            result = NULL;
                            LogError("(Error code:%s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ERROR), realloc_size);
                        }
                        else
                        {
                            schema->StructTypes = oldStructTypes;
                        }
                    }
                    else
                    {
                        schema->StructTypes = NULL;
                    }
                }
            }
        }
    }

    return result;
}

const char* Schema_GetStructTypeName(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle)
{
    const char* result;

    if (structTypeHandle == NULL)
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = ((SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle)->Name;
    }

    return result;
}

SCHEMA_RESULT Schema_GetStructTypeCount(SCHEMA_HANDLE schemaHandle, size_t* structTypeCount)
{
    SCHEMA_RESULT result;

    if ((schemaHandle == NULL) ||
        (structTypeCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;
        *structTypeCount = schema->StructTypeCount;
        result = SCHEMA_OK;
    }

    return result;
}

SCHEMA_STRUCT_TYPE_HANDLE Schema_GetStructTypeByName(SCHEMA_HANDLE schemaHandle, const char* name)
{
    SCHEMA_STRUCT_TYPE_HANDLE result;
    SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;

    if ((schemaHandle == NULL) ||
        (name == NULL))
    {
        result = NULL;
        LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        size_t i;

        for (i = 0; i < schema->StructTypeCount; i++)
        {
            SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)schema->StructTypes[i];
            if (strcmp(structType->Name, name) == 0)
            {
                break;
            }
        }

        if (i == schema->StructTypeCount)
        {
            result = NULL;
            LogError("(Error code:%s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND));
        }
        else
        {
            result = schema->StructTypes[i];
        }
    }

    return result;
}

SCHEMA_STRUCT_TYPE_HANDLE Schema_GetStructTypeByIndex(SCHEMA_HANDLE schemaHandle, size_t index)
{
    SCHEMA_STRUCT_TYPE_HANDLE result;
    SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;

    if ((schemaHandle == NULL) ||
        (index >= schema->StructTypeCount))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {

        result = schema->StructTypes[index];
    }
    return result;
}

SCHEMA_RESULT Schema_AddStructTypeProperty(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, const char* propertyName, const char* propertyType)
{
    SCHEMA_RESULT result;

    if ((structTypeHandle == NULL) ||
        (propertyName == NULL) ||
        (propertyType == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        size_t i;
        SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle;

        for (i = 0; i < structType->PropertyCount; i++)
        {
            SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)structType->Properties[i];
            if (strcmp(property->PropertyName, propertyName) == 0)
            {
                break;
            }
        }

        if (i < structType->PropertyCount)
        {
            result = SCHEMA_DUPLICATE_ELEMENT;
            LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
        else
        {
            SCHEMA_PROPERTY_HANDLE* newProperties;
            size_t realloc_size = safe_multiply_size_t(sizeof(SCHEMA_PROPERTY_HANDLE), safe_add_size_t(structType->PropertyCount, 1));
            if (realloc_size == SIZE_MAX ||
                (newProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(structType->Properties, realloc_size)) == NULL)
            {
                result = SCHEMA_ERROR;
                LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, result), realloc_size);
            }
            else
            {
                SCHEMA_PROPERTY_HANDLE_DATA* newProperty;

                structType->Properties = newProperties;
                if ((newProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)malloc(sizeof(SCHEMA_PROPERTY_HANDLE_DATA))) == NULL)
                {
                    result = SCHEMA_ERROR;
                    LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&newProperty->PropertyName, propertyName) != 0)
                    {
                        free(newProperty);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else if (mallocAndStrcpy_s((char**)&newProperty->PropertyType, propertyType) != 0)
                    {
                        free((void*)newProperty->PropertyName);
                        free(newProperty);
                        result = SCHEMA_ERROR;
                        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
                    }
                    else
                    {
                        structType->Properties[structType->PropertyCount] = (SCHEMA_PROPERTY_HANDLE)newProperty;
                        structType->PropertyCount++;

                        result = SCHEMA_OK;
                    }
                }

                /* If possible, reduce the memory of over allocation */
                if (result != SCHEMA_OK)
                {
                    if (structType->PropertyCount > 0)
                    {
                        SCHEMA_PROPERTY_HANDLE *oldProperties;
                        realloc_size = safe_multiply_size_t(sizeof(SCHEMA_PROPERTY_HANDLE), structType->PropertyCount);
                        if (realloc_size == SIZE_MAX ||
                            (oldProperties = (SCHEMA_PROPERTY_HANDLE*)realloc(structType->Properties, realloc_size)) == NULL)
                        {
                            result = SCHEMA_ERROR;
                            LogError("(result = %s), size:%zu", MU_ENUM_TO_STRING(SCHEMA_RESULT, result), realloc_size);
                        }
                        else
                        {
                            structType->Properties = oldProperties;
                        }
                    }
                    else
                    {
                        structType->Properties = NULL;
                    }
                }
            }
        }
    }

    return result;
}

SCHEMA_PROPERTY_HANDLE Schema_GetStructTypePropertyByName(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, const char* propertyName)
{
    SCHEMA_PROPERTY_HANDLE result;

    if ((structTypeHandle == NULL) ||
        (propertyName == NULL))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        size_t i;
        SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle;

        for (i = 0; i < structType->PropertyCount; i++)
        {
            SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)structType->Properties[i];
            if (strcmp(modelProperty->PropertyName, propertyName) == 0)
            {
                break;
            }
        }

        if (i == structType->PropertyCount)
        {
            result = NULL;
            LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_ELEMENT_NOT_FOUND));
        }
        else
        {
            result = structType->Properties[i];
        }
    }

    return result;
}

SCHEMA_RESULT Schema_GetStructTypePropertyCount(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, size_t* propertyCount)
{
    SCHEMA_RESULT result;

    if ((structTypeHandle == NULL) ||
        (propertyCount == NULL))
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle;

        *propertyCount = structType->PropertyCount;

        result = SCHEMA_OK;
    }

    return result;
}

SCHEMA_PROPERTY_HANDLE Schema_GetStructTypePropertyByIndex(SCHEMA_STRUCT_TYPE_HANDLE structTypeHandle, size_t index)
{
    SCHEMA_PROPERTY_HANDLE result;
    SCHEMA_STRUCT_TYPE_HANDLE_DATA* structType = (SCHEMA_STRUCT_TYPE_HANDLE_DATA*)structTypeHandle;

    if ((structTypeHandle == NULL) ||
        (index >= structType->PropertyCount))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        result = structType->Properties[index];
    }

    return result;
}

const char* Schema_GetPropertyName(SCHEMA_PROPERTY_HANDLE propertyHandle)
{
    const char* result;

    if (propertyHandle == NULL)
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_PROPERTY_HANDLE_DATA* propertyType = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle;

        result = propertyType->PropertyName;
    }

    return result;
}

const char* Schema_GetPropertyType(SCHEMA_PROPERTY_HANDLE propertyHandle)
{
    const char* result;

    if (propertyHandle == NULL)
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_PROPERTY_HANDLE_DATA* modelProperty = (SCHEMA_PROPERTY_HANDLE_DATA*)propertyHandle;

        result = modelProperty->PropertyType;
    }

    return result;
}

SCHEMA_RESULT Schema_GetModelCount(SCHEMA_HANDLE schemaHandle, size_t* modelCount)
{
    SCHEMA_RESULT result;
    if ((schemaHandle == NULL) ||
        (modelCount == NULL) )
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(result = %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;
        *modelCount = schema->ModelTypeCount;
        result = SCHEMA_OK;
    }
    return result;
}

SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelByName(SCHEMA_HANDLE schemaHandle, const char* modelName)
{
    SCHEMA_MODEL_TYPE_HANDLE result;

    if ((schemaHandle == NULL) ||
        (modelName == NULL))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;
        size_t i;
        for (i = 0; i < schema->ModelTypeCount; i++)
        {
            SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)schema->ModelTypes[i];
            if (strcmp(modelName, modelType->Name)==0)
            {
                break;
            }
        }
        if (i == schema->ModelTypeCount)
        {
            result = NULL;
        }
        else
        {
            result = schema->ModelTypes[i];
        }
    }
    return result;
}

SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelByIndex(SCHEMA_HANDLE schemaHandle, size_t index)
{
    SCHEMA_MODEL_TYPE_HANDLE result;
    SCHEMA_HANDLE_DATA* schema = (SCHEMA_HANDLE_DATA*)schemaHandle;

    if ((schemaHandle == NULL) ||
        (index >= schema->ModelTypeCount))
    {
        result = NULL;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {

        result = schema->ModelTypes[index];
    }
    return result;
}

const char* Schema_GetModelName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle)
{
    const char* result;
    if (modelTypeHandle == NULL)
    {
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        result = modelType->Name;
    }
    return result;
}

SCHEMA_RESULT Schema_AddModelModel(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName, SCHEMA_MODEL_TYPE_HANDLE modelType, size_t offset, pfOnDesiredProperty onDesiredProperty)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL) ||
        (propertyName == NULL) ||
        (modelType == NULL)
        )
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, SCHEMA_INVALID_ARG));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* parentModel = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        MODEL_IN_MODEL temp;
        temp.modelHandle = modelType;
        temp.offset = offset;
        temp.onDesiredProperty = onDesiredProperty;
        if (mallocAndStrcpy_s((char**)&(temp.propertyName), propertyName) != 0)
        {
            result = SCHEMA_ERROR;
            LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
        else if (VECTOR_push_back(parentModel->models, &temp, 1) != 0)
        {
            free((void*)temp.propertyName);
            result = SCHEMA_ERROR;
            LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
        }
        else
        {

            result = SCHEMA_OK;
        }
    }
    return result;
}


SCHEMA_RESULT Schema_GetModelModelCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* modelCount)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL) ||
        (modelCount == NULL)
        )
    {
        result = SCHEMA_INVALID_ARG;
        LogError("(Error code: %s)", MU_ENUM_TO_STRING(SCHEMA_RESULT, result));
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        *modelCount = VECTOR_size(model->models);
        /*SRS_SCHEMA_99_168: [If the function succeeds, it shall return SCHEMA_OK.]*/
        result = SCHEMA_OK;
    }
    return result;
}

static bool matchModelName(const void* element, const void* value)
{
    MODEL_IN_MODEL* decodedElement = (MODEL_IN_MODEL*)element;
    const char* name = (const char*)value;
    return (strcmp(decodedElement->propertyName, name) == 0);
}

SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelModelByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName)
{
    SCHEMA_MODEL_TYPE_HANDLE result;
    if (
        (modelTypeHandle == NULL) ||
        (propertyName == NULL)
        )
    {
        result = NULL;
        LogError("error SCHEMA_INVALID_ARG");
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        void* temp = VECTOR_find_if(model->models, matchModelName, propertyName);
        if (temp == NULL)
        {
            LogError("specified propertyName not found (%s)", propertyName);
            result = NULL;
        }
        else
        {
            result = ((MODEL_IN_MODEL*)temp)->modelHandle;
        }
    }
    return result;
}

size_t Schema_GetModelModelByName_Offset(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName)
{
    size_t result;
    if (
        (modelTypeHandle == NULL) ||
        (propertyName == NULL)
        )
    {
        result = 0;
        LogError("error SCHEMA_INVALID_ARG");
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        void* temp = VECTOR_find_if(model->models, matchModelName, propertyName);
        if (temp == NULL)
        {
            LogError("specified propertyName not found (%s)", propertyName);
            result = 0;
        }
        else
        {
            result = ((MODEL_IN_MODEL*)temp)->offset;
        }
    }
    return result;
}

pfOnDesiredProperty Schema_GetModelModelByName_OnDesiredProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyName)
{
    pfOnDesiredProperty result;
    if (
        (modelTypeHandle == NULL) ||
        (propertyName == NULL)
        )
    {
        result = NULL;
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* propertyName=%p",modelTypeHandle, propertyName);
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        void* temp = VECTOR_find_if(model->models, matchModelName, propertyName);
        if (temp == NULL)
        {
            LogError("specified propertyName not found (%s)", propertyName);
            result = NULL;
        }
        else
        {
            result = ((MODEL_IN_MODEL*)temp)->onDesiredProperty;
        }
    }
    return result;

}

size_t Schema_GetModelModelByIndex_Offset(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    size_t result;
    if (modelTypeHandle == NULL)
    {
        result = 0;
        LogError("error SCHEMA_INVALID_ARG");
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        void* temp = VECTOR_element(model->models, index);
        if (temp == 0)
        {
            LogError("specified index [out of bounds] (%lu)", (unsigned long)index);
            result = 0;
        }
        else
        {
            result = ((MODEL_IN_MODEL*)temp)->offset;
        }
    }
    return result;
}

SCHEMA_MODEL_TYPE_HANDLE Schema_GetModelModelyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    SCHEMA_MODEL_TYPE_HANDLE result;
    if (modelTypeHandle == NULL)
    {
        result = NULL;
        LogError("error SCHEMA_INVALID_ARG");
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        size_t nModelsInModel;
        nModelsInModel = VECTOR_size(model->models);
        if (index < nModelsInModel)
        {
            result = ((MODEL_IN_MODEL*)VECTOR_element(model->models, index))->modelHandle;
        }
        else
        {
            LogError("attempted out of bounds access in array.");
            result = NULL; /*out of bounds array access*/
        }
    }
    return result;
}

const char* Schema_GetModelModelPropertyNameByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    const char* result;
    if (modelTypeHandle == NULL)
    {
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* model = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        size_t nModelsInModel;
        nModelsInModel = VECTOR_size(model->models);
        if (index < nModelsInModel)
        {
            result = ((MODEL_IN_MODEL*)VECTOR_element(model->models, index))->propertyName;
        }
        else
        {
            LogError("attempted out of bounds access in array.");
            result = NULL; /*out of bounds array access*/
        }
    }
    return result;
}

bool Schema_ModelPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* propertyPath)
{
    bool result;

    if ((modelTypeHandle == NULL) ||
        (propertyPath == NULL))
    {
        LogError("error SCHEMA_INVALID_ARG");
        result = false;
    }
    else
    {
        const char* slashPos;
        result = false;

        if (*propertyPath == '/')
        {
            propertyPath++;
        }

        do
        {
            const char* endPos;
            size_t i;
            size_t modelCount;
            SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

            slashPos = strchr(propertyPath, '/');
            endPos = slashPos;
            if (endPos == NULL)
            {
                endPos = &propertyPath[strlen(propertyPath)];
            }

            /* get the child-model */
            modelCount = VECTOR_size(modelType->models);
            for (i = 0; i < modelCount; i++)
            {
                MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i);
                if (childModel != NULL &&
                    (strncmp(childModel->propertyName, propertyPath, endPos - propertyPath) == 0) &&
                    (strlen(childModel->propertyName) == (size_t)(endPos - propertyPath)))
                {
                    /* found */
                    modelTypeHandle = childModel->modelHandle;
                    break;
                }
            }

            if (i < modelCount)
            {
                /* model found, check if there is more in the path */
                if (slashPos == NULL)
                {
                    /* this is the last one, so this is the thing we were looking for */
                    result = true;
                    break;
                }
                else
                {
                    /* continue looking, there's more  */
                    propertyPath = slashPos + 1;
                }
            }
            else
            {
                /* no model found, let's see if this is a property */
                for (i = 0; i < modelType->PropertyCount; i++)
                {
                    SCHEMA_PROPERTY_HANDLE_DATA* property = (SCHEMA_PROPERTY_HANDLE_DATA*)modelType->Properties[i];
                    if ((strncmp(property->PropertyName, propertyPath, endPos - propertyPath) == 0) &&
                        (strlen(property->PropertyName) == (size_t)(endPos - propertyPath)))
                    {
                        /* found property */
                        result = true;
                        break;
                    }
                }

                break;
            }
        } while (slashPos != NULL);
    }

    return result;
}

bool Schema_ModelReportedPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* reportedPropertyPath)
{
    bool result;

    if ((modelTypeHandle == NULL) ||
        (reportedPropertyPath == NULL))
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* reportedPropertyPath=%s", modelTypeHandle, MU_P_OR_NULL(reportedPropertyPath));
        result = false;
    }
    else
    {
        const char* slashPos;

        result = false;

        if (*reportedPropertyPath == '/')
        {
            reportedPropertyPath++;
        }

        do
        {
            const char* endPos;
            size_t i;
            size_t modelCount;
            SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

            slashPos = strchr(reportedPropertyPath, '/');
            endPos = slashPos;
            if (endPos == NULL)
            {
                endPos = &reportedPropertyPath[strlen(reportedPropertyPath)];
            }

            modelCount = VECTOR_size(modelType->models);
            for (i = 0; i < modelCount; i++)
            {
                MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i);
                if (childModel != NULL &&
                    (strncmp(childModel->propertyName, reportedPropertyPath, endPos - reportedPropertyPath) == 0) &&
                    (strlen(childModel->propertyName) == (size_t)(endPos - reportedPropertyPath)))
                {
                    /* found */
                    modelTypeHandle = childModel->modelHandle;
                    break;
                }
            }

            if (i < modelCount)
            {
                /* model found, check if there is more in the path */
                if (slashPos == NULL)
                {
                    /* this is the last one, so this is the thing we were looking for */
                    result = true;
                    break;
                }
                else
                {
                    /* continue looking, there's more  */
                    reportedPropertyPath = slashPos + 1;
                }
            }
            else
            {
                /* no model found, let's see if this is a property */
                result = (VECTOR_find_if(modelType->reportedProperties, reportedPropertyExists, reportedPropertyPath) != NULL);
                if (!result)
                {
                    LogError("no such reported property \"%s\"", reportedPropertyPath);
                }
                break;
            }
        } while (slashPos != NULL);
    }

    return result;
}

static bool desiredPropertyExists(const void* element, const void* value)
{
    SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = *(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA**)element;
    return (strcmp(desiredProperty->desiredPropertyName, value) == 0);
}

SCHEMA_RESULT Schema_AddModelDesiredProperty(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyName, const char* desiredPropertyType, pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE, pfDesiredPropertyInitialize desiredPropertyInitialize, pfDesiredPropertyDeinitialize desiredPropertyDeinitialize, size_t offset, pfOnDesiredProperty onDesiredProperty)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL) ||
        (desiredPropertyName == NULL) ||
        (desiredPropertyType == NULL) ||
        (desiredPropertyFromAGENT_DATA_TYPE == NULL) ||
        (desiredPropertyInitialize == NULL) ||
        (desiredPropertyDeinitialize== NULL)
        )
    {
        LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyName=%p, const char* desiredPropertyType=%p, pfDesiredPropertyFromAGENT_DATA_TYPE desiredPropertyFromAGENT_DATA_TYPE=%p, pfDesiredPropertyInitialize desiredPropertyInitialize=%p, pfDesiredPropertyDeinitialize desiredPropertyDeinitialize=%p, size_t offset=%lu",
            modelTypeHandle, desiredPropertyName, desiredPropertyType, desiredPropertyFromAGENT_DATA_TYPE, desiredPropertyInitialize, desiredPropertyDeinitialize, (unsigned long)offset);
        result = SCHEMA_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        if (VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, desiredPropertyName) != NULL)
        {
            LogError("unable to Schema_AddModelDesiredProperty because a desired property with the same name (%s) already exists", desiredPropertyName);
            result = SCHEMA_DUPLICATE_ELEMENT;
        }
        else
        {
            SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desiredProperty = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)calloc(1, sizeof(SCHEMA_DESIRED_PROPERTY_HANDLE_DATA));
            if (desiredProperty == NULL)
            {
                LogError("failure in malloc");
                result = SCHEMA_ERROR;
            }
            else
            {
                if (mallocAndStrcpy_s((char**)&desiredProperty->desiredPropertyName, desiredPropertyName) != 0)
                {
                    LogError("failure in mallocAndStrcpy_s");
                    free(desiredProperty);
                    result = SCHEMA_ERROR;
                }
                else
                {
                    if (mallocAndStrcpy_s((char**)&desiredProperty->desiredPropertyType, desiredPropertyType) != 0)
                    {
                        LogError("failure in mallocAndStrcpy_s");
                        free((void*)desiredProperty->desiredPropertyName);
                        free(desiredProperty);
                        result = SCHEMA_ERROR;
                    }
                    else
                    {
                        if (VECTOR_push_back(handleData->desiredProperties, &desiredProperty, 1) != 0)
                        {
                            LogError("failure in VECTOR_push_back");
                            free((void*)desiredProperty->desiredPropertyType);
                            free((void*)desiredProperty->desiredPropertyName);
                            free(desiredProperty);
                            result = SCHEMA_ERROR;
                        }
                        else
                        {
                            desiredProperty->desiredPropertyFromAGENT_DATA_TYPE = desiredPropertyFromAGENT_DATA_TYPE;
                            desiredProperty->desiredPropertInitialize = desiredPropertyInitialize;
                            desiredProperty->desiredPropertDeinitialize = desiredPropertyDeinitialize;
                            desiredProperty->onDesiredProperty = onDesiredProperty; /*NULL is a perfectly fine value*/
                            desiredProperty->offset = offset;
                            result = SCHEMA_OK;
                        }
                    }
                }
            }
        }
    }
    return result;
}


SCHEMA_RESULT Schema_GetModelDesiredPropertyCount(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t* desiredPropertyCount)
{
    SCHEMA_RESULT result;
    if (
        (modelTypeHandle == NULL) ||
        (desiredPropertyCount == NULL)
        )
    {
        LogError("invalid arg: SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t* desiredPropertyCount=%p", modelTypeHandle, desiredPropertyCount);
        result = SCHEMA_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        *desiredPropertyCount = VECTOR_size(handleData->desiredProperties);
        result = SCHEMA_OK;
    }
    return result;
}

SCHEMA_DESIRED_PROPERTY_HANDLE Schema_GetModelDesiredPropertyByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyName)
{
    SCHEMA_DESIRED_PROPERTY_HANDLE result;
    if (
        (modelTypeHandle == NULL) ||
        (desiredPropertyName == NULL)
        )
    {
        LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyName=%p", modelTypeHandle, desiredPropertyName);
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        SCHEMA_DESIRED_PROPERTY_HANDLE* temp = VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, desiredPropertyName);
        if (temp == NULL)
        {
            LogError("no such desired property by name %s", desiredPropertyName);
            result = NULL;
        }
        else
        {
            result = *temp;
        }
    }
    return result;
}

SCHEMA_DESIRED_PROPERTY_HANDLE Schema_GetModelDesiredPropertyByIndex(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, size_t index)
{
    SCHEMA_DESIRED_PROPERTY_HANDLE result;
    if (modelTypeHandle == NULL)
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, size_t index=%lu", modelTypeHandle, (unsigned long)index);
        result = NULL;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;
        SCHEMA_DESIRED_PROPERTY_HANDLE* temp = VECTOR_element(handleData->desiredProperties, index);
        if (temp == NULL)
        {
            LogError("VECTOR_element produced NULL (likely out of bounds index)");
            result = NULL;
        }
        else
        {
            result = *temp;
        }
    }
    return result;
}

bool Schema_ModelDesiredPropertyByPathExists(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* desiredPropertyPath)
{
    bool result;
    if (
        (modelTypeHandle == NULL) ||
        (desiredPropertyPath == NULL)
        )
    {
        LogError("invalid arg SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* desiredPropertyPath=%p", modelTypeHandle, desiredPropertyPath);
        result = false;
    }
    else
    {
        const char* slashPos;

        result = false;

        if (*desiredPropertyPath == '/')
        {
            desiredPropertyPath++;
        }

        do
        {
            const char* endPos;
            size_t i;
            size_t modelCount;
            SCHEMA_MODEL_TYPE_HANDLE_DATA* modelType = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

            slashPos = strchr(desiredPropertyPath, '/');
            endPos = slashPos;
            if (endPos == NULL)
            {
                endPos = &desiredPropertyPath[strlen(desiredPropertyPath)];
            }

            modelCount = VECTOR_size(modelType->models);
            for (i = 0; i < modelCount; i++)
            {
                MODEL_IN_MODEL* childModel = (MODEL_IN_MODEL*)VECTOR_element(modelType->models, i);
                if (childModel != NULL &&
                    (strncmp(childModel->propertyName, desiredPropertyPath, endPos - desiredPropertyPath) == 0) &&
                    (strlen(childModel->propertyName) == (size_t)(endPos - desiredPropertyPath)))
                {
                    /* found */
                    modelTypeHandle = childModel->modelHandle;
                    break;
                }
            }

            if (i < modelCount)
            {
                /* model found, check if there is more in the path */
                if (slashPos == NULL)
                {
                    /* this is the last one, so this is the thing we were looking for */
                    result = true;
                    break;
                }
                else
                {
                    /* continue looking, there's more  */
                    desiredPropertyPath = slashPos + 1;
                }
            }
            else
            {
                /* no model found, let's see if this is a property */
                result = (VECTOR_find_if(modelType->desiredProperties, desiredPropertyExists, desiredPropertyPath) != NULL);
                if (!result)
                {
                    LogError("no such desired property \"%s\"", desiredPropertyPath);
                }
                break;
            }
        } while (slashPos != NULL);
    }
    return result;
}

const char* Schema_GetModelDesiredPropertyType(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    const char* result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = NULL;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = desirePropertyHandleData->desiredPropertyType;
    }
    return result;
}

pfDesiredPropertyFromAGENT_DATA_TYPE Schema_GetModelDesiredProperty_pfDesiredPropertyFromAGENT_DATA_TYPE(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    pfDesiredPropertyFromAGENT_DATA_TYPE result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = NULL;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = desirePropertyHandleData->desiredPropertyFromAGENT_DATA_TYPE;
    }
    return result;
}

pfOnDesiredProperty Schema_GetModelDesiredProperty_pfOnDesiredProperty(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    pfOnDesiredProperty result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = NULL;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = desirePropertyHandleData->onDesiredProperty;
    }
    return result;
}

size_t Schema_GetModelDesiredProperty_offset(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    size_t result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = 0;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* desirePropertyHandleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = desirePropertyHandleData->offset;
    }
    return result;
}

static bool modelInModelExists(const void* element, const void* value)
{
    MODEL_IN_MODEL* modelInModel = (MODEL_IN_MODEL*)element;
    return (strcmp(modelInModel->propertyName, value) == 0);
}

SCHEMA_MODEL_ELEMENT Schema_GetModelElementByName(SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle, const char* elementName)
{
    SCHEMA_MODEL_ELEMENT result;
    if (
        (modelTypeHandle == NULL) ||
        (elementName == NULL)
        )
    {
        LogError("invalid argument SCHEMA_MODEL_TYPE_HANDLE modelTypeHandle=%p, const char* elementName=%p", modelTypeHandle, elementName);
        result.elementType = SCHEMA_SEARCH_INVALID_ARG;
    }
    else
    {
        SCHEMA_MODEL_TYPE_HANDLE_DATA* handleData = (SCHEMA_MODEL_TYPE_HANDLE_DATA*)modelTypeHandle;

        SCHEMA_DESIRED_PROPERTY_HANDLE* desiredPropertyHandle = VECTOR_find_if(handleData->desiredProperties, desiredPropertyExists, elementName);
        if (desiredPropertyHandle != NULL)
        {
            result.elementType = SCHEMA_DESIRED_PROPERTY;
            result.elementHandle.desiredPropertyHandle = *desiredPropertyHandle;
        }
        else
        {
            size_t nProcessedProperties = 0;
            SCHEMA_PROPERTY_HANDLE_DATA* property = NULL;
            for (size_t i = 0; i < handleData->PropertyCount;i++)
            {
                property = (SCHEMA_PROPERTY_HANDLE_DATA*)(handleData->Properties[i]);
                if (strcmp(property->PropertyName, elementName) == 0)
                {
                    i = handleData->PropertyCount; /*found it*/
                }
                else
                {
                    nProcessedProperties++;
                }
            }

            if (nProcessedProperties < handleData->PropertyCount)
            {
                result.elementType = SCHEMA_PROPERTY;
                result.elementHandle.propertyHandle = property;
            }
            else
            {

                SCHEMA_REPORTED_PROPERTY_HANDLE* reportedPropertyHandle = VECTOR_find_if(handleData->reportedProperties, reportedPropertyExists, elementName);
                if (reportedPropertyHandle != NULL)
                {
                    result.elementType = SCHEMA_REPORTED_PROPERTY;
                    result.elementHandle.reportedPropertyHandle = *reportedPropertyHandle;
                }
                else
                {

                    size_t nProcessedActions = 0;
                    SCHEMA_ACTION_HANDLE_DATA* actionHandleData = NULL;
                    for (size_t i = 0;i < handleData->ActionCount; i++)
                    {
                        actionHandleData = (SCHEMA_ACTION_HANDLE_DATA*)(handleData->Actions[i]);
                        if (strcmp(actionHandleData->ActionName, elementName) == 0)
                        {
                            i = handleData->ActionCount; /*get out quickly*/
                        }
                        else
                        {
                            nProcessedActions++;
                        }
                    }

                    if (nProcessedActions < handleData->ActionCount)
                    {
                        result.elementType = SCHEMA_MODEL_ACTION;
                        result.elementHandle.actionHandle = actionHandleData;
                    }
                    else
                    {
                        MODEL_IN_MODEL* modelInModel = VECTOR_find_if(handleData->models, modelInModelExists, elementName);
                        if (modelInModel != NULL)
                        {
                            result.elementType = SCHEMA_MODEL_IN_MODEL;
                            result.elementHandle.modelHandle = modelInModel->modelHandle;
                        }
                        else
                        {
                            result.elementType = SCHEMA_NOT_FOUND;
                        }
                    }
                }
            }
        }
    }
    return result;
}

pfDesiredPropertyDeinitialize Schema_GetModelDesiredProperty_pfDesiredPropertyDeinitialize(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    pfDesiredPropertyDeinitialize result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = NULL;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* handleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = handleData->desiredPropertDeinitialize;
    }
    return result;
}

pfDesiredPropertyInitialize Schema_GetModelDesiredProperty_pfDesiredPropertyInitialize(SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle)
{
    pfDesiredPropertyInitialize result;
    if (desiredPropertyHandle == NULL)
    {
        LogError("invalid argument SCHEMA_DESIRED_PROPERTY_HANDLE desiredPropertyHandle=%p", desiredPropertyHandle);
        result = NULL;
    }
    else
    {
        SCHEMA_DESIRED_PROPERTY_HANDLE_DATA* handleData = (SCHEMA_DESIRED_PROPERTY_HANDLE_DATA*)desiredPropertyHandle;
        result = handleData->desiredPropertInitialize;
    }
    return result;
}

static bool SchemaHasModel(const SCHEMA_HANDLE* schemaHandle, const char* modelName)
{
    return (Schema_GetModelByName(*schemaHandle, modelName) != NULL);
}


SCHEMA_HANDLE Schema_GetSchemaForModel(const char* modelName)
{
    SCHEMA_HANDLE result;
    if (modelName == NULL)
    {
        LogError("invalid arg const char* modelName=%p", modelName);
        result = NULL;
    }
    else
    {
        SCHEMA_HANDLE* temp = VECTOR_find_if(g_schemas, (PREDICATE_FUNCTION)SchemaHasModel, modelName);

        if (temp == NULL)
        {
            LogError("unable to find a schema that has a model named %s", modelName);
            result = NULL;
        }
        else
        {
            result = *temp;
        }
    }

    return result;
}

void* Schema_GetMetadata(SCHEMA_HANDLE schemaHandle)
{
    void* result;
    if (schemaHandle == NULL)
    {
        LogError("invalid arg SCHEMA_HANDLE schemaHandle=%p", schemaHandle);
        result = NULL;
    }
    else
    {
        result = ((SCHEMA_HANDLE_DATA*)schemaHandle)->metadata;
    }
    return result;
}
