#include "ProxyHeaders.hpp"

//
//############################################################################
//###########################    VertexProxy    ############################
//############################################################################
//

VertexProxy::ClassData*
	VertexProxy::DefaultData = NULL;
MemoryBlock*
	VertexProxy::AllocatedMemory = NULL;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::InitializeClass()
{
	Verify(!AllocatedMemory);
	AllocatedMemory =
		new MemoryBlock(
			sizeof(VertexProxy),
			10,
			10,
			"VertexProxy"
		);
	Register_Object(AllocatedMemory);

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			VertexProxyClassID,
			"VertexProxy",
			GenericProxy::DefaultData
		);
	Register_Object(DefaultData);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::TerminateClass()
{
	Unregister_Object(DefaultData);
	delete DefaultData;
	DefaultData = NULL;

	Unregister_Object(AllocatedMemory);
	delete AllocatedMemory;
	AllocatedMemory = NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VertexProxy::VertexProxy(
	ClassData *class_data,
	PolygonMeshProxy *mesh
):
	GenericProxy(class_data),
	polygonMeshProxy(mesh),
	activeIndexProxies(NULL)
{
	Check_Pointer(this);

	//
	//---------------------------------
	// Connect to a mesh if we have one
	//---------------------------------
	//
	Check_Object(polygonMeshProxy);
	polygonMeshProxy->AttachVertexProxy(this);
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VertexProxy::VertexProxy():
	GenericProxy(DefaultData),
	polygonMeshProxy(NULL),
	activeIndexProxies(NULL)
{
	Check_Pointer(this);
	hasNormal = hasColor = hasUV = false;
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
VertexProxy::~VertexProxy()
{
	Check_Object(this);
	Verify(activeIndexProxies.IsEmpty());
	if (polygonMeshProxy)
	{
		Check_Object(polygonMeshProxy);
		polygonMeshProxy->DetachReference();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::Destroy()
{
	Check_Object(this);
	Verify(referenceCount == 1);
	Verify(activeIndexProxies.IsEmpty());
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::TestInstance() const
{
	Verify(IsDerivedFrom(DefaultData));
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	VertexProxy::IsEqualTo(
		VertexProxy *vertex,
		Scalar threshold
	)
{
	Check_Object(this);
	Check_Object(vertex);

	//
	//-------------------------------
	// We are always equal to ourself
	//-------------------------------
	//
	if (this == vertex)
		return true;

	//
	//---------------------
	// Compare our position
	//---------------------
	//
	Point3D our_position, their_position;
	GetPosition(&our_position);
	vertex->GetPosition(&their_position);
	if (!Close_Enough(our_position, their_position, threshold))
		return false;

	//
	//------------------
	// Compare the color
	//------------------
	//
	RGBAColor color, other_color;
	bool them = vertex->GetColor(&other_color);
	bool us = GetColor(&color);
	if (them != us)
		return false;
	if (them && us)
	{
		if (!Close_Enough(color, other_color))
			return false;
	}

	//
	//-------------------
	// Compare the normal
	//-------------------
	//
	Normal3D normal, other_normal;
	them = vertex->GetNormal(&other_normal);
	us = GetNormal(&normal);
	if (them != us)
		return false;
	if (them && us)
	{
		if (!Close_Enough(normal, other_normal))
			return false;
	}

	//
	//---------------
	// Compare the UV
	//---------------
	//
	DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > uv, other_uv;
	them = vertex->GetUVs(&other_uv);
	us = GetUVs(&uv);
	if (them != us)
		return false;
	if(uv.GetLength() != other_uv.GetLength())
		return false;

	if (them && us)
	{
		for(unsigned i=0;i<uv.GetLength();i++)
		{
			if (!Close_Enough(uv[i].x, other_uv[i].x) || !Close_Enough(uv[i].y, other_uv[i].y))
				return false;
		}
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	VertexProxy::IsAllButNormalEqual(
		VertexProxy *vertex,
		Scalar threshold
	)
{
	Check_Object(this);
	Check_Object(vertex);

	//
	//-------------------------------
	// We are always equal to ourself
	//-------------------------------
	//
	if (this == vertex)
		return true;

	//
	//---------------------
	// Compare our position
	//---------------------
	//
	Point3D our_position, their_position;
	GetPosition(&our_position);
	vertex->GetPosition(&their_position);
	if (!Close_Enough(our_position, their_position, threshold))
		return false;

	//
	//------------------
	// Compare the color
	//------------------
	//
	RGBAColor color, other_color;
	bool them = vertex->GetColor(&other_color);
	bool us = GetColor(&color);
	if (them != us)
		return false;
	if (them && us)
	{
		if (!Close_Enough(color, other_color))
			return false;
	}

	//
	//---------------
	// Compare the UV
	//---------------
	//
	DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > uv, other_uv;
	them = vertex->GetUVs(&other_uv);
	us = GetUVs(&uv);
	if (them != us)
		return false;
	if(uv.GetLength() != other_uv.GetLength())
		return false;

	if (them && us)
	{
		for(unsigned i=0;i<uv.GetLength();i++)
		{
			if (!Close_Enough(uv[i].x, other_uv[i].x) || !Close_Enough(uv[i].y, other_uv[i].y))
				return false;
		}
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	VertexProxy::AddUniqueVertex(
		Stuff::DynamicArrayOf<VertexProxy*> &vertices,
		unsigned *vertex_count,
		VertexProxy *new_vertex,
		Scalar threshold
	)
{
	Check_Object(&vertices);
	Check_Pointer(vertex_count);
	Verify(*vertex_count <= vertices.GetLength());
	Check_Object(new_vertex);

	unsigned vertex;
	for (vertex=0; vertex<*vertex_count; ++vertex)
	{
		Check_Object(vertices[vertex]);
		if (vertices[vertex]->IsEqualTo(new_vertex, threshold))
			break;
	}
	if (vertex == *vertex_count)
		vertices[(*vertex_count)++] = new_vertex;
	return vertex;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::DetachIndexProxy(IndexProxy* proxy)
{
	Check_Object(this);
	activeIndexProxies.RemovePlug(proxy);
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::GetPosition(Point3D *position)
{
	Check_Object(this);
	Check_Pointer(position);
	*position = vertexPosition;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::SetPosition(const Point3D &position)
{
	Check_Object(this);
	Check_Object(&position);
	vertexPosition = position;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	VertexProxy::GetColor(RGBAColor *color)
{
	Check_Object(this);
	Check_Pointer(color);

	if (!hasColor)
		return false;
	Check_Object(&vertexColor);
	*color = vertexColor;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::SetColor(const RGBAColor& color)
{
	Check_Object(this);
	Check_Object(&color);
	hasColor = true;
	vertexColor = color;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	VertexProxy::GetNormal(Normal3D *normal)
{
	Check_Object(this);
	Check_Pointer(normal);

	if (!hasNormal)
		return false;
	Check_Object(&vertexNormal);
	*normal = vertexNormal;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::SetNormal(const Normal3D &normal)
{
	Check_Object(this);
	Check_Object(&normal);
	hasNormal = true;
	vertexNormal = normal;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	VertexProxy::GetUVs(DynamicArrayOf<Vector2DOf<Stuff::Scalar> > *uv)
{
	Check_Object(this);
	Check_Pointer(uv);

	if (!hasUV)
		return false;

	Check_Object(&vertexUVs);
	*uv = vertexUVs;
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	VertexProxy::SetUVs(DynamicArrayOf<Vector2DOf<Scalar> > &uv)
{
	Check_Object(this);
	Check_Object(&uv);
	hasUV = true;
	vertexUVs = uv;
}

