#pragma once

#include <fstream.hfa>
#include <stddef.h>
#include <stdlib.hfa>

//==============================================================================
// type safe malloc / free

forall(T)
T* new()
{
	T* p = malloc();
	p{};
	return p;
}

forall(T)
void delete(T* p)
{
	^p{};
	free(p);
}

//==============================================================================
// ref counter content

struct content_t
{
	int value;
	size_t count;
};

void ?{}(content_t* this)
{
	sout | "Constructing content";
	this->count = 0;
}

void ^?{}(content_t* this)
{
	sout | "Destroying content";
}

//==============================================================================
// ref counter wrapper

struct wrapper_t
{
	content_t* ptr;
};

void ?{}(wrapper_t* this)
{
	sout | "Constructing empty ref pointer" | nl;
	this->ptr = NULL;
}

void ?{}(wrapper_t* this, wrapper_t rhs)
{
	sout | "Constructing ref pointer from copy";
	this->ptr = rhs.ptr;
	this->ptr->count++;
	sout | "Reference is " | this->ptr->count | nl;
}

void ^?{}(wrapper_t* this)
{
	if(this->ptr)
	{
		sout | "Destroying ref pointer";
		this->ptr->count--;
		sout | "Reference is " | this->ptr->count | nl;
		if(!this->ptr->count) delete(this->ptr);
	}
	else
	{
		sout | "Destroying empty ref pointer" | nl;
	}
}

wrapper_t ?=?(wrapper_t* this, wrapper_t rhs)
{
	sout | "Setting ref pointer";
	if(this->ptr)
	{
		this->ptr->count--;
		sout | "Reference is " | this->ptr->count | nl;
		if(!this->ptr->count) delete(this->ptr);
	}
	this->ptr = rhs.ptr;
	this->ptr->count++;
	sout | "Reference is " | this->ptr->count | nl;
}

void set(wrapper_t* this, content_t* c)
{
	this->ptr = c;
	this->ptr->count++;
	sout | "Setting ref pointer";
	sout | "Reference is " | this->ptr->count | nl;
}

void clear(wrapper_t* this)
{
	sout | "Clearing ref pointer";
	this->ptr->count--;
	sout | "Reference is " | this->ptr->count | nl;
	if(!this->ptr->count) delete(this->ptr);
	this->ptr = NULL;
}


wrapper_t wrap(int val)
{
	wrapper_t w;
	content_t* c = malloc();
	c{};
	c->value = val;
	set(&w, c);
	return w;
}
