//
// Tools_Texturing_Copy.cpp
//
// Created by Kevin Lea - 05/10/2001 16:51:18
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

#include "Rendering/Asura_Module.h"
#include "Mesh/Asura_Mesh_System.h"
#include "Mesh/Asura_Mesh.h"
#include "Debugging/Asura_Assert.h"
#include "Interface/Asura_Overlay_System.h"
#include "Interface/Asura_Overlay.h"
#include "Interface/Asura_Dialog.h"
#include "Interface/Asura_Key_Combination.h"

#include "Tools_Overlays.h"
#include "Texturing/Tools_Texturing_Copy.h"
#include "Misc/Tools_Polygon_Selection.h"
#include "Texturing/Tools_Mode_Texturing.h"
#include "Misc/Tools_Timed_Messages.h"

///////////////////////////////////////////////////////////////////////////////////////
/*----------------------**
** Tools_Texturing_Copy **
**----------------------*/

//static members
List<TextureCopyPolygon*> 		Tools_Texturing_Copy::CopyFromList;		
List<TextureCopyPolygon*> 		Tools_Texturing_Copy::CopyToList;
TextureCopyPolygon* 			Tools_Texturing_Copy::CopyFromRef1;
TextureCopyPolygon* 			Tools_Texturing_Copy::CopyFromRef2;
TextureCopyPolygon* 			Tools_Texturing_Copy::CopyToRef1;
TextureCopyPolygon* 			Tools_Texturing_Copy::CopyToRef2;


void Tools_Texturing_Copy::Initialise()
{
	CopyFromRef1 = NULL;
	CopyFromRef2 = NULL;
	CopyToRef1 = NULL;
	CopyToRef2 = NULL;
}

void Tools_Texturing_Copy::Shutdown()
{
	CopyFromRef1 = NULL;
	CopyFromRef2 = NULL;
	CopyToRef1 = NULL;
	CopyToRef2 = NULL;
	while(CopyFromList.size())
	{
		delete CopyFromList.first_entry();
		CopyFromList.delete_first_entry();
	}
	while(CopyToList.size())
	{
		delete CopyToList.first_entry();
		CopyToList.delete_first_entry();
	}
}

void Tools_Texturing_Copy::CopyFromMultiple_Start()
{
	//clear the reference polygons
	CopyFromRef1 = NULL;
	CopyFromRef2 = NULL;
	CopyToRef1 = NULL;
	CopyToRef2 = NULL;

	

	//note the currently selected polygons
	NoteSelectedPolygons(CopyFromList);
	if(CopyFromList.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"At least 2 polygons must be selected to copy from");
		return;
	}

	//prompt for selection of the reference polygons
	Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_MultipleCopy_From1);
}

void Tools_Texturing_Copy::CopyToMultiple_Start()
{
	//clear the 'to' reference polygons
	CopyToRef1 = NULL;
	CopyToRef2 = NULL;

	
	//note the currently selected polygons
	NoteSelectedPolygons(CopyToList);
	if(CopyToList.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"At least 2 polygons must be selected to copy ");
		return;
	}

	//prompt for selection of the reference polygons
	Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_MultipleCopy_To1);
}

