#ifndef MEM_H
#define MEM_H
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <stdint.h>
#include <stddef.h>

#define MT_NULL 65535

#include "interface.h"

#if UINTPTR_MAX == 0xFFFF
#define PTRSIZE 2
#define STOREPTR(b, p) {\
		(b)[0] = (p >> 8) & 0xff;\
		(b)[1] = p & 0xff;\
	}
#define GETPTR(b)\
	(void *)(\
		((uintptr_t)(b)[0] << 8) +\
		 (uintptr_t)(b)[1]\
	)
#elif UINTPTR_MAX == 0xFFFFFFFF
#define PTRSIZE 4
#define STOREPTR(b, p) {\
		(b)[0] = (p >> 24) & 0xff;\
		(b)[1] = (p >> 16) & 0xff;\
		(b)[2] = (p >> 8) & 0xff;\
		(b)[3] = p & 0xff;\
	}
#define GETPTR(b)\
	(void *)(\
		((uintptr_t)(b)[0] << 24) +\
		((uintptr_t)(b)[1] << 16) +\
		((uintptr_t)(b)[2] << 8) +\
		 (uintptr_t)(b)[3]\
	)
#elif UINTPTR_MAX == 0xFFFFFFFFFFFFFFFFu
#define PTRSIZE 8
#define STOREPTR(b, p) {\
		(b)[0] = (p >> 56) & 0xff;\
		(b)[1] = (p >> 48) & 0xff;\
		(b)[2] = (p >> 40) & 0xff;\
		(b)[3] = (p >> 32) & 0xff;\
		(b)[4] = (p >> 24) & 0xff;\
		(b)[5] = (p >> 16) & 0xff;\
		(b)[6] = (p >> 8) & 0xff;\
		(b)[7] = p & 0xff;\
	}
#define GETPTR(b)\
	(void *)(\
		((uintptr_t)(b)[0] << 56) +\
		((uintptr_t)(b)[1] << 48) +\
		((uintptr_t)(b)[2] << 40) +\
		((uintptr_t)(b)[3] << 32) +\
		((uintptr_t)(b)[4] << 24) +\
		((uintptr_t)(b)[5] << 16) +\
		((uintptr_t)(b)[6] << 8) +\
		 (uintptr_t)(b)[7]\
	)
#else /* UINTPTR_MAX == 0xFFFF */
#error TBD pointer size
#endif /* UINTPTR_MAX == 0xFFFF */

//* Convert mtask pointers to actual pointers
void *mem_ptr(uint16_t ptr);
//* Convert pointers to mtask pointers
uint16_t mem_rptr(void *ptr);
#define mem_cast_ptr(t, type) ((type)mem_ptr(t))
#define mem_cast_task(t) mem_cast_ptr(t, struct MTask *)
#define mem_cast_tree(t) mem_cast_ptr(t, struct TaskTree *)
#define mem_cast_bytes(t) mem_cast_ptr(t, uint8_t *)

//* Helper macro to get the next aligned cell
#ifdef REQUIRE_ALIGNED_MEMORY_ACCESS
#define PALIGND(sz, p) ((uintptr_t)(p) %\
	min((size_t) PTRSIZE, (size_t) sz) == 0 ? 0 :\
	(min((size_t) PTRSIZE,(size_t) sz)-(uintptr_t)(p) %\
	min((size_t) PTRSIZE, (size_t) sz)))
#define PALIGN(type, p) ((type *)((uintptr_t)(p) + PALIGND(sizeof(type), p)))
#else /* REQUIRE_ALIGNED_MEMORY_ACCESS */
#define PALIGND(sz, p) 0
#define PALIGN(type, p) ((type *)(p))
#endif /* REQUIRE_ALIGNED_MEMORY_ACCESS */

//* reset everything
void mem_reset(void);

//* stack value for which the stack overflows
uint16_t *mem_max_sp(void);
//* get the current stack pointer
uint16_t *mem_stack(void);
//* reset the stack pointer
void mem_set_stack_ptr(void *ptr);
//* increase the stack pointer (allocate tasks)
void *mem_alloc_task(size_t size);

//* Head of the task list
struct MTask *mem_task_head(void);
//* Next task list
struct MTask *mem_task_next(struct MTask *head);
//* Next task list but don't check whether there is enough space
struct MTask *mem_task_next_unsafe(struct MTask *head);

//* Allocate a tasktree
struct TaskTree *mem_alloc_tree(void);
//* Destroy and mark something and all their children as trash
void mem_mark_trash_and_destroy(struct TaskTree *tt, struct MTask *t,
				uint16_t *stack);

/**
 * Move the contents of a tasktree except the ptr while keeping the rptrs of
 * the children in tact
 */
void mem_node_move(struct TaskTree *target, struct TaskTree *source);

//* GC tasks
void mem_gc_tasks(void);
//* GC task trees
void mem_gc(void);

#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif /* !MEM_H */

