#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>

#include "util.h"

/* --8<-- ring_type */
typedef int T;

struct ring_buffer {
	size_t head, tail;
	T      array[8]; /* fit for your needs... */
};
/* -->8-- */

/* --8<-- ring_init */
void
ring_init(struct ring_buffer *rb)
{
	rb->head = rb->tail = 0;
}
/* -->8-- */

/* --8<-- ring_push_front */
bool
ring_push_front(struct ring_buffer *rb, T data)
{
	const size_t prev = (rb->tail + NELEM(rb->array) - 1) % NELEM(rb->array);

	if ( prev == rb->head ) {
		return false;
	}

	rb->array[prev] = data;
	rb->tail        = prev;

	return true;
}
/* -->8-- */

/* --8<-- ring_push_back */
bool
ring_push_back(struct ring_buffer *rb, T data)
{
	const size_t next = (rb->head + 1) % NELEM(rb->array);

	if ( next == rb->tail ) {
		return false;
	}

	rb->array[rb->head] = data;
	rb->head            = next;

	return true;
}
/* -->8-- */

/* --8<-- ring_pop_front */
bool
ring_pop_front(struct ring_buffer *rb, T *data)
{
	if ( rb->head == rb->tail ) {
		return false;
	}

	const size_t next = (rb->tail + 1) % NELEM(rb->array);

	*data    = rb->array[rb->tail];
	rb->tail = next;

	return true;
}
/* -->8-- */

/* --8<-- ring_pop_back */
bool
ring_pop_back(struct ring_buffer *rb, T *data)
{
	if ( rb->head == rb->tail ) {
		return false;
	}

	const size_t prev = (rb->head + NELEM(rb->array) - 1) % NELEM(rb->array);

	*data    = rb->array[prev];
	rb->head = prev;

	return true;
}
/* -->8-- */

/* --8<-- ring_put */
bool
ring_put(struct ring_buffer *rb, T data)
{
	return ring_push_back(rb, data);
}
/* -->8-- */

/* --8<-- ring_get */
bool
ring_get(struct ring_buffer *rb, T *data)
{
	return ring_pop_front(rb, data);
}
/* -->8-- */

void
debug_print(const char *msg, struct ring_buffer *rb)
{
	printf("%s: head: %zu, tail: %zu\n", msg, rb->head, rb->tail);
}

int
main(void)
{
#if 0
	struct ring_buffer rb;
	ring_init(&rb);
#else
	struct ring_buffer rb = { .head = 0, .tail = 0 };
#endif

	assert(ring_push_back(&rb, 1) == true); // 1
	assert(ring_push_back(&rb, 2) == true); // 1, 2
	assert(ring_push_back(&rb, 3) == true); // 1, 2, 3
	assert(ring_push_back(&rb, 4) == true); // 1, 2, 3, 4

	debug_print("Stand", &rb);

	assert(ring_push_front(&rb, 0) == true);  // 0, 1, 2, 3, 4
	assert(ring_push_front(&rb, -1) == true); // -1, 0, 1, 2, 3, 4

	debug_print("Stand", &rb);

	T temp;
	assert(ring_pop_back(&rb, &temp) == true);  // -1, 0, 1, 2, 3
	assert(ring_pop_front(&rb, &temp) == true); // 0, 1, 2, 3

	debug_print("Stand", &rb);

	assert(ring_push_back(&rb, 4) == true);   // 0, 1, 2, 3, 4
	assert(ring_push_back(&rb, 5) == true);   // 0, 1, 2, 3, 4, 5
	assert(ring_push_back(&rb, 6) == true);   // 0, 1, 2, 3, 4, 5, 6
	assert(ring_push_back(&rb, 7) == false);  // 0, 1, 2, 3, 4, 5, 6
	assert(ring_push_front(&rb, 7) == false); // 0, 1, 2, 3, 4, 5, 6

	while ( ring_pop_back(&rb, &temp) ) {
		printf("temp: %d\n", temp);
	}

	debug_print("Stand", &rb);

	return EXIT_SUCCESS;
}