void Tools_Texturing_Copy::PolygonSelected(PolygonID polygon)
{
	List<TextureCopyPolygon*>* ListToSearch = NULL;
		
	//determine whether we are selecting one of the from or to ref polygons
	switch(Tools_Mode_Texturing::PromptedSelectionMode)
	{
		case PSM_MultipleCopy_From1 :
		case PSM_MultipleCopy_From2 :
			ListToSearch = &CopyFromList;
			break;
		case PSM_MultipleCopy_To1 :
		case PSM_MultipleCopy_To2 :
			ListToSearch = &CopyToList;
			break;
		default :
			Asura_Assert(0,"Incorrect prompted selection mode for this function");
			return;
	}
		
	//search for the selected polygon in the list
	TextureCopyPolygon* poly = 0;
	for(LIF<TextureCopyPolygon*> plif(ListToSearch);!plif.done();plif.next())
	{
		if(plif()->ModuleIndex== polygon.ModuleIndex && plif()->PolyIndex == polygon.PolyIndex)
		{
			poly = plif();
			break;
		}
	}

	if(!poly)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"You must choose one of the selected polygons");
	 	return;//failed to find the polygon in the list
	}

	
	if(poly == ListToSearch->first_entry())
	{
		if(Tools_Mode_Texturing::PromptedSelectionMode == PSM_MultipleCopy_From2 || 
		   Tools_Mode_Texturing::PromptedSelectionMode == PSM_MultipleCopy_To2)
		{
			Tools_Timed_Message_System::AddMessage(0xffffff,"That polygon has already been chosen as a reference polygon");
			return;
		}
	}

	//transfer the polygon found to the start of the list
	ListToSearch->delete_entry(poly);
	ListToSearch->add_entry_start(poly);

	//note the reference polygon , and switch modes
	switch(Tools_Mode_Texturing::PromptedSelectionMode)
	{
		case PSM_MultipleCopy_From1 :
			CopyFromRef2 = poly;
			//ask for the second ref polygon
			Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_MultipleCopy_From2);
			break;
		case PSM_MultipleCopy_From2 :
			CopyFromRef1 = poly;
			Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_None);
			break;
		case PSM_MultipleCopy_To1 :
			CopyToRef2 = poly;
			//ask for the second ref polygon
			Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_MultipleCopy_To2);
			break;
		case PSM_MultipleCopy_To2 :
			CopyToRef1 = poly;
			Tools_Mode_Texturing::SetPromptedSelectionMode(PSM_None);

			//make sure we are okay to go ahead with the copy
			if(!ValidateBeforeCopy()) return;

			//ask what method to use
			CreateChoiceOverlay();
			break;
	}
	
}

void Tools_Texturing_Copy::NoteSelectedPolygons(List<TextureCopyPolygon*>& CopyList)
{
	//clear the old list
	while(CopyList.size())
	{
		delete CopyList.first_entry();
		CopyList.delete_first_entry();
	}
	
	//iterate through the selected polygons , and store them in a list

	PolygonID polygon;
	Tools_Polygon_Selection::StartIteration();
	while(Tools_Polygon_Selection::GetNextSelected(&polygon))
	{
		CopyList.add_entry(new TextureCopyPolygon(polygon));
	};
}

bool Tools_Texturing_Copy::ValidateBeforeCopy()
{
	//make sure everything is happy before going ahead with the copy
	if(CopyFromList.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"Need at least 2 polygons to copy from");
		return false;
	}
	if(CopyToList.size()<2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"Need at least 2 polygons to copy to");
		return false;
	}

	if(!CopyFromRef1 || !CopyFromRef2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"The 'from' reference polygons haven't been chosen");
		return false;
	}
	if(!CopyToRef1 || !CopyToRef2)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"The 'to' reference polygons haven't been chosen");
		return false;
	}

	//make sure the corresponding polygons have the same number of verts
	if(CopyFromRef1->NumVerts != CopyToRef1->NumVerts ||
	   CopyFromRef2->NumVerts != CopyToRef2->NumVerts)
	{
		Tools_Timed_Message_System::AddMessage(0xffffff,"The equivalent 'to' and 'from' reference polygons have differing numbers of vertices");
		return false;
	}

	//things are looking good to go ahead with the copy
	return true;
}

void Tools_Texturing_Copy::CreateChoiceOverlay()
{

	Asura_Overlay* newOverlay;
	newOverlay = new Asura_Overlay("Multiple Copy Method :",true);
	
	Asura_Dialog* newDialog;
	Asura_Vector_2 offset = Asura_Vector_2(1.0f,3.0f);
			
	newDialog = new Asura_FunctionWithKeybinding_Dialog("Position (O)",offset,Tools_Texturing_Copy::CopyAbsolute,'o');
	newOverlay->AddDialog(newDialog);
	offset.y+=Asura_Dialog::LargeSpacing();

	newDialog = new Asura_FunctionWithKeybinding_Dialog("Topology (T)",offset,Tools_Texturing_Copy::CopyTopologically,'t');
	newOverlay->AddDialog(newDialog);
	
	
	newOverlay->m_uID = TOOLS_OVERLAY_TEXTURING_MODAL;

	Asura_Overlay_System::SubmitNewOverlay(newOverlay,true,false);
	
	Tools_Mode_Texturing::DisableInputForModalOverlay();
						  
}

