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

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

#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
#error TBD pointer size
#endif

uint16_t *mem_stack(void);
uint16_t mem_max_sp(void);

size_t mem_freeb(void);
void mem_reset(void);
void mem_report(void);

struct task *mem_alloc_task(
	uint8_t taskid,
	uint8_t returnwidth, uint16_t numsds, uint16_t bc, uint8_t peripherals);
struct tasktree *mem_alloc_tree(void);

//Garbage collection
void mem_gc(void);
void mem_mark_trash(uint16_t t);
void mem_mark_trash_deep(uint16_t t, uint16_t *stack);

/**
 * Destructively update the pointer
 *
 * @param tree to update
 * @param old pointer
 * @param new pointer
 */
void mem_update_ptr(struct tasktree *, uint16_t, uint16_t);
/**
 * Move the contents of a tasktree except the ptr
 * while keeping the rptrs of the children in tact
 *
 * @param target
 * @param source
 */
void mem_node_move(struct tasktree *, struct tasktree *);

#if LOGLEVEL == 2
void mem_print_heap();
void mem_print_task_heap();
#else
#define mem_print_heap()
#define mem_print_task_heap()
#endif

//Loop over tasks
struct task *mem_task_head(void);
struct task *mem_task_next(struct task *);

//Convert mtask pointers to-and-fro actual pointers
void *mem_ptr(uint16_t);
uint16_t mem_rptr(void *);
#define mem_cast_ptr(t, type) ((type)mem_ptr(t))
#define mem_cast_task(t) mem_cast_ptr(t, struct task *)
#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 nextaligned(type, p) \
	((type *)(\
		(uintptr_t)(p)+\
		(sizeof(type)-(uintptr_t)(p) % sizeof(type))\
	))
#else
#define nextaligned(type, p) ((type *)(p))
#endif

#ifdef __cplusplus
}
#endif
#endif
