#include "gcpointers.h"

// #include "gc.h"
#include "internal/collector.h"
#include "internal/object_header.h"
#include "internal/state.h"

void register_ptr(gcpointer_t* this)
{
	if(gcpointer_null(this)) return;

	if(gc_is_managed(this))
	{
		gc_object_header* obj = gc_get_object_for_ref(gc_get_state(), (void*)this);
		check(obj);
		check(is_valid(obj));
		check(gc_is_managed(this) == gc_is_managed(obj->type_chain) || !obj->type_chain);
		this->next = obj->type_chain;
		obj->type_chain = this;
		check(is_valid(obj));
	}
	else
	{
		gc_object_header* obj = gc_get_object_ptr((void*)this->ptr);
		check(obj);
		check(is_valid(obj));
		check(!obj->root_chain || this->ptr == obj->root_chain->ptr);
		check(!obj->root_chain || gc_is_managed(this) == gc_is_managed(obj->root_chain));
		this->next = obj->root_chain;
		obj->root_chain = this;
		check(is_valid(obj));
	}
}

void unregister_ptr(gcpointer_t* this)
{
	if(gcpointer_null(this)) return;

	gcpointer_t** prev_next_ptr = gc_find_previous_ref(this);
	check((*prev_next_ptr) == this);

	(*prev_next_ptr) = this->next;
}

void ?{}(gcpointer_t* this)
{
	this->ptr = (intptr_t)NULL;
	this->next = NULL;
}

void ?{}(gcpointer_t* this, void* address)
{
	this->ptr = (intptr_t)address;
	this->next = NULL;

	register_ptr(this);
}

void ?{}(gcpointer_t* this, gcpointer_t other)
{
	this->ptr = other.ptr;
	this->next = NULL;

	register_ptr(this);
}

void ^?{}(gcpointer_t* this)
{
	unregister_ptr(this);
}

gcpointer_t ?=?(gcpointer_t* this, gcpointer_t rhs)
{
	unregister_ptr(this);
	this->ptr = rhs.ptr;
	register_ptr(this);

	return *this;
}

//Logical operators
bool gcpointer_equal(const gcpointer_t* this, const gcpointer_t* rhs)
{
	return this->ptr == rhs->ptr;
}

bool gcpointer_not_equal(const gcpointer_t* this, const gcpointer_t* rhs)
{
	return this->ptr != rhs->ptr;
}

bool gcpointer_null(const gcpointer_t* this)
{
	return this->ptr == (intptr_t)NULL;
}

#ifndef NDEBUG
	bool is_valid(const gcpointer_t* this) {
		if(gcpointer_null(this)) return true;

		gc_object_header* obj = gc_get_object_ptr((void*)this->ptr);
		check(obj);
		check(is_valid(obj));
		check(!obj->root_chain || this->ptr == obj->root_chain->ptr);

		if( !gc_is_managed(this))
		{
			check( !(this->next) || this->ptr == this->next->ptr );
		}

		return true;
	}
#endif

forall(otype T) void ?{}(gcpointer(T)* this) {
	(&this->internal) {};
}

forall(otype T) void ?{}(gcpointer(T)* this, void* address) {
	(&this->internal) { address };
}

forall(otype T) void ?{}(gcpointer(T)* this, gcpointer(T) other) {
	(&this->internal) { other.internal };
}

forall(otype T) void ^?{}(gcpointer(T)* this) {
	^?{}(&this->internal);
}

forall(otype T) gcpointer(T) ?=?(gcpointer(T)* this, gcpointer(T) rhs) {
	this->internal = rhs.internal;
	return *this;
}
//
// forall(otype T) T *?(gcpointer(T) this);

forall(otype T) T* get(gcpointer(T)* this) {
	return (T*)this->internal.ptr;
}
//
// //Logical operators
forall(otype T) int ?!=?(gcpointer(T) this, int zero) {
	return this.internal.ptr != 0;
}
// forall(otype T) int ?!=?(gcpointer(T) this, gcpointer(T) rhs);
// forall(otype T) int ?==?(gcpointer(T) this, gcpointer(T) rhs);
