#include "lib/mlr_globals.h"
#include "lib/mlrutil.h"
#include "loop_stack.h"

#define INITIAL_SIZE 32

// Example states:

// num_allocated = 4         num_allocated = 4         num_allocated = 4         num_allocated = 4
// num_used = 1              num_used = 2              num_used = 3              num_used = 4
// num_used_minus_one = 0    num_used_minus_one = 1    num_used_minus_one = 2    num_used_minus_one = 3
//
// +---+                     +---+                     +---+                     +---+
// | 2 | 0  <--- top         | 2 | 0                   | 2 | 0                   | 2 | 0
// +---+                     +---+                     +---+                     +---+
// |///| 1                   | 0 | 1  <--- top         | 0 | 1                   | 0 |
// +---+                     +---+                     +---+                     +---+
// |///| 2                   |///| 2                   | 4 | 2  <--- top         | 4 |
// +---+                     +---+                     +---+                     +---+
// |///| 3                   |///| 3                   |///| 3                   | 6 | 3  <--- top
// +---+                     +---+                     +---+                     +---+

// ----------------------------------------------------------------
loop_stack_t* loop_stack_alloc() {
	loop_stack_t* pstack = mlr_malloc_or_die(sizeof(loop_stack_t));

	// Guard zone of one. As noted in the header file, set/get are intentionally not bounds-checked.
	// If set is called without push, or after final pop, we can at least not corrupt other code.
	pstack->num_used_minus_one = 0;
	pstack->num_allocated = INITIAL_SIZE;

	pstack->pframes = mlr_malloc_or_die(pstack->num_allocated * sizeof(int));
	memset(pstack->pframes, 0, pstack->num_allocated * sizeof(int));

	return pstack;
}

// ----------------------------------------------------------------
void loop_stack_free(loop_stack_t* pstack) {
	if (pstack == NULL)
		return;
	free(pstack->pframes);
	free(pstack);
}

// ----------------------------------------------------------------
void loop_stack_push(loop_stack_t* pstack) {
	if (pstack->num_used_minus_one >= pstack->num_allocated - 1) {
		pstack->num_allocated += INITIAL_SIZE;
		pstack->pframes = mlr_realloc_or_die(pstack->pframes, pstack->num_allocated * sizeof(int));
	}
	pstack->num_used_minus_one++;
	pstack->pframes[pstack->num_used_minus_one] = 0;
}

// ----------------------------------------------------------------
int loop_stack_pop(loop_stack_t* pstack) {
	MLR_INTERNAL_CODING_ERROR_IF(pstack->num_used_minus_one <= 0);
	int rv = pstack->pframes[pstack->num_used_minus_one];
	pstack->num_used_minus_one--;
	return rv;
}

// ----------------------------------------------------------------
// Not bounds-checked, as noted in the header file.
void loop_stack_set(loop_stack_t* pstack, int bits) {
	pstack->pframes[pstack->num_used_minus_one] |= bits;
}
void loop_stack_clear(loop_stack_t* pstack, int bits) {
	pstack->pframes[pstack->num_used_minus_one] &= ~bits;
}

// ----------------------------------------------------------------
// Not bounds-checked, as noted in the header file.
int loop_stack_get(loop_stack_t* pstack) {
	return pstack->pframes[pstack->num_used_minus_one];
}