void Tools_Texturing_Copy::CopyTopologically()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_TEXTURING_MODAL);

	if(!ValidateBeforeCopy()) return;
	
	
	LIF<TextureCopyPolygon*> fromlif(&CopyFromList);
	LIF<TextureCopyPolygon*> tolif(&CopyToList);

	//get the positions of all the polygons being copied from
	for(fromlif.restart();!fromlif.done();fromlif.next())
	{
		fromlif()->CalculateVertices(false);
	}

	//work out the adjacencies for the from list
	CalculateAdjacencies(CopyFromList);

	//Need to determine which side of the reference polygons match , and whether the second 
	//is a reflection of the first
	//Try each combination to see which gives the best result

	int best_reflect;
	int best_offset;
	int best_matches = 0;

	for(int reflect=0;reflect<=1;reflect++)
	{
		//calculate the positions of the polygons being copied to (taking reflectoin into account)
		for(tolif.restart();!tolif.done();tolif.next())
		{
			tolif()->CalculateVertices(reflect ? true : false);
		}

		//get the adjacenecies for the polygons copied to
		CalculateAdjacencies(CopyToList);


		//try out different offset values for the first polygon.
		for(int offset=0;offset<CopyToRef1->NumVerts;offset++)
		{
			int num_matches = DoTopologicalPolygonMatch(offset);
			
			//is this better than our previous attempts?
		  	if(num_matches>best_matches)
		  	{
		  		best_reflect = reflect;
				best_offset = offset;
				best_matches = num_matches;
			}
		}
	}

	//did we get any valid attempts?
	if(!best_matches) return;

	//redo the matching , with our best reflect/offset values
	if(!best_reflect)
	{
		//calculate the positions of the polygons being copied to (taking reflectoin into account)
		for(tolif.restart();!tolif.done();tolif.next())
		{
			tolif()->CalculateVertices(false);
		}

		//get the adjacenecies for the polygons copied to
		CalculateAdjacencies(CopyToList);
	}
	DoTopologicalPolygonMatch(best_offset);
	//In cases where a polygon is adjacent to more than one polygon on a given side , the results may change as a result of the list order changing
	//Asura_Assert(num_matches == best_matches,"Should be getting the same result as the previous attempt to match the polygons");

	//go through the matched polygons , copying the texturing
	for(fromlif.restart(),tolif.restart();!fromlif.done() && fromlif()->Matched;fromlif.next(),tolif.next())
	{
		CopyTexturingBetweenPolygons(fromlif(),tolif(),best_reflect ? true : false);	
	}
	
}

int Tools_Texturing_Copy::DoTopologicalPolygonMatch(int offset)
{
	//flag all the polygons as not matched
	LIF<TextureCopyPolygon*> fromlif(&CopyFromList);
	for(;!fromlif.done();fromlif.next())
	{
		fromlif()->Matched = false;
	}
	LIF<TextureCopyPolygon*> tolif(&CopyToList);
	for(;!tolif.done();tolif.next())
	{
		tolif()->Matched = false;
	}

	int num_matches = 1;
	CopyToRef1->Offset = offset;
	
	fromlif.restart();
	tolif.restart();
	fromlif()->Matched = true;
	tolif()->Matched = true;

	Asura_Assert(fromlif() == CopyFromRef1,"");
	Asura_Assert(tolif() == CopyToRef1,"");

	TextureCopyPolygon* last_match_from = fromlif();
	TextureCopyPolygon* last_match_to = tolif();

	bool valid_match = false;

	for(int i=0;i<num_matches;i++,fromlif.next(),tolif.next())
	{
		//'from' and 'to' should be equivalent polygons
		TextureCopyPolygon* from = fromlif();;			
		TextureCopyPolygon* to = tolif();			
		
		for(int v=0;v<from->NumVerts;v++)
		{
			//look at each side of the two equivalent polygons.
			//see if there is an equivalent side (taking offset into account) that is
			//shared with polygons that haven't been matched yet
			TextureCopyPolygon* adj1 = from->Adjacent[v];
			if(!adj1 || adj1->Matched) continue;

			TextureCopyPolygon* adj2 = to->Adjacent[(v+to->Offset)%to->NumVerts];
			if(!adj2 || adj2->Matched) continue;

			//make sure they have the same number of vertices
			if(adj1->NumVerts != adj2->NumVerts) continue;

			//find the sides in each of the adjacent polygons that are shared with
			//the original polygons
			int j;
			for(j=0;j<adj1->NumVerts;j++)
			{
				if(adj1->Adjacent[j]==from) break;
			}
			if(j==adj1->NumVerts) continue;
			
			int k;
			for(k=0;k<adj2->NumVerts;k++)
			{
				if(adj2->Adjacent[k]==to) break;
			}
			if(k==adj2->NumVerts) continue;

			
			//we have a match
			//the difference in the side numbers will be the offset for the new polygons
			adj2->Offset = (adj2->NumVerts+k-j)%adj2->NumVerts;

			//mark the polygons as matched
			adj1->Matched = true;
			adj2->Matched = true;

			//transfer the polygons so that they are after the last matched polygons in the list
			CopyFromList.delete_entry(adj1);
			CopyFromList.add_entry_after(adj1,last_match_from);

			CopyToList.delete_entry(adj2);
			CopyToList.add_entry_after(adj2,last_match_to);

			//increment the number of matches
			num_matches++;

			//and note the last match
			last_match_from = adj1;
			last_match_to =adj2;

			//are we matching the second reference polygons?
			if(adj1==CopyFromRef2 && adj2 == CopyToRef2)
			{
				//matched the reference polygons , this set of matches is valid then
				valid_match = true;
			}

		}
	}
	//return the number of matched polygons
	return valid_match ? num_matches : 0;
}

