#include "utilities/FileBundle.h"
#include "utilities/printfFormats.h"
#include "unittest/TestSuite.h"
#include <limits.h>
#include <string.h>

static void testInit(void) {
	FileBundle * bundle;
	
	bundle = FileBundle_create();
	TestCase_assertPointerNonNULL(bundle);
	TestCase_assertUIntEqual(bundle->entryCount, 0);
	TestCase_assertPointerEqual(bundle->entries, NULL);
	FileBundle_dispose(bundle);
}

static void testMutate(void) {
	FileBundle * bundle;
	uint32_t size;
	char testData1[] = {0, 1, 2};
	char testData2[] = {3, 4};
	const char * returnedData, * returnedData2;
	bool success;
	const char * fileIdentifier;
	
	bundle = FileBundle_create();
	
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 0);
	TestCase_assertPointerNULL(FileBundle_getFileIdentifier(bundle, 0));
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file"), UINT_MAX);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file"), FILE_TYPE_INVALID);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 0), FILE_TYPE_INVALID);
	TestCase_assertPointerNULL(FileBundle_getFile(bundle, "test_file", &size));
	TestCase_assertPointerNULL(FileBundle_getFileAtIndex(bundle, 0, &size));
	
	success = FileBundle_removeFile(bundle, "test_file");
	TestCase_assertBoolFalse(success);
	success = FileBundle_removeFileAtIndex(bundle, 0);
	TestCase_assertBoolFalse(success);
	
	FileBundle_addFile(bundle, "test_file", FILE_TYPE_INVALID, testData1, sizeof(testData1), false, false);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 0);
	
	FileBundle_addFile(bundle, "test_file", 0, testData1, sizeof(testData1), false, false);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 1);
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 0);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "test_file");
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file"), 0);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file"), 0);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 0), 0);
	size = 0;
	TestCase_assertPointerEqual(FileBundle_getFile(bundle, "test_file", &size), testData1);
	TestCase_assertSizeEqual(size, sizeof(testData1));
	size = 0;
	TestCase_assertPointerEqual(FileBundle_getFileAtIndex(bundle, 0, &size), testData1);
	TestCase_assertSizeEqual(size, sizeof(testData1));
	
	TestCase_assertPointerNULL(FileBundle_getFileIdentifier(bundle, 1));
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file_2"), UINT_MAX);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file_2"), FILE_TYPE_INVALID);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 1), FILE_TYPE_INVALID);
	TestCase_assertPointerNULL(FileBundle_getFile(bundle, "test_file_2", &size));
	TestCase_assertPointerNULL(FileBundle_getFileAtIndex(bundle, 1, &size));
	
	FileBundle_addFile(bundle, "test_file_2", 3, testData2, sizeof(testData2), true, true);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 2);
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 1);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "test_file_2");
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file_2"), 1);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file_2"), 3);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 1), 3);
	size = 0;
	returnedData = FileBundle_getFile(bundle, "test_file_2", &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertPointerUnequal(returnedData, testData2);
	TestCase_assertSizeEqual(size, sizeof(testData2));
	TestCase_assert(!memcmp(returnedData, testData2, sizeof(testData2)), "Expected {%u, %u} but got {%u, %u}", testData2[0], testData2[1], returnedData[0], returnedData[1]);
	size = 0;
	returnedData2 = FileBundle_getFileAtIndex(bundle, 1, &size);
	TestCase_assertPointerEqual(returnedData2, returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData2));
	
	success = FileBundle_removeFile(bundle, "test_file");
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 1);
	
	TestCase_assertPointerNULL(FileBundle_getFileIdentifier(bundle, 1));
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file"), UINT_MAX);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file"), FILE_TYPE_INVALID);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 1), FILE_TYPE_INVALID);
	TestCase_assertPointerNULL(FileBundle_getFile(bundle, "test_file", &size));
	TestCase_assertPointerNULL(FileBundle_getFileAtIndex(bundle, 1, &size));
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 0);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "test_file_2");
	TestCase_assertUIntEqual(FileBundle_getFileIndex(bundle, "test_file_2"), 0);
	TestCase_assertIntEqual(FileBundle_getFileType(bundle, "test_file_2"), 3);
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 0), 3);
	size = 0;
	returnedData = FileBundle_getFile(bundle, "test_file_2", &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertPointerUnequal(returnedData, testData2);
	TestCase_assertSizeEqual(size, sizeof(testData2));
	TestCase_assert(!memcmp(returnedData, testData2, sizeof(testData2)), "Expected {%u, %u} but got {%u, %u}", testData2[0], testData2[1], returnedData[0], returnedData[1]);
	size = 0;
	returnedData2 = FileBundle_getFileAtIndex(bundle, 0, &size);
	TestCase_assertPointerEqual(returnedData2, returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData2));
	
	success = FileBundle_removeFile(bundle, "test_file");
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 1);
	
	success = FileBundle_removeFileAtIndex(bundle, 1);
	TestCase_assertBoolFalse(success);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 1);
	
	success = FileBundle_removeFileAtIndex(bundle, 0);
	TestCase_assertBoolTrue(success);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 0);
	
	FileBundle_dispose(bundle);
}

