#include "ProxyHeaders.hpp"

//
//############################################################################
//###########################    Multistate    #############################
//############################################################################
//

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	MultiState::IsEqualTo(const MultiState& multi_state)
{
	if(GetLength() != multi_state.GetLength())
	{
		return false;
	}

	for(unsigned i=0;i<GetLength();++i)
	{
		if(!((*this)[i]->IsEqualTo(multi_state[i])))
		{
			return false;
		}
	}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MultiState::MultiState(const MultiState& multi_state)
{
	Check_Object(this);

	unsigned state_count = multi_state.GetLength();
	
	SetLength(state_count);

	for (unsigned i=0; i<state_count; ++i)
	{
		(*this)[i] = multi_state[i];

		(*this)[i]->AttachReference();
	}

	isInverted = multi_state.isInverted;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MultiState&
	MultiState::operator=(const MultiState& multi_state)
{
	Check_Object(this);

	unsigned state_count = multi_state.GetLength();
	
	SetLength(state_count);

	for (unsigned i=0; i<state_count; ++i)
	{
		(*this)[i] = multi_state[i];

		(*this)[i]->AttachReference();
	}

	isInverted = multi_state.isInverted;

	return *this;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/*
unsigned
	MultiState::UseStateArray(MultiState *states)
{
	Check_Object(this);

	unsigned state_count = GetLength();
	states->SetLength(state_count);
	for (unsigned i=0; i<state_count; ++i)
	{
		(*states)[i] = (*this)[i];

		(*states)[i]->AttachReference();
	}
	return state_count;
}
*/

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	MultiState::DetachReferences()
{
	for(unsigned i=0;i<GetLength();++i)
	{
		(*this)[i]->DetachReference();
	}

	SetLength(0);
}


//
//############################################################################
//###########################    PolygonProxy    #############################
//############################################################################
//

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

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

	Verify(!DefaultData);
	DefaultData =
		new ClassData(
			PolygonProxyClassID,
			"PolygonProxy",
			GenericProxy::DefaultData
		);
	Register_Object(DefaultData);
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonProxy::PolygonProxy(
	ClassData *class_data,
	PolygonMeshProxy *mesh
):
	GenericProxy(class_data),
	meshProxy(mesh),
	activeIndexProxies(NULL)
{
	Check_Pointer(this);
	Check_Object(meshProxy);
	meshProxy->AttachPolygonProxy(this);
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonProxy::PolygonProxy():
	GenericProxy(DefaultData),
	meshProxy(NULL),
	activeIndexProxies(NULL)
{
	Check_Object(this);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
PolygonProxy::~PolygonProxy()
{
	Check_Object(this);

	//
	//---------------------------------------
	// Detach the state array if there is one
	//---------------------------------------
	//
	unsigned i;
	stateArray.DetachReferences();

	//
	//----------------------------------------
	// Detach the vertex array if there is one
	//----------------------------------------
	//
	unsigned vertex_count = vertexArray.GetLength();
	for (i=0; i<vertex_count; ++i)
	{
		Verify(GetClassID() == PolygonProxyClassID);
		Check_Object(vertexArray[i]);
		vertexArray[i]->DetachReference();
	}

	//
	//--------------------------------------
	// Detach the mesh proxy if there is one
	//--------------------------------------
	//
	if (meshProxy)
	{
		Check_Object(meshProxy);
		Verify(activeIndexProxies.IsEmpty());
		meshProxy->DetachPolygonProxy(this);
	}
}

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

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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	PolygonProxy::UseMultiState(MultiState *states)
{
	Check_Object(this);
	Verify(GetClassID() == PolygonProxyClassID);

	*states = stateArray;

	return stateArray.GetLength();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::SetStatesToMatch(const MultiState &states)
{
	Check_Object(this);
	Verify(GetClassID() == PolygonProxyClassID);

	Verify(states.GetLength() > 0);
	stateArray = states;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::AddToCollection(const char *group)
{
	Check_Object(this);
	Check_Pointer(group);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::AddToCollections(MStringChain &group_list)
{
	Check_Object(this);
	Check_Object(&group_list);
	MStringChainIterator groups(&group_list);
	PlugOf<MString> *group;
	while ((group = groups.ReadAndNext()) != NULL)
	{
		Check_Object(group);
		AddToCollection(group->GetItem());
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::RemoveFromCollection(const char *group)
{
	Check_Object(this);
	Check_Pointer(group);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::RemoveFromCollections(MStringChain &group_list)
{
	Check_Object(this);
	Check_Object(&group_list);
	MStringChainIterator groups(&group_list);
	PlugOf<MString> *group;
	while ((group = groups.ReadAndNext()) != NULL)
	{
		Check_Object(group);
		RemoveFromCollection(group->GetItem());
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::GetCollections(MStringChain *group_list)
{
	Check_Object(this);
	Check_Pointer(group_list);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	PolygonProxy::IsMemberOf(const char* group)
{
	Check_Object(this);
	Check_Pointer(group);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::DetachIndexProxy(IndexProxy* proxy)
{
	Check_Object(this);
	activeIndexProxies.RemovePlug(proxy);
	Verify(referenceCount > 1);
	DetachReference();
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::GetNormal(Normal3D *normal)
{
	Check_Object(this);
	Check_Pointer(normal);

	//
	//-----------------
	// Get the vertices
	//-----------------
	//
	DynamicArrayOf<IndexProxy*> indices;
	#if defined(_ARMOR)
		unsigned index_count = UseIndexArray(&indices);
		Verify(index_count == indices.GetLength());
		Verify(index_count >= 3);
	#endif
	IndexProxy *index_a = indices[0];
	Check_Object(index_a);
	IndexProxy *index_b = indices[1];
	Check_Object(index_b);
	IndexProxy *index_c = indices[2];
	Check_Object(index_c);

	//
	//------------------------------------------
	// Get the positions and release the proxies
	//------------------------------------------
	//
	VertexProxy *vertex = index_a->GetVertexProxy();
	Check_Object(vertex);
	Point3D position_a;
	vertex->GetPosition(&position_a);

	vertex = index_b->GetVertexProxy();
	Check_Object(vertex);
	Point3D position_b;
	vertex->GetPosition(&position_b);

	vertex = index_c->GetVertexProxy();
	Check_Object(vertex);
	Point3D position_c;
	vertex->GetPosition(&position_c);
	DetachArrayReferences(&indices);

	//
	//--------------------
	// Get the leg vectors
	//--------------------
	//
	Vector3D
		leg_1,
		leg_2;
	leg_1.Subtract(position_b, position_a);
	leg_2.Subtract(position_c, position_a);

	//
	//----------------------------------------------------------------------
	// Compute the cross-product of the two legs to get the direction of the
	// normal
	//----------------------------------------------------------------------
	//
	Vector3D v;
	v.Cross(leg_1, leg_2);
	*normal = v;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	PolygonProxy::GetArea()
{
	Check_Object(this);

	//
	//---------------------
	// Set up the variables
	//---------------------
	//
	Point3D
		position_a = Point3D::Identity,
		position_b,
		position_c = Point3D::Identity;
	VertexProxy
		*vertex_a = NULL,
		*vertex_b = NULL,
		*vertex_c = NULL;
	Vector3D
		leg_1,
		leg_2 = Vector3D::Identity;

	//
	//-----------------------------------
	// Spin through, testing the vertices
	//-----------------------------------
	//
	Scalar area = 0.0f;
	DynamicArrayOf<IndexProxy*> indices;
	unsigned index_count = UseIndexArray(&indices);
	Verify(index_count == indices.GetLength());
	Verify(index_count >= 3);
	for (unsigned i=0; i<index_count-2; ++i)
	{
		//
		//-----------------------------------------------
		// Generate all the information on the first pass
		//-----------------------------------------------
		//
		if (!i)
		{
			Check_Object(indices[0]);
			vertex_a = indices[0]->GetVertexProxy();
			Check_Object(vertex_a);

			Check_Object(indices[1]);
			vertex_b = indices[1]->GetVertexProxy();
			Check_Object(vertex_b);

			Check_Object(indices[2]);
			vertex_c = indices[2]->GetVertexProxy();
			Check_Object(vertex_c);

			vertex_a->GetPosition(&position_a);
			vertex_b->GetPosition(&position_b);
			vertex_c->GetPosition(&position_c);

			leg_1.Subtract(position_b, position_a);
			leg_2.Subtract(position_c, position_a);
		}

		//
		//--------------------------------------------------------------
		// Get the index info.  If this is not the first pass, copy the
		// information from last pass
		//--------------------------------------------------------------
		//
		else
		{
			Check_Object(vertex_c);
			vertex_b = vertex_c;
			Check_Object(indices[i+2]);
			vertex_c = indices[i+2]->GetVertexProxy();
			Check_Object(vertex_c);

			position_b = position_c;
			vertex_c->GetPosition(&position_c);
			leg_1 = leg_2;
			leg_2.Subtract(position_c, position_a);
		}

		//
		//-----------------------------------------------------------------
		// Compute the cross-product of the two legs to get the area of the
		// triangle
		//-----------------------------------------------------------------
		//
		Vector3D v;
		v.Cross(leg_1, leg_2);
		area += v.GetLength();
	}

	//
	//-----------------------------
	// Delete the remaining proxies
	//-----------------------------
	//
	DetachArrayReferences(&indices);
	return 0.5f * area;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::GetVertexCentroid(Point3D *center)
{
	Check_Object(this);
	Check_Pointer(center);

	//
	//------------------------------
	// Add all the vertices together
	//------------------------------
	//
	*center = Point3D::Identity;
	DynamicArrayOf<IndexProxy*> indices;
	unsigned index_count = UseIndexArray(&indices);
	Verify(index_count >= 3);
	Verify(index_count == indices.GetLength());
	for (unsigned i=0; i<index_count; ++i)
	{
		IndexProxy *index = indices[i];
		Check_Object(index);
		VertexProxy *vertex = index->GetVertexProxy();
		Check_Object(vertex);
		Point3D position;
		vertex->GetPosition(&position);
		*center += position;
	}
	DetachArrayReferences(&indices);

	//
	//----------------------
	// Now, average them all
	//----------------------
	//
	*center /= static_cast<Scalar>(index_count);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Scalar
	PolygonProxy::GetSurfaceAreaAndCentroid(Point3D *center)
{
	Check_Object(this);

	//
	//---------------------
	// Set up the variables
	//---------------------
	//
	Point3D
		position_a = Point3D::Identity,
		position_b,
		position_c = Point3D::Identity;
	VertexProxy
		*vertex_a = NULL,
		*vertex_b = NULL,
		*vertex_c = NULL;
	Vector3D
		leg_1,
		leg_2 = Vector3D::Identity;

	//
	//-----------------------------------
	// Spin through, testing the vertices
	//-----------------------------------
	//
	Scalar area = 0.0f;
	DynamicArrayOf<IndexProxy*> indices;
	unsigned index_count = UseIndexArray(&indices);
	Verify(index_count == indices.GetLength());
	Verify(index_count >= 3);
	*center = Point3D::Identity;
	for (unsigned i=0; i<index_count-2; ++i)
	{
		//
		//-----------------------------------------------
		// Generate all the information on the first pass
		//-----------------------------------------------
		//
		if (!i)
		{
			Check_Object(indices[0]);
			vertex_a = indices[0]->GetVertexProxy();
			Check_Object(vertex_a);

			Check_Object(indices[1]);
			vertex_b = indices[1]->GetVertexProxy();
			Check_Object(vertex_b);

			Check_Object(indices[2]);
			vertex_c = indices[2]->GetVertexProxy();
			Check_Object(vertex_c);

			vertex_a->GetPosition(&position_a);
			vertex_b->GetPosition(&position_b);
			vertex_c->GetPosition(&position_c);
			leg_1.Subtract(position_b, position_a);
			leg_2.Subtract(position_c, position_a);
		}

		//
		//--------------------------------------------------------------
		// Get the index info.  If this is not the first pass, copy the
		// information from last pass
		//--------------------------------------------------------------
		//
		else
		{
			Check_Object(vertex_c);
			vertex_b = vertex_c;
			Check_Object(indices[i+2]);
			vertex_c = indices[i+2]->GetVertexProxy();
			Check_Object(vertex_c);

			position_b = position_c;
			vertex_c->GetPosition(&position_c);
			leg_1 = leg_2;
			leg_2.Subtract(position_c, position_a);
		}

		//
		//-----------------------------------------------------------------
		// Compute the cross-product of the two legs to get the area of the
		// triangle
		//-----------------------------------------------------------------
		//
		Vector3D v;
		v.Cross(leg_1, leg_2);

		//
		//-------------------------------------------------------------------
		// Add the three triangle points together and multiply by the area of
		// the triangle to give a weighted sum for the polygon centroid
		//-------------------------------------------------------------------
		//
		Point3D centroid;
		centroid.Add(position_a, position_b);
		centroid += position_c;
		Scalar wedge_area = v.GetLength() * 0.5f;
		if (area <= SMALL)
		{
			if (wedge_area > SMALL)
			{
				area += wedge_area;
				centroid *= wedge_area;
			}
			*center = centroid;
		}
		else
		{
			if (wedge_area > SMALL)
			{
				area += wedge_area;
				centroid *= wedge_area;
				*center += centroid;
			}
		}
	}

	//
	//-----------------------------
	// Delete the remaining proxies
	//-----------------------------
	//
	DetachArrayReferences(&indices);
	*center *= 1.0f/3.0f;
	return area;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
unsigned
	PolygonProxy::UseIndexArray(Stuff::DynamicArrayOf<IndexProxy*> *indices)
{
	Verify(GetClassID() == PolygonProxyClassID);
	unsigned vertex_count = vertexArray.GetLength();
	indices->SetLength(vertex_count);
	for (unsigned i=0; i<vertex_count; ++i)
	{
		(*indices)[i] = IndexProxy::MakeProxy(this, vertexArray[i]);
		Register_Object((*indices)[i]);
	}
	return vertex_count;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::DetachArrayReferences(DynamicArrayOf<IndexProxy*> *indices)
{
	Check_Object(this);

	Check_Object(indices);
	unsigned index_count = indices->GetLength();
	for (unsigned i=0; i<index_count; ++i)
	{
		Check_Object((*indices)[i]);
		(*indices)[i]->DetachReference();
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
	PolygonProxy::SetPolygonIndices(
		const Stuff::DynamicArrayOf<VertexProxy*> &vertices
	)
{
	Check_Object(this);
	Check_Object(&vertices);
	Verify(GetClassID() == PolygonProxyClassID);

	//
	//---------------------------------------
	// Detach the index array if there is one
	//---------------------------------------
	//
	unsigned vertex_count = vertexArray.GetLength();
	unsigned i;
	for (i=0; i<vertex_count; ++i)
	{
		Check_Object(vertexArray[i]);
		vertexArray[i]->DetachReference();
	}

	//
	//-------------------------------------------------------------------
	// Set the length of the index array, then spin through and create an
	// index proxy for each vertex
	//-------------------------------------------------------------------
	//
	vertex_count = vertices.GetLength();
	vertexArray.SetLength(vertex_count);
	for (i=0; i<vertex_count; ++i)
	{
		Check_Object(vertices[i]);
		vertexArray[i] = vertices[i];
		vertexArray[i]->AttachReference();
	}
}
