#include "utilities/Queue.h"
#include "unittest/TestSuite.h"

static void testCreate(void) {
	Queue * queue = Queue_create();
	TestCase_assertPointerNonNULL(queue);
	TestCase_assertUIntEqual(queue->itemCount, 0);
	TestCase_assertPointerNULL(queue->items);
	Queue_dispose(queue);
	
	Queue queueStruct;
	memset(&queueStruct, 0xFF, sizeof(queueStruct));
	Queue_init(&queueStruct);
	TestCase_assertUIntEqual(queueStruct.itemCount, 0);
	TestCase_assertPointerNULL(queueStruct.items);
	Queue_disposeContents(&queueStruct);
}

static void testMutate(void) {
	Queue * queue = Queue_create();
	
	Queue_push(queue, (void *) 1);
	TestCase_assertUIntEqual(queue->itemCount, 1);
	TestCase_assertPointerNonNULL(queue->items);
	TestCase_assertPointerEqual(queue->items[0], (void *) 1);
	Queue_push(queue, (void *) 2);
	TestCase_assertUIntEqual(queue->itemCount, 2);
	TestCase_assertPointerEqual(queue->items[0], (void *) 1);
	TestCase_assertPointerEqual(queue->items[1], (void *) 2);
	
	Queue_unshift(queue, (void *) 3);
	TestCase_assertUIntEqual(queue->itemCount, 3);
	TestCase_assertPointerEqual(queue->items[0], (void *) 3);
	TestCase_assertPointerEqual(queue->items[1], (void *) 1);
	TestCase_assertPointerEqual(queue->items[2], (void *) 2);
	
	Queue_insert(queue, (void *) 4, 2);
	TestCase_assertUIntEqual(queue->itemCount, 4);
	TestCase_assertPointerEqual(queue->items[0], (void *) 3);
	TestCase_assertPointerEqual(queue->items[1], (void *) 1);
	TestCase_assertPointerEqual(queue->items[2], (void *) 4);
	TestCase_assertPointerEqual(queue->items[3], (void *) 2);
	
	void * result = Queue_pop(queue);
	TestCase_assertPointerEqual(result, (void *) 2);
	TestCase_assertUIntEqual(queue->itemCount, 3);
	TestCase_assertPointerEqual(queue->items[0], (void *) 3);
	TestCase_assertPointerEqual(queue->items[1], (void *) 1);
	TestCase_assertPointerEqual(queue->items[2], (void *) 4);
	
	result = Queue_remove(queue, 1);
	TestCase_assertPointerEqual(result, (void *) 1);
	TestCase_assertUIntEqual(queue->itemCount, 2);
	TestCase_assertPointerEqual(queue->items[0], (void *) 3);
	TestCase_assertPointerEqual(queue->items[1], (void *) 4);
	
	result = Queue_shift(queue);
	TestCase_assertPointerEqual(result, (void *) 3);
	TestCase_assertUIntEqual(queue->itemCount, 1);
	TestCase_assertPointerEqual(queue->items[0], (void *) 4);
	
	Queue_insert(queue, (void *) 5, 10);
	TestCase_assertUIntEqual(queue->itemCount, 2);
	TestCase_assertPointerEqual(queue->items[0], (void *) 4);
	TestCase_assertPointerEqual(queue->items[1], (void *) 5);
	
	Queue_removeAll(queue);
	TestCase_assertUIntEqual(queue->itemCount, 0);
	
	result = Queue_pop(queue);
	TestCase_assertPointerNULL(result);
	result = Queue_shift(queue);
	TestCase_assertPointerNULL(result);
	result = Queue_remove(queue, 0);
	TestCase_assertPointerNULL(result);
	
	Queue_dispose(queue);
}

