// Copyright (C) 2004-2009 Christoph Kubisch & Eike Decker
// This file is part of the "Luxinia Engine".
// For conditions of distribution and use, see copyright notice in luxiniapublicsdk.txt

#include "exmodel_local.h"

static int EXMesh_localmirrors(EXMesh &imesh,uint channel, int asnormals)
{
	// go thru all meshes and find uvs outside 0,1
	// find "uv partner"
	int hadborders = FALSE;

	const float thresh = 1.0f/65536.0f;

	std::vector<EXVertexPTR> onplaneverts;
	onplaneverts.clear();

	std::vector<EXVertex> &vertices = imesh.instance ? imesh.instance->vertices : imesh.vertices;
	


	for (size_t ov = 0; ov < vertices.size(); ov++)
	{
		EXVertexPTR outer = &vertices[ov];

		float *uv = EXVertex_getUVW(outer,channel);
		float *pos = EXVertex_getPosition(outer);
		float uvfrac[2] = {fmodf(uv[0],1.0f),fmodf(uv[1],1.0f)};
		uvfrac[0] = uvfrac[0] < 0.0f ? 1.0f-uvfrac[0] : uvfrac[0];
		uvfrac[1] = uvfrac[1] < 0.0f ? 1.0f-uvfrac[1] : uvfrac[1];

		// outer 0,1 vertices look for their inner partner
		if (uv[0] > 1.0f || uv[0] < 0.0f ||
			uv[1] > 1.0f || uv[1] < 0.0f)
		{
			// find inner vertex to create normal
			for (size_t iv = ov+1; iv < vertices.size(); iv++)
			{
				EXVertexPTR inner = &vertices[iv];

				float *uvin = EXVertex_getUVW(inner,channel);
				if (uvin[0] <= 1.0f && uvin[0] >= 0.0f &&
					uvin[1] <= 1.0f && uvin[1] >= 0.0f)
				{
					float uvdiff[2] = {uvin[0]-uvfrac[0],uvin[1]-uvfrac[1]};
					if (uvdiff[0]*uvdiff[0]+uvdiff[1]*uvdiff[1] < thresh){
						float *posin = EXVertex_getPosition(inner);
						Vector3 sub;
						Vector3Sub(sub,posin,pos);
						// if pos equal (uv border matches), we need to search for primitives later
						if (Vector3Dot(sub,sub)<0.00001f)
						{
							if (asnormals)
								EXVertex_setNormal(outer,0.0f,0.0f,0.0f);
							else
								EXVertex_setTangent(outer,0.0f,0.0f,0.0f);

							onplaneverts.push_back(outer);
							hadborders = TRUE;
						}
						else{
							Vector3Normalized(sub);
							if (asnormals)
								EXVertex_setNormal(outer,sub[0],sub[1],sub[2]);
							else
								EXVertex_setTangent(outer,sub[0],sub[1],sub[2]);
						}
						// always move uvs back to 0-1 
						Vector2Copy(uv,uvfrac);
						break;
					}
				}
			}
		}
		else{
			// inside ones shall not be mirrored
			if (asnormals)
				EXVertex_setNormal(outer,0.0f,0.0f,0.0f);
			else
				EXVertex_setTangent(outer,0.0f,0.0f,0.0f);
		}
	}

	return FALSE;
}

booln EXModel_localmirrorsLUX(EXModelPTR imodel,uint channel, booln asnormals)
{
	booln haderrors = 0;

	for (size_t i = 0; i < imodel->meshes.size(); i++){
		EXMeshPTR imesh = imodel->meshes[i];
		EXMeshPTR vertmesh = imesh->instance ? imesh->instance : imesh;

		if ((EXMesh_getType(imesh)==EXMESH_MESH) &&
			EXMesh_getPrimitiveType(imesh)==EXPRIM_TRIANGLE_LIST &&
			EXMesh_getUsedUVWCount(vertmesh) > channel &&
			!EXMesh_getNoLocalMirrors(vertmesh))
		{
			haderrors |= EXMesh_localmirrors(*imesh,channel,asnormals);
		}

	}

	return !haderrors;
}
