#include "renderer/VertexFormat.h"
#include "unittest/TestSuite.h"
#include "stem_core.h"

static void testCreate(void) {
	VertexFormat * vertexFormat = VertexFormat_create(0, NULL);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 0);
	TestCase_assertPointerNULL(vertexFormat->attributeTypes);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 0);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 0);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpec = {"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 3};
	vertexFormat = VertexFormat_create(1, &attributeTypeSpec);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 1);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 3);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 12);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpecs1[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	vertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs1), attributeTypeSpecs1);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 2);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 2);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[1].name, "normal");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].usageHint, ATTRIBUTE_USAGE_NORMAL);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, sizeof(float) * 2);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 5);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 20);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpecs2[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 3},
		{"color", ATTRIBUTE_TYPE_UINT8, ATTRIBUTE_USAGE_COLOR, 4},
		{"normal", ATTRIBUTE_TYPE_INT16_NORM, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	vertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs2), attributeTypeSpecs2);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 3);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[1].name, "color");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].dataType, ATTRIBUTE_TYPE_UINT8);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].usageHint, ATTRIBUTE_USAGE_COLOR);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].componentCount, 4);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, 12);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[2].name, "normal");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[2].dataType, ATTRIBUTE_TYPE_INT16_NORM);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[2].usageHint, ATTRIBUTE_USAGE_NORMAL);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[2].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[2].byteOffset, 16);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 10);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 22);
	VertexFormat_dispose(vertexFormat);
}

static void testCopy(void) {
	VertexFormat * originalVertexFormat = VertexFormat_create(0, NULL);
	VertexFormat * vertexFormat = VertexFormat_copy(originalVertexFormat);
	VertexFormat_dispose(originalVertexFormat);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 0);
	TestCase_assertPointerNULL(vertexFormat->attributeTypes);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 0);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 0);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpec = {"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 3};
	originalVertexFormat = VertexFormat_create(1, &attributeTypeSpec);
	vertexFormat = VertexFormat_copy(originalVertexFormat);
	VertexFormat_dispose(originalVertexFormat);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 1);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 3);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 12);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpecs1[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	originalVertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs1), attributeTypeSpecs1);
	vertexFormat = VertexFormat_copy(originalVertexFormat);
	VertexFormat_dispose(originalVertexFormat);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 2);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 2);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[1].name, "normal");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].usageHint, ATTRIBUTE_USAGE_NORMAL);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, sizeof(float) * 2);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 5);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 20);
	VertexFormat_dispose(vertexFormat);
	
	VertexAttributeTypeSpec attributeTypeSpecs2[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 3},
		{"color", ATTRIBUTE_TYPE_UINT8, ATTRIBUTE_USAGE_COLOR, 4},
		{"normal", ATTRIBUTE_TYPE_INT16_NORM, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	originalVertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs2), attributeTypeSpecs2);
	vertexFormat = VertexFormat_copy(originalVertexFormat);
	VertexFormat_dispose(originalVertexFormat);
	TestCase_assertPointerNonNULL(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeCount, 3);
	TestCase_assertPointerNonNULL(vertexFormat->attributeTypes);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[0].name, "position");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].dataType, ATTRIBUTE_TYPE_FLOAT);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[0].usageHint, ATTRIBUTE_USAGE_POSITION);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[1].name, "color");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].dataType, ATTRIBUTE_TYPE_UINT8);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[1].usageHint, ATTRIBUTE_USAGE_COLOR);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].componentCount, 4);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, 12);
	TestCase_assertStringEqual(vertexFormat->attributeTypes[2].name, "normal");
	TestCase_assertIntEqual(vertexFormat->attributeTypes[2].dataType, ATTRIBUTE_TYPE_INT16_NORM);
	TestCase_assertIntEqual(vertexFormat->attributeTypes[2].usageHint, ATTRIBUTE_USAGE_NORMAL);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[2].componentCount, 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[2].byteOffset, 16);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 10);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 22);
	VertexFormat_dispose(vertexFormat);
}

