#include "rwqueue.h"

#define DEBUG 1

void rw_queue_init(rw_queue_t *q)
{
    Sem_init(&q->mutex, 0, 1);
    q->reading_count = 0;
    q->writing_count = 0;
    q->head = NULL;
    q->tail = NULL;
}

/*
 * Helper routine to insert element in queue
 * Assume exclusive access
 */
static void enqueue(rw_queue_t *q, rw_token_t *t)
{
    if (q->tail == NULL) {
	q->tail = q->head = t;
    } else {
	q->tail->next = t;
	q->tail = t;
    }
    t->next = NULL;
}

/*
 * Helper routine to retrieve element at head of queue
 */
static rw_token_t *peekqueue(rw_queue_t *q)
{
    return q->head;
}

/*
 * Helper routine to remove first element from queue
 */
static void dequeue(rw_queue_t *q)
{
    rw_token_t *t = q->head;
    if (t == NULL)
	return;
    q->head = t->next;
    if (q->head == NULL)
	q->tail = NULL;
}

void rw_queue_request_read(rw_queue_t *q, rw_token_t *t)
{
    bool block = false;
    P(&q->mutex);
    if (q->head == NULL && q->writing_count == 0)
	/* Can bypass queue */
	q->reading_count++;
    else {
	/* Must add to queue */
	t->is_reader = true;
	enqueue(q, t);
	Sem_init(&t->enable, 0, 0);
	block = true;
    }
    V(&q->mutex);
    if (block)
	P(&t->enable);
}

void rw_queue_request_write(rw_queue_t *q, rw_token_t *t)
{
    bool block = false;
    P(&q->mutex);
    if (q->head == NULL && q->writing_count == 0 && q->reading_count == 0)
	/* Can bypass queue */
	q->writing_count++;
    else {
	t->is_reader = false;
	enqueue(q, t);
	Sem_init(&t->enable, 0, 0);
	block = true;
    }
    V(&q->mutex);
    if (block)
	P(&t->enable);
}


void rw_queue_release(rw_queue_t *q)
{
    rw_token_t *t;
    P(&q->mutex);
#if DEBUG
    printf("Start of release: ");
    rw_queue_status(q);
#endif
    if (q->writing_count > 0) 
	q->writing_count--;
    else
	q->reading_count--;

    t = peekqueue(q);
    if (!t) {
	V(&q->mutex);
	return;
    }
    bool wanttoread = t->is_reader;
    if (!wanttoread && q->reading_count == 0) {
	q->writing_count++;
	V(&t->enable);
	dequeue(q);
    } else {
	while (wanttoread) {
	    q->reading_count++;
	    V(&t->enable);
	    dequeue(q);
	    t = peekqueue(q);
	    wanttoread = t && t->is_reader;
	}
    }
#if DEBUG
    printf("End of release: ");
    rw_queue_status(q);
#endif
    V(&q->mutex);
}

#if DEBUG
void rw_queue_status(rw_queue_t *q) {
    rw_token_t *t;
    char qbuf[1024];
    char *pos = qbuf;
    for (t = q->head; t; t = t->next)
	*pos++ = t->is_reader ? 'r' : 'w';
    *pos = '\0';
    printf("Readers = %d, Writers = %d, Queue=[%s]\n", q->reading_count, q->writing_count, qbuf);
}
#endif
