#include "Types.h"

#include <iostream>
#include <Windows.h>
#include <algorithm>

namespace Brainstem
{
	IObject* IObject::LiveObjects = 0;
	IObject* IObject::DeadObjects = 0;
	std::list<IObject*> IObject::HeapObjects;
	unsigned long IObject::HeapUsage = 0;

	IObject::IObject()
	{
		Next = Prev = 0;
		References = 0;

		std::list<IObject*>::iterator it = std::find(HeapObjects.begin(), HeapObjects.end(), this);
		if(it == HeapObjects.end())
		{
			StackAllocated = true;
		}
		else
		{
			StackAllocated = false;
			HeapObjects.erase(it);
		}

		if(!StackAllocated)
		{
			Next = DeadObjects;

			if(DeadObjects)
				DeadObjects->Prev = this;

			DeadObjects = this;
		}
	}

	IObject::~IObject() { }

	void IObject::CollectGarbage()
	{
		while(DeadObjects)
		{
			IObject *nObj = DeadObjects->Next;
			WRITE_MM(DeadObjects, "<GARBAGE>");
			delete DeadObjects;

			DeadObjects = nObj;
		}
	}

	void IObject::AddRef()
	{
		References++;

		if(!StackAllocated && (References == 1))
		{
			WRITE_MM(this, "<REF++> " << this->GetName());

			if(Prev)
				Prev->Next = Next;

			if(Next)
				Next->Prev = Prev;

			if(DeadObjects == this)
				DeadObjects = Next;

			Prev = 0;
			Next = LiveObjects; 
			
			if(LiveObjects)
				LiveObjects->Prev = this;

			LiveObjects = this;
		}
	}

	void IObject::RemoveRef()
	{
		References--; 

		if(!StackAllocated && (References <= 0))
		{
			WRITE_MM(this, "<REF--> " << this->GetName());

			if(Prev)
				Prev->Next = Next;

			if(Next)
				Next->Prev = Prev;

			if(LiveObjects == this)
				LiveObjects = Next;

			Prev = 0;
			Next = DeadObjects;

			if(DeadObjects)
				DeadObjects->Prev = this;

			DeadObjects = this;
		}
	}

	void IObject::CollectRemaining()
	{
		CollectGarbage();

		while(LiveObjects)
		{
			IObject *o = LiveObjects->Next;
			delete LiveObjects;
			LiveObjects = o;
		}
	}

	void* IObject::operator new(size_t objsize)
	{
		void *newObj = malloc(objsize);
		HeapUsage += objsize;

		HeapObjects.push_back((IObject*)newObj);
		((IObject*)newObj)->HeapAllocSize=objsize;
		return newObj;
	}

	void IObject::operator delete(void* obj)
	{
		if(!((IObject*)obj)->StackAllocated)
		{
			HeapUsage -= ((IObject*)obj)->HeapAllocSize;
			free(obj);
		}
	}
}