void Tools_Texturing_Copy::CalculateAdjacencies(List<TextureCopyPolygon*>& CopyList)
{
	const float tolerance = 0.01f; //1 cm
	
	//initialise the adjacencies
	LIF<TextureCopyPolygon*>plif1(&CopyList);
	for(;!plif1.done();plif1.next())
	{
		plif1()->Adjacent[0] = NULL;
		plif1()->Adjacent[1] = NULL;
		plif1()->Adjacent[2] = NULL;
		plif1()->Adjacent[3] = NULL;
	}

	//compare each pair of polygons
	for(plif1.restart();!plif1.done();plif1.next())
	{
		LIF<TextureCopyPolygon*> plif2(plif1);
		plif2.next();

		for(;!plif2.done();plif2.next())
		{
			//compare the 2 polygons and see if they share a side
			for(int i=0;i<plif1()->NumVerts;i++)
			{
				if(plif1()->Adjacent[i]) continue;
				for(int j=0;j<plif2()->NumVerts;j++)
				{
					Asura_Vector_3 diff = plif1()->Vert[i]-plif2()->Vert[j];
					if(diff.x>-tolerance && diff.x<tolerance && diff.y>-tolerance && diff.y<tolerance && diff.z>-tolerance && diff.z<tolerance)
					{
						//if vertex a in poly1 is the same as vertex a' in poly2
						//then vertex a+1 in poly1 needs to be the same as vertex a'-1 in poly2
						int v1 = (i+1)%plif1()->NumVerts;
					   	int v2 = (j+plif2()->NumVerts-1)%plif2()->NumVerts;

						if(plif2()->Adjacent[v2]) continue;
						
						diff = plif1()->Vert[v1] - plif2()->Vert[v2];
						if(diff.x>-tolerance && diff.x<tolerance && diff.y>-tolerance && diff.y<tolerance && diff.z>-tolerance && diff.z<tolerance)
						{
							//note the adjacency for both polygons
							plif1()->Adjacent[i] = plif2();
							plif2()->Adjacent[v2] = plif1();
						}
					}
				}
			}

		}
	}
}