static void testCopy(void) {
	Queue * queue = Queue_create();
	Queue_push(queue, (void *) 1);
	Queue_push(queue, (void *) 2);
	
	Queue * copy = Queue_copy(queue);
	TestCase_assertPointerNonNULL(copy);
	TestCase_assertPointerUnequal(copy, queue);
	
	Queue_push(queue, (void *) 3);
	TestCase_assertUIntEqual(copy->itemCount, 2);
	TestCase_assertPointerEqual(copy->items[0], (void *) 1);
	TestCase_assertPointerEqual(copy->items[1], (void *) 2);
	Queue_dispose(copy);
	
	copy = Queue_copy(queue);
	TestCase_assertPointerNonNULL(copy);
	TestCase_assertUIntEqual(copy->itemCount, 3);
	TestCase_assertPointerEqual(copy->items[0], (void *) 1);
	TestCase_assertPointerEqual(copy->items[1], (void *) 2);
	TestCase_assertPointerEqual(copy->items[2], (void *) 3);
	Queue_dispose(copy);
	
	Queue copyStruct;
	Queue_initCopy(&copyStruct, queue);
	Queue_dispose(queue);
	TestCase_assertUIntEqual(copyStruct.itemCount, 3);
	TestCase_assertPointerEqual(copyStruct.items[0], (void *) 1);
	TestCase_assertPointerEqual(copyStruct.items[1], (void *) 2);
	TestCase_assertPointerEqual(copyStruct.items[2], (void *) 3);
	Queue_disposeContents(&copyStruct);
}

static void testMerge(void) {
	Queue * queue1 = Queue_create();
	Queue * queue2 = Queue_create();
	Queue_push(queue1, (void *) 1);
	Queue_push(queue1, (void *) 2);
	Queue_push(queue2, (void *) 3);
	Queue_push(queue2, (void *) 4);
	Queue_merge(queue1, queue2);
	Queue_dispose(queue2);
	TestCase_assertUIntEqual(queue1->itemCount, 4);
	TestCase_assertPointerEqual(queue1->items[0], (void *) 1);
	TestCase_assertPointerEqual(queue1->items[1], (void *) 2);
	TestCase_assertPointerEqual(queue1->items[2], (void *) 3);
	TestCase_assertPointerEqual(queue1->items[3], (void *) 4);
	Queue_dispose(queue1);
}

struct foreachCallbackContext {
	unsigned int callCount;
	unsigned int falseReturnIndex;
	void * items[4];
};

static bool foreachCallback(Queue * queue, void * item, void * context) {
	struct foreachCallbackContext * contextStruct = context;
	contextStruct->items[contextStruct->callCount++] = item;
	return contextStruct->callCount < contextStruct->falseReturnIndex;
}

static void testForeach(void) {
	Queue * queue = Queue_create();
	Queue_push(queue, (void *) 16);
	Queue_push(queue, (void *) 32);
	struct foreachCallbackContext contextStruct = {0, 1, {NULL, NULL, NULL, NULL}};
	Queue_foreach(queue, foreachCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.callCount, 1);
	TestCase_assertPointerEqual(contextStruct.items[0], (void *) 16);
	
	contextStruct.callCount = 0;
	contextStruct.items[0] = NULL;
	Queue_foreachReverse(queue, foreachCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.callCount, 1);
	TestCase_assertPointerEqual(contextStruct.items[0], (void *) 32);
	
	Queue_push(queue, (void *) 48);
	contextStruct.callCount = 0;
	contextStruct.falseReturnIndex = 3;
	Queue_foreach(queue, foreachCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.callCount, 3);
	TestCase_assertPointerEqual(contextStruct.items[0], (void *) 16);
	TestCase_assertPointerEqual(contextStruct.items[1], (void *) 32);
	TestCase_assertPointerEqual(contextStruct.items[2], (void *) 48);
	
	contextStruct.callCount = 0;
	Queue_foreachReverse(queue, foreachCallback, &contextStruct);
	TestCase_assertUIntEqual(contextStruct.callCount, 3);
	TestCase_assertPointerEqual(contextStruct.items[0], (void *) 48);
	TestCase_assertPointerEqual(contextStruct.items[1], (void *) 32);
	TestCase_assertPointerEqual(contextStruct.items[2], (void *) 16);
	Queue_dispose(queue);
}

TEST_SUITE(QueueTest,
           testCreate,
           testMutate,
           testCopy,
           testMerge,
           testForeach)
