/*
 * Copyright (c) Likewise Software.  All rights Reserved.
 *
 * This library is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the license, or (at
 * your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser
 * General Public License for more details.  You should have received a copy
 * of the GNU Lesser General Public License along with this program.  If
 * not, see <http://www.gnu.org/licenses/>.
 *
 * LIKEWISE SOFTWARE MAKES THIS SOFTWARE AVAILABLE UNDER OTHER LICENSING
 * TERMS AS WELL.  IF YOU HAVE ENTERED INTO A SEPARATE LICENSE AGREEMENT
 * WITH LIKEWISE SOFTWARE, THEN YOU MAY ELECT TO USE THE SOFTWARE UNDER THE
 * TERMS OF THAT SOFTWARE LICENSE AGREEMENT INSTEAD OF THE TERMS OF THE GNU
 * LESSER GENERAL PUBLIC LICENSE, NOTWITHSTANDING THE ABOVE NOTICE.  IF YOU
 * HAVE QUESTIONS, OR WISH TO REQUEST A COPY OF THE ALTERNATE LICENSING
 * TERMS OFFERED BY LIKEWISE SOFTWARE, PLEASE CONTACT LIKEWISE SOFTWARE AT
 * license@likewisesoftware.com
 */

/*
 * Module Name:
 *
 *        test-data.c
 *
 * Abstract:
 *
 *        Data model unit tests
 *
 * Authors: Brian Koropoff (bkoropoff@likewise.com)
 *
 */

#include <config.h>
#include <lwmsg/lwmsg.h>
#include <moonunit/moonunit.h>
#include <string.h>

#include "test-private.h"

typedef struct FlexibleString
{
    size_t length;
    char value[];
} FlexibleString;

static LWMsgTypeSpec FlexibleString_spec[] =
{
    LWMSG_STRUCT_BEGIN(FlexibleString),
    LWMSG_MEMBER_UINT32(FlexibleString, length),
    LWMSG_MEMBER_ARRAY(FlexibleString, value, LWMSG_UINT8(char)),
    LWMSG_ATTR_LENGTH_MEMBER(FlexibleString, length),
    LWMSG_ATTR_ENCODING(""),
    LWMSG_STRUCT_END,
    LWMSG_TYPE_END
};

typedef enum ComplexTag
{
    COMPLEX_SIMPLE_STRING,
    COMPLEX_FLEXIBLE_STRING,
    COMPLEX_USHORT,
    COMPLEX_LONGINT
} ComplexTag;

static LWMsgTypeSpec ComplexTag_spec[] =
{
    LWMSG_ENUM_BEGIN(ComplexTag, 1, LWMSG_UNSIGNED),
    LWMSG_ENUM_VALUE(COMPLEX_SIMPLE_STRING),
    LWMSG_ENUM_VALUE(COMPLEX_FLEXIBLE_STRING),
    LWMSG_ENUM_VALUE(COMPLEX_USHORT),
    LWMSG_ENUM_VALUE(COMPLEX_LONGINT),
    LWMSG_ENUM_END,
    LWMSG_TYPE_END
};

typedef union ComplexUnion
{
    char* simple_string;
    FlexibleString* flexible_string;
    unsigned short ushort;
    long int longint;
} ComplexUnion;

static LWMsgTypeSpec ComplexUnion_spec[] =
{
    LWMSG_UNION_BEGIN(ComplexUnion),
    LWMSG_MEMBER_PSTR(ComplexUnion, simple_string),
    LWMSG_ATTR_TAG(COMPLEX_SIMPLE_STRING),
    LWMSG_MEMBER_POINTER(ComplexUnion, flexible_string, LWMSG_TYPESPEC(FlexibleString_spec)),
    LWMSG_ATTR_NOT_NULL,
    LWMSG_ATTR_TAG(COMPLEX_FLEXIBLE_STRING),
    LWMSG_MEMBER_UINT16(ComplexUnion, ushort),
    LWMSG_ATTR_TAG(COMPLEX_USHORT),
    LWMSG_MEMBER_INT32(ComplexUnion, longint),
    LWMSG_ATTR_TAG(COMPLEX_LONGINT),
    LWMSG_UNION_END,
    LWMSG_TYPE_END
};