void Tools_Texturing_Copy::CopyTexturingBetweenPolygons(TextureCopyPolygon* from,TextureCopyPolygon* to,bool reflect)
{
	//log for undo
	Tools_Texturing_UndoData_TextureInfo::Create(PolygonID(to->ModuleIndex,to->PolyIndex));

	//get the mesh and texturing data for each polygon
	Asura_Mesh* mesh1 = Asura_Module_System::GetMeshForModule(from->ModuleIndex);
	Asura_Assert(mesh1->TextureDataPtr,"Texture data hasn't been allocated for the mesh");
	Asura_Mesh_TextureInfo* tinfo1 = &mesh1->TextureDataPtr[from->PolyIndex];

	Asura_Mesh* mesh2 = Asura_Module_System::GetMeshForModule(to->ModuleIndex);
	Asura_Assert(mesh2->TextureDataPtr,"Texture data hasn't been allocated for the mesh");
	Asura_Mesh_TextureInfo* tinfo2 = &mesh2->TextureDataPtr[to->PolyIndex];

	Asura_Assert(from->NumVerts == to->NumVerts,"Polygons with differing number of vertices shouldn't have been matched");

	for(int iChan=0;iChan<ASURA_MATERIAL_MAXTEXTURES;iChan++)
	{
		if(!Tools_Mode_Texturing::IsEditingChannel(iChan)) continue;

		for(int i=0;i<to->NumVerts;i++)
		{
			//detmine the matching index in the first polygon
			int index;
			if(reflect)
			{
				index = (from->NumVerts-1-i)-to->Offset;
			}
			else
			{
				index = i-to->Offset;
			}
			while(index<0)index+=from->NumVerts;

			//copy the uvs for the corresponding vertices
			tinfo2->U[iChan][i] = tinfo1->U[iChan][index];
			tinfo2->V[iChan][i] = tinfo1->V[iChan][index];
			if(iChan==1)
			{
				//copy alpha as well
				tinfo2->m_afAlpha[i] = tinfo1->m_afAlpha[index];
			}
		}
	}
	//copy the texture
	if(Tools_Mode_Texturing::IsEditingAllChannels())
	{
		tinfo2->m_iMaterialID = tinfo1->m_iMaterialID;
	}
	else if (Tools_Mode_Texturing::IsEditingChannel(0))
	{
		int iTextureID = Asura_Material_System::GetTextureID(tinfo1->m_iMaterialID,0);
		int iBumpTextureID = Asura_Material_System::GetNormalMapID(tinfo1->m_iMaterialID,0);
		int iSpecularTextureID = Asura_Material_System::GetSpecularMapID(tinfo1->m_iMaterialID,0);
		tinfo2->m_iMaterialID = Tools_Mode_Texturing::GetMaterialWithTexture(tinfo2->m_iMaterialID,iTextureID,iBumpTextureID,iSpecularTextureID,0);
	}
	else if (Tools_Mode_Texturing::IsEditingChannel(1))
	{
		int iTextureID = Asura_Material_System::GetTextureID(tinfo1->m_iMaterialID,1);
		int iBumpTextureID = Asura_Material_System::GetNormalMapID(tinfo1->m_iMaterialID,1);
		int iSpecularTextureID = Asura_Material_System::GetSpecularMapID(tinfo1->m_iMaterialID,1);
		tinfo2->m_iMaterialID = Tools_Mode_Texturing::GetMaterialWithTexture(tinfo2->m_iMaterialID,iTextureID,iBumpTextureID,iSpecularTextureID,1);
	}

	// Don't create new materials in the response system, just copy the material across
	for(int iMat=0; iMat<TOOLS_NUM_MATERIALRESPONSE_PER_POLY; iMat++)
	{
		tinfo2->m_auMaterialResponseHashID[iMat] = tinfo1->m_auMaterialResponseHashID[iMat];
	}

	//make sure the mesh gets updated in the hardware
	Tools_UpdateTexturingOnSurfaces(mesh2);
	

}


