/*
 * coda_circolare.c
 */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

typedef struct {
    pthread_mutex_t mutex;
    pthread_cond_t  for_data, for_space;
    unsigned char max_len;
    unsigned char head, tail, size;
    int * data;
} t_queue;

void init_queue(t_queue * q, int max_len)
{
    pthread_mutex_init(&q->mutex, NULL);
    pthread_cond_init(&q->for_data, NULL);
    pthread_cond_init(&q->for_space, NULL);
    q->max_len = max_len;
    q->head = q->tail = q->size = 0;
    q->data = (int *)malloc(max_len * sizeof(int));
}

void put(t_queue * q, int value)
{
    pthread_mutex_lock(&q->mutex);

    while (q->size == q->max_len) {
        printf("Waiting for space...\n");
        pthread_cond_wait(&q->for_space, &q->mutex);
    }

    q->data[q->tail] = value;
    q->size++;
    q->tail = (q->tail + 1) % q->max_len;

    pthread_cond_broadcast(&q->for_data);

    pthread_mutex_unlock(&q->mutex);
}


void get(t_queue * q, int * value)
{
    pthread_mutex_lock(&q->mutex);

    while (q->size == 0) {
        printf("Waiting for data...\n");
        pthread_cond_wait(&q->for_data, &q->mutex);
    }

    *value = q->data[q->head];
    q->size--;
    q->head = (q->head + 1) % q->max_len;

    pthread_cond_broadcast(&q->for_space);

    pthread_mutex_unlock(&q->mutex);
}

void delete_queue(t_queue * q)
{
    free(q->data);
    q->max_len = 0;
    q->head = q->tail = q->size = 0;
}


void * producer(void * x)
{
    int v;
    t_queue * q = (t_queue *)x;
    for (;;) {
        v = rand() % 30;
        put(q, v);
        printf("Put --> %d\n", v);
        sleep(1);
    }
}


void * consumer(void * x)
{
    int v;
    t_queue * q = (t_queue *)x;
    for (;;) {
        get(q, &v);
        printf("Get --> %d\n", v);
        //sleep(1);
    }
}




int main(int argc, char **argv)
{
    t_queue myqueue;
    pthread_t prod, cons;

    init_queue(&myqueue, 10);

    pthread_create(&prod, NULL, producer, (void *)&myqueue);
    pthread_create(&cons, NULL, consumer, (void *)&myqueue);

    pthread_join(prod, NULL);

    delete_queue(&myqueue);

    return 0;
}