typedef struct ComplexArray
{
    size_t count;
    ComplexTag tag;
    ComplexUnion* array;
} ComplexArray;

static LWMsgTypeSpec ComplexArray_spec[] =
{
    LWMSG_STRUCT_BEGIN(ComplexArray),
    LWMSG_MEMBER_UINT32(ComplexArray, count),
    LWMSG_MEMBER_TYPESPEC(ComplexArray, tag, ComplexTag_spec),
    LWMSG_MEMBER_POINTER_BEGIN(ComplexArray, array),
    LWMSG_TYPESPEC(ComplexUnion_spec),
    LWMSG_ATTR_DISCRIM(ComplexArray, tag),
    LWMSG_POINTER_END,
    LWMSG_ATTR_NOT_NULL,
    LWMSG_ATTR_LENGTH_MEMBER(ComplexArray, count),
    LWMSG_STRUCT_END,
    LWMSG_TYPE_END
};

static LWMsgDataContext* data_context = NULL;

MU_FIXTURE_SETUP(Visitor)
{
    MU_TRY(lwmsg_data_context_new(NULL, &data_context));
}

#define SIMPLE_STRING_CONST "simple string"
#define FLEXIBLE_STRING_CONST "flexible string"

static ComplexArray* complex_array;
static ComplexUnion* complex_array_array;
static char* simple_string;
static FlexibleString* flexible_string;
size_t array_index = 0;

static
void
visitor_clear(
    LWMsgDataVisitor* visitor
    )
{
    memset(visitor, 0, sizeof(visitor));
}

static
LWMsgStatus
visit_complex_tag_longint(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "COMPLEX_LONGINT");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_LONGINT);

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_tag_ushort(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "COMPLEX_USHORT");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_USHORT);

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_tag_flexible_string(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "COMPLEX_FLEXIBLE_STRING");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_FLEXIBLE_STRING);

    visitor_clear(visitor);
    visitor->visit_enum_value = visit_complex_tag_ushort;

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_tag_simple_string(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_ENUM_VALUE);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "COMPLEX_SIMPLE_STRING");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_SIMPLE_STRING);

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array_array_correlated(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array->count);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "count");

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array_union_correlated(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array->tag);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_ENUM);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "tag");

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_simple_string(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].simple_string);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_POINTER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_SIMPLE_STRING);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_length_strategy(type), LWMSG_LENGTH_ZERO);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_encoding(type), "");
    MU_ASSERT_EQUAL(MU_TYPE_STRING, *(char**) object, SIMPLE_STRING_CONST);

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_flexible_string_value_correlated(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].flexible_string->length);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 4);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(size_t));
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "length");

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}


