#include "utilities/StrideArray.h"
#include "unittest/TestSuite.h"
#include <limits.h>

static void testInit(void) {
	StrideArray array = StrideArray_init(0, 1, NULL, false, false);
	TestCase_assertUIntEqual(array.count, 0);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNULL(array.data);
	StrideArray_free(&array);
	
	char * string = "ab";
	array = StrideArray_init(2, 1, string, false, false);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerEqual(array.data, string);
	StrideArray_free(&array);
	
	array = StrideArray_init(2, 1, string, true, true);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assertPointerUnequal(array.data, string);
	TestCase_assert(!memcmp(array.data, string, 2), "Expected \"ab\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	StrideArray_free(&array);
	
	char * string2 = "ABCDEFGHIJKL";
	array = StrideArray_init(3, 4, string2, true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 4);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assertPointerUnequal(array.data, string2);
	TestCase_assert(!memcmp(array.data, string2, 12), "Expected \"ABCDEFGHIJKL\" but got \"%c%c%c%c%c%c%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5], ((char *) array.data)[6], ((char *) array.data)[7], ((char *) array.data)[8], ((char *) array.data)[9], ((char *) array.data)[10], ((char *) array.data)[11]);
	StrideArray_free(&array);
	
	array = StrideArray_init(3, 4, NULL, true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 4);
	TestCase_assertPointerNonNULL(array.data);
	StrideArray_free(&array);
}

static void testCopy(void) {
	char * data = "abcd";
	StrideArray array = StrideArray_init(3, 1, data, false, false);
	StrideArray copy = StrideArray_copy(&array);
	TestCase_assertPointerNonNULL(copy.data);
	TestCase_assertPointerUnequal(copy.data, data);
	TestCase_assertUIntEqual(copy.count, 3);
	TestCase_assertUIntEqual(copy.stride, 1);
	TestCase_assertBytesEqual(copy.data, data, 3);
	StrideArray_free(&copy);
	
	array = StrideArray_init(2, 2, data, false, false);
	copy = StrideArray_copy(&array);
	TestCase_assertPointerNonNULL(copy.data);
	TestCase_assertPointerUnequal(copy.data, data);
	TestCase_assertUIntEqual(copy.count, 2);
	TestCase_assertUIntEqual(copy.stride, 2);
	TestCase_assertBytesEqual(copy.data, data, 4);
	StrideArray_free(&copy);
}

static void testAppend(void) {
	StrideArray array = StrideArray_init(0, 2, NULL, true, true);
	TestCase_assertUIntEqual(array.count, 0);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNULL(array.data);
	
	void * entry = StrideArray_append(&array, "ab");
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ab", 2), "Expected \"ab\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	TestCase_assertPointerEqual(entry, array.data);
	
	entry = StrideArray_append(&array, "cd");
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assert(!memcmp(array.data, "abcd", 4), "Expected \"abcd\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	TestCase_assertPointerEqual(entry, array.data + 2);
	
	entry = StrideArray_append(&array, NULL);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertPointerEqual(entry, array.data + 4);
	StrideArray_free(&array);
	
	array = StrideArray_init(1, 1, "A", true, true);
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(((char *) array.data)[0] == 'A', "Expected 'A' but got '%c'", ((char *) array.data)[0]);
	
	entry = StrideArray_append(&array, "B");
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "AB", 2), "Expected \"AB\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	TestCase_assertPointerEqual(entry, array.data + 1);
	
	entry = StrideArray_append(&array, "CD");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "ABC", 3), "Expected \"ABC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	TestCase_assertPointerEqual(entry, array.data + 2);
	
	entry = StrideArray_append(&array, NULL);
	TestCase_assertUIntEqual(array.count, 4);
	TestCase_assertPointerEqual(entry, array.data + 3);
	StrideArray_free(&array);
}

static void testInsert(void) {
	StrideArray array = StrideArray_init(0, 2, NULL, true, true);
	TestCase_assertUIntEqual(array.count, 0);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNULL(array.data);
	
	void * entry = StrideArray_insert(&array, 0, "ab");
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ab", 2), "Expected \"ab\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	TestCase_assertPointerEqual(entry, array.data);
	
	entry = StrideArray_insert(&array, 1, "cd");
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assert(!memcmp(array.data, "abcd", 4), "Expected \"abcd\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	TestCase_assertPointerEqual(entry, array.data + 2);
	
	entry = StrideArray_insert(&array, 1, "ef");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "abefcd", 6), "Expected \"abefcd\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	TestCase_assertPointerEqual(entry, array.data + 2);
	
	entry = StrideArray_insert(&array, 0, NULL);
	TestCase_assertUIntEqual(array.count, 4);
	TestCase_assert(!memcmp(array.data + 2, "abefcd", 6), "Expected \"abefcd\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5], ((char *) array.data)[6], ((char *) array.data)[7]);
	TestCase_assertPointerEqual(entry, array.data);
	StrideArray_free(&array);
	
	array = StrideArray_init(1, 1, "A", true, true);
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(((char *) array.data)[0] == 'A', "Expected 'A' but got '%c'", ((char *) array.data)[0]);
	
	entry = StrideArray_insert(&array, 0, "B");
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "BA", 2), "Expected \"BA\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	TestCase_assertPointerEqual(entry, array.data);
	
	entry = StrideArray_insert(&array, 2, "C");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "BAC", 3), "Expected \"BAC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	TestCase_assertPointerEqual(entry, array.data + 2);
	
	entry = StrideArray_insert(&array, 1, NULL);
	TestCase_assertUIntEqual(array.count, 4);
	TestCase_assertPointerEqual(entry, array.data + 1);
	*((char *) entry) = 'D';
	TestCase_assert(!memcmp(array.data, "BDAC", 4), "Expected \"BDAC\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	StrideArray_free(&array);
}

static void testRemove(void) {
	StrideArray array = StrideArray_init(3, 2, "abcdef", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "abcdef", 6), "Expected \"abcdef\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	
	StrideArray_remove(&array, 1);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assert(!memcmp(array.data, "abef", 4), "Expected \"abef\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	
	StrideArray_remove(&array, 0);
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assert(!memcmp(array.data, "ef", 2), "Expected \"ef\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	
	StrideArray_remove(&array, 0);
	TestCase_assertUIntEqual(array.count, 0);
	StrideArray_free(&array);
	
	array = StrideArray_init(3, 1, "ABC", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ABC", 3), "Expected \"ABC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	StrideArray_remove(&array, 0);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "BC", 2), "Expected \"BC\" but got \"%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1]);
	
	StrideArray_remove(&array, 1);
	TestCase_assertUIntEqual(array.count, 1);
	TestCase_assert(((char *) array.data)[0] == 'B', "Expected 'B' but got '%c'", ((char *) array.data)[0]);
	
	StrideArray_removeAll(&array);
	TestCase_assertUIntEqual(array.count, 0);
	StrideArray_free(&array);
	
	array = StrideArray_init(3, 2, "abcdef", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "abcdef", 6), "Expected \"abcdef\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	StrideArray_removeAll(&array);
	TestCase_assertUIntEqual(array.count, 0);
	StrideArray_free(&array);
}

static void testReplace(void) {
	StrideArray array = StrideArray_init(3, 2, "abcdef", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "abcdef", 6), "Expected \"abcdef\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	
	StrideArray_replace(&array, 1, "gh");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "abghef", 6), "Expected \"abghef\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	
	StrideArray_replace(&array, 0, "ij");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "ijghef", 6), "Expected \"ijghef\" but got \"%c%c%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3], ((char *) array.data)[4], ((char *) array.data)[5]);
	StrideArray_free(&array);
	
	array = StrideArray_init(3, 1, "ABC", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ABC", 3), "Expected \"ABC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	StrideArray_replace(&array, 0, "D");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "DBC", 3), "Expected \"DBC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	StrideArray_replace(&array, 1, "E");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "DEC", 3), "Expected \"DEC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	StrideArray_replace(&array, 3, "F");
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assert(!memcmp(array.data, "DEC", 3), "Expected \"DEC\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	StrideArray_free(&array);
}

static void testEntryAtIndex(void) {
	StrideArray array = StrideArray_init(3, 1, "abc", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "abc", 3), "Expected \"abc\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	TestCase_assertPointerEqual(StrideArray_entryAtIndex(&array, 0), array.data);
	TestCase_assertPointerEqual(StrideArray_entryAtIndex(&array, 1), array.data + 1);
	TestCase_assertPointerEqual(StrideArray_entryAtIndex(&array, 2), array.data + 2);
	TestCase_assertPointerNULL(StrideArray_entryAtIndex(&array, 3));
	StrideArray_free(&array);
	
	array = StrideArray_init(2, 2, "ABCD", true, true);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ABCD", 2), "Expected \"ABCD\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	
	TestCase_assertPointerEqual(StrideArray_entryAtIndex(&array, 0), array.data);
	TestCase_assertPointerEqual(StrideArray_entryAtIndex(&array, 1), array.data + 2);
	TestCase_assertPointerNULL(StrideArray_entryAtIndex(&array, 2));
	StrideArray_free(&array);
}

static void testIndexOfEntry(void) {
	StrideArray array = StrideArray_init(3, 1, "abc", true, true);
	TestCase_assertUIntEqual(array.count, 3);
	TestCase_assertUIntEqual(array.stride, 1);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "abc", 3), "Expected \"abc\" but got \"%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2]);
	
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data), 0);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 1), 1);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 2), 2);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 3), UINT_MAX);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, NULL), UINT_MAX);
	StrideArray_free(&array);
	
	array = StrideArray_init(2, 2, "ABCD", true, true);
	TestCase_assertUIntEqual(array.count, 2);
	TestCase_assertUIntEqual(array.stride, 2);
	TestCase_assertPointerNonNULL(array.data);
	TestCase_assert(!memcmp(array.data, "ABCD", 2), "Expected \"ABCD\" but got \"%c%c%c%c\"", ((char *) array.data)[0], ((char *) array.data)[1], ((char *) array.data)[2], ((char *) array.data)[3]);
	
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data), 0);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 2), 1);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 4), UINT_MAX);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 1), UINT_MAX);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, array.data + 3), UINT_MAX);
	TestCase_assertUIntEqual(StrideArray_indexOfEntry(&array, NULL), UINT_MAX);
	StrideArray_free(&array);
}