void Tools_Texturing_Copy::CopyAbsolute()
{
	Asura_Overlay_System::CloseOverlays(TOOLS_OVERLAY_TEXTURING_MODAL);

	if(!ValidateBeforeCopy()) return;

	Asura_Matrix_3x3 mat;
	Asura_Vector_3 centre_from,centre_to;
	float scale;

	int MatchesWithoutReflection=0;
	int MatchesWithReflection=0;

	LIF<TextureCopyPolygon*> fromlif(&CopyFromList);
	LIF<TextureCopyPolygon*> tolif(&CopyToList);
	
	
	for(tolif.restart();!tolif.done();tolif.next())
	{
		tolif()->CalculateVertices(false);
	}
	
	//see how many matches we can get with and without reflection
	int reflect;
	for(reflect = 0;reflect<2;reflect++)
	{
		int matches = 0;
		
		for(fromlif.restart();!fromlif.done();fromlif.next())
		{
			fromlif()->CalculateVertices(reflect ? true : false);
		}
		if(!GetTransformationBetweenCopyLists(mat,centre_from,centre_to,scale)) return;
	
		//transform the 'from' list so that it should match the 'to' list
		for(fromlif.restart();!fromlif.done();fromlif.next())
		{
			fromlif()->Transform(mat,centre_from,centre_to,scale);
		}

		//count the number of matches
		for(fromlif.restart();!fromlif.done();fromlif.next())
		{
			for(tolif.restart();!tolif.done();tolif.next())
			{
				if(fromlif()->PolygonMatches(tolif()))
				{
					matches++;
					break;
				}
			}
		}
		if(reflect)
		{
			MatchesWithReflection = matches;
		}
		else
		{
			MatchesWithoutReflection = matches;
		}
	}
	
	
	
	
	/*
	If we get the best result without  reflection , then we will need to recalculate the vertices for
	the from polygons again , without reflection
	*/
	if(MatchesWithoutReflection>=MatchesWithReflection)
	{
		for(fromlif.restart();!fromlif.done();fromlif.next())
		{
			fromlif()->CalculateVertices(false);
		}
		if(!GetTransformationBetweenCopyLists(mat,centre_from,centre_to,scale)) return;
	
		for(fromlif.restart();!fromlif.done();fromlif.next())
		{
			fromlif()->Transform(mat,centre_from,centre_to,scale);
		}
		reflect = 0;
	}
	
	//compare the polygons in the two lists
	for(fromlif.restart();!fromlif.done();fromlif.next())
	{
		for(tolif.restart();!tolif.done();tolif.next())
		{
			if(fromlif()->PolygonMatches(tolif()))
			{
				if(reflect)
				{
					tolif()->Offset = tolif()->NumVerts - tolif()->Offset;
				}
				CopyTexturingBetweenPolygons(fromlif(),tolif(),reflect ? true : false);	
				break;
			}
		}
	}
	
}

/*
Get a set of axes by considering the offset between two polygons , and their normals.
Use these to create a rotation matrix.
Also return the distance between the two polygons
*/
 void Tools_Texturing_Copy::GetMatrixForCopy(TextureCopyPolygon* p1,TextureCopyPolygon* p2,Asura_Matrix_3x3& output_mat,float& length)
{
	Asura_Vector_3 y,z;
	Asura_Vector_3 offset = p2->CalculateCentre() - p1->CalculateCentre();

	length = offset.Magnitude();
	offset.Normalise();

	//get the first polygon's normal (our x vector)
	Asura_Vector_3 x = p1->Normal;

	//cross the offset with the normal to get our y vector
	y = offset ^ x;
	y.Normalise();

	//cross the x and y vectors to get the z vector
	z = x ^ y;
	z.Normalise();

	//fill in the vectors in the matrix
	output_mat.e11=x.x;
	output_mat.e21=x.y;
	output_mat.e31=x.z;

	output_mat.e12=y.x;
	output_mat.e22=y.y;
	output_mat.e32=y.z;

	output_mat.e13=z.x;
	output_mat.e23=z.y;
	output_mat.e33=z.z;
}												  

bool Tools_Texturing_Copy::GetTransformationBetweenCopyLists(Asura_Matrix_3x3& mat,Asura_Vector_3& centre_from,Asura_Vector_3& centre_to,float& scale)
{

	Asura_Matrix_3x3 mat_from;
	Asura_Matrix_3x3 mat_to;
	float length1,length2;

	//get matrices for each pair of polygons
	GetMatrixForCopy(CopyFromRef1,CopyFromRef2,mat_from,length1);
	GetMatrixForCopy(CopyToRef1,CopyToRef2,mat_to,length2);
	
	//get the matrix for transforming bwteen the two matrices
	mat_from.Transpose();
	mat = mat_to * mat_from;

	//get the scale factor for the two lengths
	if(!length1 || !length2) return false;
	scale = length2/length1;

	//get the centres of the reference 1 polygons	
	centre_from = CopyFromRef1->CalculateCentre();
	centre_to = CopyToRef1->CalculateCentre();

	return true;
}

///////////////////////////////////////////////////////////////////////////////////////
/*--------------------**
** TextureCopyPolygon **
**--------------------*/