static
LWMsgStatus
visit_flexible_string_value(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, complex_array_array[array_index].flexible_string->value);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_ARRAY);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_length_strategy(type), LWMSG_LENGTH_MEMBER);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_encoding(type), "");
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "value");
    MU_ASSERT_EQUAL(MU_TYPE_STRING, (char*) object, FLEXIBLE_STRING_CONST);

    inner.visit_integer = visit_flexible_string_value_correlated;
    MU_TRY(lwmsg_data_accept_correlated_visitor(context, type, object, &inner));

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_flexible_string_length(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].flexible_string->length);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 4);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(size_t));
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "length");

    visitor_clear(visitor);
    visitor->visit_array = visit_flexible_string_value;

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_flexible_string(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, complex_array_array[array_index].flexible_string);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_STRUCT);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "FlexibleString");

    inner.visit_integer = visit_flexible_string_length;

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_flexible_string_pointer(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].flexible_string);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_POINTER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_FLEXIBLE_STRING);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_length_strategy(type), LWMSG_LENGTH_STATIC);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_static_length(type), 1);

    inner.visit_struct = visit_flexible_string;

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_ushort(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].ushort);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_USHORT);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 2);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(unsigned short));
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_signage(type), LWMSG_UNSIGNED);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_longint(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index].longint);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, (unsigned int) lwmsg_type_get_tag(type), COMPLEX_LONGINT);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 4);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(long int));
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_signage(type), LWMSG_SIGNED);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_union(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array_array[array_index]);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_UNION);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "ComplexUnion");

    visitor_clear(&inner);
    inner.visit_enum = visit_complex_array_union_correlated;

    MU_TRY(lwmsg_data_accept_correlated_visitor(context, type, object, &inner));

    visitor_clear(&inner);
    switch (complex_array->tag)
    {
    case COMPLEX_SIMPLE_STRING:
        inner.visit_pointer = visit_simple_string;
        break;
    case COMPLEX_FLEXIBLE_STRING:
        inner.visit_pointer = visit_flexible_string_pointer;
        break;
    case COMPLEX_USHORT:
        inner.visit_integer = visit_ushort;
        break;
    case COMPLEX_LONGINT:
        inner.visit_integer = visit_longint;
        break;
    default:
        MU_ASSERT_NOT_REACHED();
    }

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    array_index++;

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array_array(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array->array);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_POINTER);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "array");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_length_strategy(type), LWMSG_LENGTH_MEMBER);
    MU_ASSERT(lwmsg_type_get_flags(type) & LWMSG_TYPE_FLAG_NOT_NULL);

    visitor_clear(&inner);
    inner.visit_integer = visit_complex_array_array_correlated;

    MU_TRY(lwmsg_data_accept_correlated_visitor(context, type, object, &inner));

    visitor_clear(&inner);

    inner.visit_union = visit_complex_union;

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array_tag(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array->tag);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_ENUM);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "ComplexTag");
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "tag");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 1);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(ComplexTag));
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_signage(type), LWMSG_UNSIGNED);
    MU_ASSERT(!lwmsg_type_has_enum_mask(type));
    MU_ASSERT(lwmsg_type_has_enum_value(type));

    switch (complex_array->tag)
    {
    case COMPLEX_SIMPLE_STRING:
        inner.visit_enum_value = visit_complex_tag_simple_string;
        break;
    case COMPLEX_FLEXIBLE_STRING:
        inner.visit_enum_value = visit_complex_tag_flexible_string;
        break;
    case COMPLEX_USHORT:
        inner.visit_enum_value = visit_complex_tag_ushort;
        break;
    case COMPLEX_LONGINT:
        inner.visit_enum_value = visit_complex_tag_longint;
        break;
    }

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    visitor_clear(visitor);
    visitor->visit_pointer = visit_complex_array_array;

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array_count(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, &complex_array->count);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_INTEGER);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_member_name(type), "count");
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_width(type), 4);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_size(type), sizeof(size_t));
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_signage(type), LWMSG_UNSIGNED);

    visitor_clear(visitor);
    visitor->visit_enum = visit_complex_array_tag;

    return LWMSG_STATUS_SUCCESS;
}

static
LWMsgStatus
visit_complex_array(
    LWMsgDataContext* context,
    LWMsgDataVisitor* visitor,
    LWMsgType* type,
    void* object
    )
{
    LWMsgDataVisitor inner = {0};

    MU_ASSERT_EQUAL(MU_TYPE_POINTER, object, complex_array);
    MU_ASSERT_EQUAL(MU_TYPE_INTEGER, lwmsg_type_get_kind(type), LWMSG_KIND_STRUCT);
    MU_ASSERT_EQUAL(MU_TYPE_STRING, lwmsg_type_get_name(type), "ComplexArray");

    inner.visit_integer = visit_complex_array_count;

    MU_TRY(lwmsg_data_accept_child_visitor(context, type, object, &inner));

    visitor_clear(visitor);

    return LWMSG_STATUS_SUCCESS;
}

MU_TEST(visitor, visit_complex_array_simple_string)
{
    union ComplexUnion my_array_array[2];
    ComplexArray my_array;
    LWMsgDataVisitor visitor = {0};

    simple_string = SIMPLE_STRING_CONST;
    complex_array_array = my_array_array;
    complex_array = &my_array;

    complex_array_array[0].simple_string = simple_string;
    complex_array_array[1].simple_string = simple_string;
    complex_array->count = 2;
    complex_array->tag = COMPLEX_SIMPLE_STRING;
    complex_array->array = complex_array_array;

    visitor.visit_struct = visit_complex_array;

    MU_TRY(lwmsg_data_accept_graph_visitor(data_context, ComplexArray_spec, complex_array, &visitor));
}

