#ifndef AUDIOBUFFER_HPP
#define AUDIOBUFFER_HPP

#include <SDL.h>
#include <vector>
#include <cassert>

const unsigned int BufferBlockSize = 1000;

template< typename NUM >
class AudioBuffer {
public:
	AudioBuffer() : capacity(0), line_free_sem(NULL), line_sent_sem(NULL) {
	}
	void init(unsigned int _capacity) {
		capacity = _capacity;
		storage.resize(capacity * BufferBlockSize);
		assert(!line_free_sem);
		assert(!line_sent_sem);
		line_free_sem = SDL_CreateSemaphore(capacity);
		line_sent_sem = SDL_CreateSemaphore(0);
	}
	void deinit() {
		assert(line_free_sem);
		assert(line_sent_sem);
		SDL_DestroySemaphore(line_free_sem);
		SDL_DestroySemaphore(line_sent_sem);
		line_free_sem = line_sent_sem = NULL;
	}
	//called by thread providing lines:
	NUM *alloc_block() {
		//wait for a free line:
		if (SDL_SemWait(line_free_sem) != 0) assert(0);

		alloc_pt = (alloc_pt + 1) % capacity;
		return &(storage[alloc_pt * BufferBlockSize]);
	}
	void send_block(NUM *r) {
		assert(send_pt != alloc_pt);
		send_pt = (send_pt + 1) % capacity;
		assert(r == &(storage[send_pt * BufferBlockSize]));

		//post 'line sent' semaphore:
		if (SDL_SemPost(line_sent_sem) != 0) assert(0);
	}
	//called by thread reading lines:
	NUM *try_receive_block() { //non-blocking receive for, e.g., mixing thread.
		int ret = SDL_SemTryWait(line_sent_sem);
		if (ret == SDL_MUTEX_TIMEDOUT) return NULL;
		assert(ret == 0);
		receive_pt = (receive_pt + 1) % capacity;
		return &(storage[receive_pt * BufferBlockSize]);
	}
	NUM *receive_block() {
		//wait on 'line sent' semaphore:
		if (SDL_SemWait(line_sent_sem) != 0) assert(0);

		receive_pt = (receive_pt + 1) % capacity;
		return &(storage[receive_pt * BufferBlockSize]);
	}
	void free_block(NUM *r) {
		assert(free_pt != receive_pt);
		free_pt = (free_pt + 1) % capacity;
		assert(r == &(storage[free_pt * BufferBlockSize]));

		//post 'line free' semaphore:
		if (SDL_SemPost(line_free_sem) != 0) assert(0);
	}
private:
	unsigned int capacity;
	std::vector< NUM > storage;

	//key variables:
	unsigned int alloc_pt, send_pt, receive_pt, free_pt;
	//lockin':
	SDL_sem *line_free_sem;
	SDL_sem *line_sent_sem;
};

#endif //AUDIOBUFFER_HPP
