#pragma once

/*
 * machinarium.
 *
 * cooperative multitasking engine.
 */

#include <string.h>
#include <assert.h>

#include <machinarium/memory.h>

typedef struct mm_buf mm_buf_t;

struct mm_buf {
	char *start;
	char *pos;
	char *end;
};

static inline void mm_buf_init(mm_buf_t *buf)
{
	buf->start = NULL;
	buf->pos = NULL;
	buf->end = NULL;
}

static inline void mm_buf_free(mm_buf_t *buf)
{
	if (buf->start == NULL) {
		return;
	}
	mm_free(buf->start);
	buf->start = NULL;
	buf->pos = NULL;
	buf->end = NULL;
}

static inline int mm_buf_size(mm_buf_t *buf)
{
	return buf->end - buf->start;
}

static inline int mm_buf_used(mm_buf_t *buf)
{
	return buf->pos - buf->start;
}

static inline int mm_buf_unused(mm_buf_t *buf)
{
	return buf->end - buf->pos;
}

static inline void mm_buf_reset(mm_buf_t *buf)
{
	buf->pos = buf->start;
}

static inline int mm_buf_ensure(mm_buf_t *buf, int size)
{
	if (buf->end - buf->pos >= size) {
		return 0;
	}
	int sz = mm_buf_size(buf) * 2;
	int actual = mm_buf_used(buf) + size;
	if (actual > sz) {
		sz = actual;
	}
	char *p;
	p = mm_realloc(buf->start, sz);
	if (p == NULL) {
		return -1;
	}
	buf->pos = p + (buf->pos - buf->start);
	buf->end = p + sz;
	buf->start = p;
	assert((buf->end - buf->pos) >= size);
	return 0;
}

static inline void mm_buf_advance(mm_buf_t *buf, int size)
{
	buf->pos += size;
}

static inline int mm_buf_add(mm_buf_t *buf, void *pointer, int size)
{
	int rc = mm_buf_ensure(buf, size);
	if (rc == -1) {
		return -1;
	}
	memcpy(buf->pos, pointer, size);
	mm_buf_advance(buf, size);
	return 0;
}
