#ifndef BOUNDED_BUFFER_H_
#define BOUNDED_BUFFER_H_

#include <pthread.h>		// has pthread_ routines
#include <semaphore.h>		// has sem_ routines

// ---- Class for "buffer" (array of type T) of fixed size -----------

// This version of the class is implemented using semaphores.

template <typename T>
class boundedBuffer {
private:
  T * buffer;
  unsigned int bufferSize;
  pthread_mutex_t bufferLock;
  sem_t bufferNotEmpty;		// will be zero if buffer is empty
  sem_t bufferNotFull;		// will be zero if buffer is full
  // Implement as circular array:
  unsigned int nextToRemove;		// index of next element to remove
  unsigned int numElements;		// number of elements

public:
  // Constructor.
  boundedBuffer(const int sz) {
    bufferSize = sz;
    buffer = new T[sz];
    pthread_mutex_init(&bufferLock, NULL);
    sem_init(&bufferNotEmpty, 0, 0);
    sem_init(&bufferNotFull, 0, sz);
    nextToRemove = 0;
    numElements = 0;
  }
  // Destructor.
  ~boundedBuffer(void) {
    pthread_mutex_destroy(&bufferLock);
    sem_destroy(&bufferNotEmpty);
    sem_destroy(&bufferNotFull);
  }
private:
  // Copy constructor and assignment operator:  Make private so
  //   they can't be used (and we don't have to write them).
  boundedBuffer(const boundedBuffer & bb);
  boundedBuffer & operator= (const boundedBuffer & bb);
  
public:
  void put(const T itm) {
    sem_wait(&bufferNotFull);
    pthread_mutex_lock(&bufferLock);
    unsigned int next = (nextToRemove + numElements) % bufferSize;
    buffer[next] = itm;
    numElements++;
    pthread_mutex_unlock(&bufferLock);
    sem_post(&bufferNotEmpty);
  }
  T get(void) {
    sem_wait(&bufferNotEmpty);
    pthread_mutex_lock(&bufferLock);
    T returnVal = buffer[nextToRemove];
    nextToRemove = (nextToRemove + 1) % bufferSize;
    --numElements;
    pthread_mutex_unlock(&bufferLock);
    sem_post(&bufferNotFull);
    return returnVal;
  }
};

#endif // BOUNDED_BUFFER_H_