static void testIO(void) {
	FileBundle * bundle;
	char testData1[] = {5, 6, 7, 8};
	char testData2[] = {9, 10, 11};
	char testData3[] = {12, 13};
	void * serializedData;
	size_t serializedSize;
	const char * returnedData;
	uint32_t size;
	const char * fileIdentifier;
	
	bundle = FileBundle_create();
	FileBundle_addFile(bundle, "Test data 1", 3, testData1, sizeof(testData1), false, false);
	FileBundle_addFile(bundle, "Test data 2", 2, testData2, sizeof(testData2), false, false);
	FileBundle_addFile(bundle, "Test data 3", 5, testData3, sizeof(testData3), false, false);
	serializedSize = 0;
	serializedData = FileBundle_writeData(bundle, &serializedSize);
	FileBundle_dispose(bundle);
	
	TestCase_assertPointerNonNULL(serializedData);
	TestCase_assert(serializedSize > 0, "Expected nonzero size");
	
	bundle = FileBundle_loadData(serializedData, serializedSize);
	free(serializedData);
	TestCase_assertPointerNonNULL(bundle);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 3);
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 0);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "Test data 1");
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 0), 3);
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 1);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "Test data 2");
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 1), 2);
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 2);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "Test data 3");
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 2), 5);
	
	size = 0;
	returnedData = FileBundle_getFileAtIndex(bundle, 0, &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData1));
	TestCase_assert(!memcmp(returnedData, testData1, sizeof(testData1)), "Expected {%u, %u, %u, %u} but got {%u, %u, %u, %u}", testData1[0], testData1[1], testData1[2], testData1[3], returnedData[0], returnedData[1], returnedData[2], returnedData[3]);
	
	size = 0;
	returnedData = FileBundle_getFileAtIndex(bundle, 1, &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData2));
	TestCase_assert(!memcmp(returnedData, testData2, sizeof(testData2)), "Expected {%u, %u, %u} but got {%u, %u, %u}", testData2[0], testData2[1], testData2[2], returnedData[0], returnedData[1], returnedData[2]);
	
	size = 0;
	returnedData = FileBundle_getFileAtIndex(bundle, 2, &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData3));
	TestCase_assert(!memcmp(returnedData, testData3, sizeof(testData3)), "Expected {%u, %u} but got {%u, %u}", testData3[0], testData3[1], returnedData[0], returnedData[1]);
	
	FileBundle_dispose(bundle);
	
	
	bundle = FileBundle_create();
	FileBundle_addFile(bundle, "test_data_0", 6, testData3, sizeof(testData3), false, false);
	FileBundle_addFile(bundle, "", 1, testData1, sizeof(testData1), false, false);
	serializedSize = 0;
	serializedData = FileBundle_writeData(bundle, &serializedSize);
	FileBundle_dispose(bundle);
	
	TestCase_assertPointerNonNULL(serializedData);
	TestCase_assert(serializedSize > 0, "Expected nonzero size");
	
	bundle = FileBundle_loadData(serializedData, serializedSize);
	free(serializedData);
	TestCase_assertPointerNonNULL(bundle);
	TestCase_assertUIntEqual(FileBundle_getFileCount(bundle), 2);
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 0);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "test_data_0");
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 0), 6);
	
	fileIdentifier = FileBundle_getFileIdentifier(bundle, 1);
	TestCase_assertPointerNonNULL(fileIdentifier);
	TestCase_assertStringEqual(fileIdentifier, "");
	TestCase_assertIntEqual(FileBundle_getFileTypeAtIndex(bundle, 1), 1);
	
	size = 0;
	returnedData = FileBundle_getFileAtIndex(bundle, 0, &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData3));
	TestCase_assert(!memcmp(returnedData, testData3, sizeof(testData3)), "Expected {%u, %u} but got {%u, %u}", testData3[0], testData3[1], returnedData[0], returnedData[1]);
	
	size = 0;
	returnedData = FileBundle_getFileAtIndex(bundle, 1, &size);
	TestCase_assertPointerNonNULL(returnedData);
	TestCase_assertSizeEqual(size, sizeof(testData1));
	TestCase_assert(!memcmp(returnedData, testData1, sizeof(testData1)), "Expected {%u, %u, %u, %u} but got {%u, %u, %u, %u}", testData1[0], testData1[1], testData1[2], testData1[3], returnedData[0], returnedData[1], returnedData[2], returnedData[3]);
	
	FileBundle_dispose(bundle);
}

static void testGetNextFileIndexOfType(void) {
	FileBundle * bundle = FileBundle_create();
	char testData1[] = {5, 6, 7, 8};
	char testData2[] = {9, 10, 11};
	char testData3[] = {12, 13};
	FileBundle_addFile(bundle, "file1", 6, testData1, sizeof(testData1), false, false);
	FileBundle_addFile(bundle, "file2", 7, testData2, sizeof(testData2), false, false);
	FileBundle_addFile(bundle, "file3", 6, testData3, sizeof(testData3), false, false);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 0, 0), UINT_MAX);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 6, 0), 0);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 6, 1), 2);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 6, 2), 2);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 6, 3), UINT_MAX);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 7, 0), 1);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 7, 1), 1);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 7, 2), UINT_MAX);
	FileBundle_dispose(bundle);
	
	bundle = FileBundle_create();
	FileBundle_addFile(bundle, "file1", 2, testData1, sizeof(testData1), false, false);
	FileBundle_addFile(bundle, "file2", 2, testData2, sizeof(testData2), false, false);
	FileBundle_addFile(bundle, "file3", 5, testData3, sizeof(testData3), false, false);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 0, 0), UINT_MAX);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 2, 0), 0);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 2, 1), 1);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 2, 2), UINT_MAX);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 5, 0), 2);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 5, 1), 2);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 5, 2), 2);
	TestCase_assertUIntEqual(FileBundle_getNextFileIndexOfType(bundle, 5, 3), UINT_MAX);
	FileBundle_dispose(bundle);
}

TEST_SUITE(FileBundleTest,
           testInit,
           testMutate,
           testIO,
           testGetNextFileIndexOfType)
