#include "memory_pool.h"

extern "C" {
	#include <stdlib.h>
	#include <string.h>
}

#include "collector.h"
#include "object_header.h"

const size_t gc_pool_header_size = (size_t)(  &(((gc_memory_pool*)NULL)->start_p) );

void ?{}(gc_memory_pool* this, size_t size, gc_memory_pool* next, gc_memory_pool* mirror, uint8_t type)
{
	this->mirror = mirror;
	this->next = next;
	this->type_code = type;

	this->cards = ( (card_table_t*)malloc(sizeof(card_table_t)) ){};

	this->end_p = ((uint8_t*)this) + size;
	this->free_p = this->start_p;

	check( gc_pool_of( (void*)this ) == this);
	check(this->cards);
	gc_reset_pool(this);
}

void ^?{}(gc_memory_pool* this)
{
	^(&this->cards){};
	free(this->cards);
}

void gc_reset_pool(gc_memory_pool *const this)
{
	this->free_p = this->start_p;
	#ifndef NDEBUG
		memset(this->start_p, 0xCD, gc_pool_size_total(this));
	#endif

	check(this->cards);
	reset(this->cards);

	check(gc_pool_size_left(this) == gc_pool_size_total(this));
}

void* gc_pool_allocate(gc_memory_pool *const this, size_t size, bool zero)
{
	void* ret = this->free_p;

	this->free_p += size;

	if (zero) memset(ret, 0x00, size);

	check(this->cards);
	register_object(this->cards, ret);

	return ret;
}

void ?{}(	gc_pool_object_iterator* this,
		struct gc_object_header* start_object
		#ifndef NDEBUG
			, intptr_t pool_start
			, intptr_t pool_end
		#endif
	)
{
	this->object = start_object;
	#ifndef NDEBUG
		this->lower_limit = pool_start;
		this->upper_limit = pool_end;
	#endif

	check( ((intptr_t)start_object) >= this->lower_limit );
	check( ((intptr_t)start_object) <= this->upper_limit );
}

void ^?{}( gc_pool_object_iterator* this ) {}

gc_pool_object_iterator gc_pool_iterator_for(gc_memory_pool* const this, void* member)
{
	size_t card = card_of(member);
	intptr_t member_add = (intptr_t)member;
	intptr_t start_obj;

	do
	{
		check(card < CARDS_COUNT);
		start_obj = (intptr_t)object_at(this->cards, card);
		check(card != 0 || start_obj);
		card--;
	}
	while(start_obj > member_add || !(start_obj));

	check( start_obj );

	struct gc_object_header* start_obj_typed = (struct gc_object_header*)start_obj;

	return (gc_pool_object_iterator) {
		start_obj_typed
		#ifndef NDEBUG
			, (intptr_t)this->start_p
			, (intptr_t)this->free_p
		#endif
	};
}

bool ?!=?(const gc_pool_object_iterator lhs, const gc_pool_object_iterator rhs)
{
	return lhs.object != rhs.object;
}

gc_pool_object_iterator begin(gc_memory_pool* const this)
{
	struct gc_object_header* start_obj = (struct gc_object_header*)this->start_p;
	return (gc_pool_object_iterator) {
		start_obj
		#ifndef NDEBUG
			, (intptr_t)this->start_p
			, (intptr_t)this->free_p
		#endif
	};
}

gc_pool_object_iterator end(gc_memory_pool* const this)
{
	return (gc_pool_object_iterator) {
		(struct gc_object_header*)this->free_p
		#ifndef NDEBUG
			, (intptr_t)this->start_p
			, (intptr_t)this->free_p
		#endif
	};
}

gc_pool_object_iterator* ++?(gc_pool_object_iterator* it)
{
	struct gc_object_header* object = it->object;
	intptr_t next_ptr = ((intptr_t)object) + object->size;
	check(next_ptr > it->lower_limit);
	check(next_ptr <= it->upper_limit);

	struct gc_object_header* next_obj = ((struct gc_object_header*)next_ptr);
	check(next_ptr == it->upper_limit || is_valid(next_obj));

	it->object = next_obj;
	return it;
}

const struct gc_object_header* *?(const gc_pool_object_iterator it)
{
	return it.object;
}

struct gc_object_header* *?(gc_pool_object_iterator it)
{
	return it.object;
}
