#include "ElementProxyHeaders.hpp"
#include <Proxies\OptimizeFlatShading.hpp>

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementPolygonMeshProxy::OptimizeFlatShading(OptimizeFlatShadingProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//--------------------------
	// Make sure we can continue
	//--------------------------
	//
	process->OptimizeCallback(this);
	if (!process->continueProcess)
		return true;

	//
	//------------------------------------------------------
	// Make sure that we only have a single mesh to evaluate
	//------------------------------------------------------
	//
	unsigned unique_combinations = GetPrimitiveCount();
	if (!unique_combinations)
	{
		Destroy();
		return false;
	}
	Verify(unique_combinations == 1);

	//
	//--------------------------------------------
	// If we aren't in flat coloring mode, we done
	//--------------------------------------------
	//
	MLR_I_PMesh *primitive = GetPrimitive(0);
	Check_Object(primitive);
	MLRState state = primitive->GetReferenceState();
	if (state.GetFlatColoringMode() == MLRState::FlatColoringOffMode)
		return true;

	//
	//-------------------------------------------
	// Create a new polymesh and set its position
	//-------------------------------------------
	//
	GroupProxy *parent = GetParentGroupProxy();
	LinearMatrix4D m;
	GetLocalToParent(&m);
	PolygonMeshProxy *mesh;
	if (parent)
	{
		Check_Object(parent);
		mesh = parent->AppendNewPolygonMeshProxy();
	}
	else
		mesh = GetSceneProxy()->AppendNewPolygonMeshProxy();
	LinearMatrix4D matrix;
	if (mesh->GetLocalToParent(&matrix))
		SetLocalToParent(matrix);

	//
	//-----------------------------------------------------------------
	// Make an array of all the vertices in the mesh and figure out the
	// minimum set of base vertices
	//-----------------------------------------------------------------
	//
	DynamicArrayOf<VertexProxy*> original_vertices;
	UseVertexArray(&original_vertices);
	unsigned old_vertex_count = original_vertices.GetLength();
	DynamicArrayOf<VertexProxy*> new_vertices(old_vertex_count);
	DynamicArrayOf<bool> locked(old_vertex_count);
	unsigned new_vertex_count=0;
	int v,i;
	for (v=0; v<old_vertex_count; ++v)
	{
		VertexProxy *vertex = original_vertices[v];
		Check_Object(vertex);
		for (i=0; i<new_vertex_count; ++i)
		{
			if (vertex->IsAllButNormalEqual(new_vertices[i], 0.0f))
				break;
		}

		//
		//------------------------------------------------------
		// If the vertex couldn't be matched, add it to the list
		//------------------------------------------------------
		//
		if (i == new_vertex_count)
		{
			Proxies::VertexProxy *new_vertex = Proxies::VertexProxy::MakeProxy();
			Check_Object(new_vertex);

			Stuff::Point3D pos;
			Stuff::Normal3D normal;
			Stuff::RGBAColor color;
			Stuff::DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > uvs;
			vertex->GetPosition(&pos);
			new_vertex->SetPosition(pos);
			if (vertex->GetNormal(&normal))
				new_vertex->SetNormal(normal);
			if (vertex->GetColor(&color))
				new_vertex->SetColor(color);
			if (vertex->GetUVs(&uvs))
				new_vertex->SetUVs(uvs);

			locked[new_vertex_count] = false;
			new_vertices[new_vertex_count++] = new_vertex;
		}
	}

	//
	//-------------------------------------------------
	// Go through the poly array and start copying them
	//-------------------------------------------------
	//
	DynamicArrayOf<PolygonProxy*> poly_array;
	int polycount = UsePolygonArray(&poly_array);
	DynamicArrayOf<PolygonProxy*> newpoly_array(polycount);
	for (int p=0; p<polycount; ++p)
	{
		PolygonProxy *polygon = poly_array[p];
		Check_Object(polygon);

		//
		//---------------
		// Copy the state
		//---------------
		//
		MultiState multi_state;
		polygon->UseMultiState(&multi_state);
		PolygonProxy *new_polygon = Proxies::PolygonProxy::MakeProxy();
		Check_Object(new_polygon);
		new_polygon->SetStatesToMatch(multi_state);
		multi_state.DetachReferences();

		//
		//----------------------------------------------------------
		// Get the indicies, then look for the best first one to use
		//----------------------------------------------------------
		//
		DynamicArrayOf<IndexProxy*> index_array;
		int indexcount = polygon->UseIndexArray(&index_array);
		DynamicArrayOf<VertexProxy*> newvertex_array(indexcount);
		int roll=0;
Search_For_First:
		IndexProxy *index = index_array[roll];
		Check_Object(index);
		Proxies::VertexProxy *old_vertex = index->GetVertexProxy();
		Check_Object(old_vertex);

		//
		//--------------------------------------------------------------------
		// Search the current vertex list looking for a match that is unlocked
		//--------------------------------------------------------------------
		//
		for (v=0; v<new_vertex_count; ++v)
		{
			if (old_vertex->IsAllButNormalEqual(new_vertices[v], 0.0f) && !locked[v])
			{
				locked[v] = true;
				Stuff::Normal3D normal;
				if (old_vertex->GetNormal(&normal))
					new_vertices[v]->SetNormal(normal);
				break;
			}
			if (old_vertex->IsEqualTo(new_vertices[v], 0.0f))
				break;
		}

		//
		//----------------------------------------------------------------
		// If we didn't find one, we need to see if maybe one of our other
		// vertices should be first
		//----------------------------------------------------------------
		//
		if (v == new_vertex_count)
		{
#if 0
			if (++roll < indexcount)
				goto Search_For_First;
#endif

			//
			//-------------------------------------------------------------
			// We looked at all of them, so now we have to make a new entry
			//-------------------------------------------------------------
			//
			Proxies::VertexProxy *new_vertex = Proxies::VertexProxy::MakeProxy();
			Check_Object(new_vertex);
			roll = 0;

			Stuff::Point3D pos;
			Stuff::RGBAColor color;
			Stuff::Normal3D normal;
			Stuff::DynamicArrayOf<Stuff::Vector2DOf<Stuff::Scalar> > uvs;
			old_vertex->GetPosition(&pos);
			new_vertex->SetPosition(pos);
			if (old_vertex->GetNormal(&normal))
				new_vertex->SetNormal(normal);
			if (old_vertex->GetColor(&color))
				new_vertex->SetColor(color);
			if (old_vertex->GetUVs(&uvs))
				new_vertex->SetUVs(uvs);

			v = new_vertex_count;
			locked[new_vertex_count] = true;
			new_vertices[new_vertex_count++] = new_vertex;
		}

		//
		//----------------
		// Set the indices
		//----------------
		//
		newvertex_array[0] = new_vertices[v];
		for (int k=1; k<indexcount; k++)
		{
			int t = k+roll;
			if (t>=indexcount)
				t -= indexcount;
			old_vertex = index_array[t]->GetVertexProxy();
			Check_Object(old_vertex);
			for (v=0; v<new_vertex_count; ++v)
			{
				if (old_vertex->IsAllButNormalEqual(new_vertices[v], 0.0f))
					break;
			}
			Verify(v < new_vertex_count);
			newvertex_array[k] = new_vertices[v];
		}
		new_polygon->SetPolygonIndices(newvertex_array);
		newpoly_array[p] = new_polygon;
		polygon->DetachArrayReferences(&index_array);
	}

	Proxies::Process a_process;
	mesh->AddPolygons(&a_process,newpoly_array);
	mesh->DetachReference();
	DetachArrayReferences(&newpoly_array);
	DetachArrayReferences(&poly_array);
	DetachArrayReferences(&original_vertices);
	Destroy();
	return false;
}
