/*
 *          File: queue.c
 *        Author: Robert I. Pitts <rip@cs.bu.edu>
 * Last Modified: March 9, 2000
 *         Topic: Queue - Array Implementation
 * ----------------------------------------------------------------
 */

#include <stdio.h>
#include <stdlib.h>
#include<pthread.h>

#include "utils_queue.h"


/*
 * struct queueCDT gives the implementation of a queue.
 * It holds the information that we need for each queue.
 */
typedef struct queueCDT {
	queueElementT *contents;
	int front;
	int count;

	int	elem_size;
	pthread_rwlock_t queueRWLock;
}queueCDT;

queueADT QueueCreate(const int elem_size)
{
	queueADT queue;

	queue = (queueADT)malloc(sizeof(queueCDT));

	if(queue == NULL) {
		fprintf(stderr, "Insufficient Memory for new Queue.\n");
		return NULL;  /* Exit program, returning error code. */
	}

	queue->contents = (queueElementT*)malloc(elem_size * sizeof(queueElementT));

	if(queue == NULL) {
		fprintf(stderr, "Insufficient Memory for QueueElems.\n");
		return NULL;  /* Exit program, returning error code. */
	}

	queue->front = 0;
	queue->count = 0;

	queue->elem_size = elem_size;
	pthread_rwlock_init(&(queue->queueRWLock), NULL);

	return queue;
}

void QueueDestroy(queueADT queue)
{
	free(queue->contents);
	free(queue);
}

int QueueEnter(queueADT queue, queueElementT element)
{
	int newElementIndex;

	pthread_rwlock_wrlock(&queue->queueRWLock);

	if (queue->count >= queue->elem_size) {
		fprintf(stderr, "QueueEnter on Full Queue.\n");
		pthread_rwlock_unlock(&queue->queueRWLock);	
		return ERROR_QUEUE;  /* Exit program, returning error code. */
	}

	/*
	* Calculate index at which to put
	* next element.
	*/
	newElementIndex = (queue->front + queue->count) % queue->elem_size;
	queue->contents[newElementIndex] = element;

	queue->count++;

	pthread_rwlock_unlock(&queue->queueRWLock);
	return SUCCESS_QUEUE;
}

queueElementT QueueDelete(queueADT queue)
{
	queueElementT oldElement;

	pthread_rwlock_wrlock(&queue->queueRWLock);
	if (queue->count <= 0) {
		fprintf(stderr, "QueueDelete on Empty Queue.\n");
		pthread_rwlock_unlock(&queue->queueRWLock);
		return NULL;  /* Exit program, returning error code. */
	}

	/* Save the element so we can return it. */
	oldElement = queue->contents[queue->front];

	/*
	* Advance the index of the front,
	* making sure it wraps around the
	* array properly.
	*/
	queue->front++;
	queue->front %= queue->elem_size;

	queue->count--;
	pthread_rwlock_unlock(&queue->queueRWLock);
	return oldElement;
}

int QueueIsEmpty(queueADT queue)
{
	int rc;

	pthread_rwlock_rdlock(&queue->queueRWLock);

	rc = queue->count <= 0;

	pthread_rwlock_unlock(&queue->queueRWLock);
	return rc;
}

int QueueIsFull(queueADT queue)
{
	int rc;

	pthread_rwlock_rdlock(&queue->queueRWLock);

	rc = queue->count >= queue->elem_size;

	pthread_rwlock_unlock(&queue->queueRWLock);
	
	return rc;
}