MU_TEST(visitor, visit_complex_array_flexible_string)
{
    union ComplexUnion my_array_array[2];
    ComplexArray my_array;
    union
    {
        FlexibleString flexible;
        char buffer[sizeof(FlexibleString) + sizeof(FLEXIBLE_STRING_CONST)];
    } u;
    LWMsgDataVisitor visitor = {0};

    flexible_string = &u.flexible;
    memcpy(flexible_string->value, FLEXIBLE_STRING_CONST, sizeof(FLEXIBLE_STRING_CONST));
    flexible_string->length = strlen(FLEXIBLE_STRING_CONST);
    complex_array_array = my_array_array;
    complex_array = &my_array;

    complex_array_array[0].flexible_string = flexible_string;
    complex_array_array[1].flexible_string = flexible_string;
    complex_array->count = 2;
    complex_array->tag = COMPLEX_FLEXIBLE_STRING;
    complex_array->array = complex_array_array;

    visitor.visit_struct = visit_complex_array;

    MU_TRY(lwmsg_data_accept_graph_visitor(data_context, ComplexArray_spec, complex_array, &visitor));
}

MU_TEST(visitor, visit_complex_array_ushort)
{
    union ComplexUnion my_array_array[2];
    ComplexArray my_array;
    LWMsgDataVisitor visitor = {0};

    complex_array_array = my_array_array;
    complex_array = &my_array;

    complex_array_array[0].ushort = 1;
    complex_array_array[1].ushort = 2;
    complex_array->count = 2;
    complex_array->tag = COMPLEX_USHORT;
    complex_array->array = complex_array_array;

    visitor.visit_struct = visit_complex_array;

    MU_TRY(lwmsg_data_accept_graph_visitor(data_context, ComplexArray_spec, complex_array, &visitor));
}

MU_TEST(visitor, visit_complex_array_longint)
{
    union ComplexUnion my_array_array[2];
    ComplexArray my_array;
    LWMsgDataVisitor visitor = {0};

    complex_array_array = my_array_array;
    complex_array = &my_array;

    complex_array_array[0].longint = -1;
    complex_array_array[1].longint = -2;
    complex_array->count = 2;
    complex_array->tag = COMPLEX_LONGINT;
    complex_array->array = complex_array_array;

    visitor.visit_struct = visit_complex_array;

    MU_TRY(lwmsg_data_accept_graph_visitor(data_context, ComplexArray_spec, complex_array, &visitor));
}

MU_TEST(type, type_rep_public_api)
{
    LWMsgDataContext* data_context = NULL;
    LWMsgProtocol* prot = NULL;
    LWMsgTypeRep* rep = NULL;
    void* new_rep = NULL;
    static LWMsgTypeSpec spec[] =
    {
        LWMSG_TYPEREP,
        LWMSG_TYPE_END
    };
    static const LWMsgTag tag = 0;
    LWMsgTypeSpec* new_spec = NULL;
    const char* name = NULL;
    char* text = NULL;
    size_t data_size = 0;
    void* data = NULL;

    MU_TRY(lwmsg_data_context_new(NULL, &data_context));
    MU_TRY(lwmsg_protocol_new(NULL, &prot));
    MU_TRY(lwmsg_type_rep_from_spec(NULL, spec, &rep));

    MU_TRY(lwmsg_data_print_graph_alloc(data_context, spec, rep, &text));
    MU_VERBOSE("%s", text);
    free(text);

    MU_TRY(lwmsg_protocol_add_type_rep(prot, tag, "TYPE_REP", rep));
    MU_TRY(lwmsg_protocol_add_type_rep(prot, tag, "TYPE_REP", rep));
    MU_TRY(lwmsg_protocol_get_message_name(prot, tag, &name));
    MU_ASSERT_EQUAL(MU_TYPE_STRING, name, "TYPE_REP");
    MU_TRY(lwmsg_protocol_get_message_type(prot, tag, &new_spec));

    MU_TRY(lwmsg_data_marshal_flat_alloc(data_context, spec, rep, &data, &data_size));
    MU_TRY(lwmsg_data_unmarshal_flat(data_context, new_spec, data, data_size, &new_rep));

    lwmsg_data_free_graph(data_context, new_spec, new_rep);
    lwmsg_protocol_delete(prot);
    lwmsg_data_context_delete(data_context);
    lwmsg_type_free_rep(NULL, rep);
}