void testMatchesSpec(void) {
	VertexAttributeTypeSpec attributeTypeSpecs1[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs2[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 3},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs3[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 4}
	};
	VertexAttributeTypeSpec attributeTypeSpecs4[] = {
		{"some_other_name_for_position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"something_used_as_a_normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs5[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3},
		{"color", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_COLOR, 4}
	};
	VertexAttributeTypeSpec attributeTypeSpecs6[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_UNSPECIFIED, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs7[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_TEXTURE_COORDINATE, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs8[] = {
		{"position", ATTRIBUTE_TYPE_INT32_NORM, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexAttributeTypeSpec attributeTypeSpecs9[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_UINT32, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexFormat * vertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs1), attributeTypeSpecs1);
	TestCase_assertBoolTrue(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs1), attributeTypeSpecs1));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, 1, attributeTypeSpecs1));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs2), attributeTypeSpecs2));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs3), attributeTypeSpecs3));
	TestCase_assertBoolTrue(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs4), attributeTypeSpecs4));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs5), attributeTypeSpecs5));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs6), attributeTypeSpecs6));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs7), attributeTypeSpecs7));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs8), attributeTypeSpecs8));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs9), attributeTypeSpecs9));
	VertexFormat_dispose(vertexFormat);
	
	vertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs5), attributeTypeSpecs5);
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs1), attributeTypeSpecs1));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs2), attributeTypeSpecs2));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs3), attributeTypeSpecs3));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs4), attributeTypeSpecs4));
	TestCase_assertBoolTrue(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs5), attributeTypeSpecs5));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs6), attributeTypeSpecs6));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs7), attributeTypeSpecs7));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs8), attributeTypeSpecs8));
	TestCase_assertBoolFalse(VertexFormat_matchesSpec(vertexFormat, sizeof_count(attributeTypeSpecs9), attributeTypeSpecs9));
	VertexFormat_dispose(vertexFormat);
}

static void testUpdate(void) {
	VertexAttributeTypeSpec attributeTypeSpecs[] = {
		{"position", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_POSITION, 2},
		{"normal", ATTRIBUTE_TYPE_FLOAT, ATTRIBUTE_USAGE_NORMAL, 3}
	};
	VertexFormat * vertexFormat = VertexFormat_create(sizeof_count(attributeTypeSpecs), attributeTypeSpecs);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, sizeof(float) * 2);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 5);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 20);
	
	vertexFormat->attributeTypes[0].componentCount = 3;
	VertexFormat_update(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, sizeof(float) * 3);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 6);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 24);
	
	vertexFormat->attributeTypes = realloc(vertexFormat->attributeTypes, (vertexFormat->attributeCount + 1) * sizeof(*vertexFormat->attributeTypes));
	vertexFormat->attributeTypes[vertexFormat->attributeCount].name = strdup("color");
	vertexFormat->attributeTypes[vertexFormat->attributeCount].dataType = ATTRIBUTE_TYPE_FLOAT;
	vertexFormat->attributeTypes[vertexFormat->attributeCount].usageHint = ATTRIBUTE_USAGE_COLOR;
	vertexFormat->attributeTypes[vertexFormat->attributeCount].componentCount = 4;
	vertexFormat->attributeCount++;
	VertexFormat_update(vertexFormat);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[0].byteOffset, 0);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[1].byteOffset, sizeof(float) * 3);
	TestCase_assertUIntEqual(vertexFormat->attributeTypes[2].byteOffset, sizeof(float) * 6);
	TestCase_assertUIntEqual(vertexFormat->componentCountTotal, 10);
	TestCase_assertUIntEqual(vertexFormat->bytesPerVertex, 40);
	
	VertexFormat_dispose(vertexFormat);
}

TEST_SUITE(VertexFormatTest,
           testCreate,
           testCopy,
           testMatchesSpec,
           testUpdate)