TextureCopyPolygon::TextureCopyPolygon(const PolygonID& polygon)
{
	ModuleIndex = polygon.ModuleIndex;
	PolyIndex = polygon.PolyIndex;

	//get the mesh and polygon data
	Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(ModuleIndex);
	Asura_Mesh_Polygon* poly = &mesh->PolygonDataPtr[PolyIndex];

	NumVerts = poly->NumberOfVertices();

}


void TextureCopyPolygon::CalculateVertices(bool reflect)
{
	//get the mesh and polygon data
	Asura_Mesh* mesh = Asura_Module_System::GetMeshForModule(ModuleIndex);
	Asura_Mesh_Polygon* poly = &mesh->PolygonDataPtr[PolyIndex];

	//get the module location
	Asura_Vector_3  module_loc;
	Asura_Module_System::GetPositionOfModule(ModuleIndex,module_loc);


	//get the world coordinates for each vertex
	for(int i=0;i<NumVerts;i++)
	{
		Asura_Vector_3 pos = module_loc + mesh->VertexDataPtr[poly->VertexID[i]].Position;;
		if(reflect)
		{
			//copy the vertices in reverse order
			Vert[NumVerts-(i+1)] = -pos;
		}
		else
		{
			Vert[i] = pos;
		}
	}

	//get the normal
	if(!mesh->AreNormalsValid()) mesh->CreateNormals();
	Normal = mesh->NormalsDataPtr[PolyIndex].FaceNormal;
	if(reflect) Normal = -Normal;
		
}


Asura_Vector_3 TextureCopyPolygon::CalculateCentre()
{
	Asura_Vector_3 centre(0,0,0);

	for(int i=0;i<NumVerts;i++)
	{
		centre += Vert[i];
	}
	centre /= (float)NumVerts;

	return centre;
}


/*
Transform the vertices of a polygon , using the given transformation
*/
void TextureCopyPolygon::Transform(const Asura_Matrix_3x3& mat,const Asura_Vector_3& centre_from,const Asura_Vector_3& centre_to,const float& scale)
{
	for(int i=0;i<NumVerts;i++)
	{
		//rotate around centre_from
		Vert[i] -= centre_from;
		Vert[i] = Vert[i] * mat;
		//multiple the rotated position by the scale
		Vert[i] *= scale;
		//translate the vertex's position
		Vert[i] += centre_to;
	}
}


//Compare the vertices of 2 polygons , to see if they are identical (within tolerance)
//If they are the same provide the offset (eg vertex 0 in poly1 corresponds to vertex 2 in poly2)

bool TextureCopyPolygon::PolygonMatches(TextureCopyPolygon* poly)
{
	const float  tolerance = 0.01f;
	//must have the same number of vertices
	if(NumVerts!=poly->NumVerts) return false;

	for(int i=0;i<NumVerts;i++)
	{
		//compare each vertex in this polygon , with the first vertex in the other poly
		Asura_Vector_3 diff = Vert[i] - poly->Vert[0];

		if(diff.x>-tolerance && diff.x<=tolerance && diff.y>-tolerance && diff.y<=tolerance && diff.z>-tolerance && diff.z<=tolerance)
		{
			//compare the remaining vertices in order
			int j;
			for(j=1;j<NumVerts;j++)
			{
				diff = Vert[(i+j)%NumVerts] - poly->Vert[j];
				if(!(diff.x>-tolerance && diff.x<=tolerance && diff.y>-tolerance && diff.y<=tolerance && diff.z>-tolerance && diff.z<=tolerance))
				{
					break;
				}
			}
			if(j==NumVerts)
			{
				//the polygons match
				//fill in the offset (the correspondence between vertices)
				poly->Offset = NumVerts-i;
				return true;
			}
		}
	}
	return false;
}

void Tools_Texturing_Copy::ClearLists()
{
	while(CopyFromList.size())
	{
		delete CopyFromList.first_entry();
		CopyFromList.delete_first_entry();
	}
	while(CopyToList.size())
	{
		delete CopyToList.first_entry();
		CopyToList.delete_first_entry();
	}

	CopyToRef1 = 0;
	CopyToRef2 = 0;
	CopyFromRef1 = 0;
	CopyFromRef2 = 0;
}