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

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
	ElementPolygonMeshProxy::MakeSingleSided(MakeSingleSidedProcess *process)
{
	Check_Object(this);
	Check_Object(process);

	//
	//--------------------------
	// Make sure we can continue
	//--------------------------
	//
	process->MirrorCallback(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 backface rejection is on, we are done
	//-----------------------------------------
	//
	MLR_I_PMesh *primitive = GetPrimitive(0);
	Check_Object(primitive);
	MLRState state = primitive->GetReferenceState();

	// If back face culling is off, no need to duplicate
	if (state.GetBackFaceMode() == MLRState::BackFaceOffMode) {
		state.SetMatTwoSidedOff();
		return true;
	}

	// If material is single sided, no need to duplicate either
	if (state.GetMatTwoSidedMode() == MLRState::MatTwoSidedOffMode &&
		state.GetProcessDeltaMask()&MLRState::MatTwoSidedMask != 0) {

		state.SetMatTwoSidedOff();
		return true;
	}

	state.SetMatTwoSidedOff();

	//----------------------------------------------------------------
	// Backface rejection is on, but material is two-sided, 
	// so we need to duplicate the polygons
	//----------------------------------------------------------------
	//
	state.SetBackFaceOn();
	primitive->SetReferenceState(state);

	//
	//-------------------------------------------
	// 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);

	//
	//---------------------------------------------------------------------
	// Get the polygons of the original mesh and copy them into the new one
	// after mirroring
	//---------------------------------------------------------------------
	//
	DynamicArrayOf<PolygonProxy*> poly_array;
	int polycount = UsePolygonArray(&poly_array);
	DynamicArrayOf<PolygonProxy*> newpoly_array(polycount);
	for (int i=0;i<polycount;i++)
	{
		PolygonProxy *polygon = poly_array[i];
		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();

		//
		//------------------
		// Copy the vertices
		//------------------
		//
		DynamicArrayOf<IndexProxy*> index_array;
		int indexcount = polygon->UseIndexArray(&index_array);
		DynamicArrayOf<IndexProxy*> newindex_array(indexcount);
		DynamicArrayOf<VertexProxy*> newvertex_array(indexcount);

		for (int k=0;k<indexcount;k++)
		{
			Proxies::VertexProxy *old_vertex = index_array[indexcount-1-k]->GetVertexProxy();
			Check_Object(old_vertex);
			Proxies::VertexProxy *new_vertex = Proxies::VertexProxy::MakeProxy();
			Check_Object(new_vertex);
			newvertex_array[k] = new_vertex;
			
			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->GetColor(&color))
				new_vertex->SetColor(color);

			if (old_vertex->GetNormal(&normal))
			{
				new_vertex->SetNormal(normal.Negate(normal));
			}

			if (old_vertex->GetUVs(&uvs))
				new_vertex->SetUVs(uvs);

			newindex_array[k] = Proxies::IndexProxy::MakeProxy(new_polygon,new_vertex);
		}

		new_polygon->SetPolygonIndices(newvertex_array);
		newpoly_array[i] = new_polygon;
		new_polygon->DetachArrayReferences(&newindex_array);
		polygon->DetachArrayReferences(&index_array);
	}

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