static void testAppendAll(void) {
	StrideArray array = StrideArray_init(4, 1, "1234", true, true);
	StrideArray array2 = StrideArray_init(2, 1, "567", false, false);
	StrideArray_appendAll(&array, &array2);
	TestCase_assertUIntEqual(array.count, 6);
	uint8_t * entry = StrideArray_entryAtIndex(&array, 0);
	TestCase_assertUIntEqual(*entry, '1');
	entry = StrideArray_entryAtIndex(&array, 1);
	TestCase_assertUIntEqual(*entry, '2');
	entry = StrideArray_entryAtIndex(&array, 2);
	TestCase_assertUIntEqual(*entry, '3');
	entry = StrideArray_entryAtIndex(&array, 3);
	TestCase_assertUIntEqual(*entry, '4');
	entry = StrideArray_entryAtIndex(&array, 4);
	TestCase_assertUIntEqual(*entry, '5');
	entry = StrideArray_entryAtIndex(&array, 5);
	TestCase_assertUIntEqual(*entry, '6');
	
	array2.count = 3;
	StrideArray_appendAll(&array, &array2);
	TestCase_assertUIntEqual(array.count, 9);
	entry = StrideArray_entryAtIndex(&array, 0);
	TestCase_assertUIntEqual(*entry, '1');
	entry = StrideArray_entryAtIndex(&array, 1);
	TestCase_assertUIntEqual(*entry, '2');
	entry = StrideArray_entryAtIndex(&array, 2);
	TestCase_assertUIntEqual(*entry, '3');
	entry = StrideArray_entryAtIndex(&array, 3);
	TestCase_assertUIntEqual(*entry, '4');
	entry = StrideArray_entryAtIndex(&array, 4);
	TestCase_assertUIntEqual(*entry, '5');
	entry = StrideArray_entryAtIndex(&array, 5);
	TestCase_assertUIntEqual(*entry, '6');
	entry = StrideArray_entryAtIndex(&array, 6);
	TestCase_assertUIntEqual(*entry, '5');
	entry = StrideArray_entryAtIndex(&array, 7);
	TestCase_assertUIntEqual(*entry, '6');
	entry = StrideArray_entryAtIndex(&array, 8);
	TestCase_assertUIntEqual(*entry, '7');
	StrideArray_free(&array);
}

TEST_SUITE(StrideArrayTest,
           testInit,
           testCopy,
           testAppend,
           testInsert,
           testRemove,
           testReplace,
           testEntryAtIndex,
           testIndexOfEntry,
           testAppendAll)
