
#include "ModelStatic.h"
#include "MemoryManager/DebugNew.h"

using namespace RenderSystem;

ModelStatic::ModelStatic(void)
{
	this->nbAnimation = 0;	//0 animation car nous sommes dans le cas d'un model static.
	this->texCoords = NULL;
	this->triangles = NULL;
	this->theFrame.vertex = NULL;
	this->theFrame.trisNormal = NULL;
	this->theFrame.normal = NULL;
}

ModelStatic::~ModelStatic(void)
{
	delete [] this->name;
	delete [] this->texCoords;
	delete [] this->triangles;
	delete [] this->theFrame.vertex;
	delete [] this->theFrame.trisNormal;
	delete [] this->theFrame.normal;
}

bool ModelStatic::load(char *path, char* _name)
{
	//les variables temporaires
	char txt[64];
	float f1,f2,f3;
	int i1,i2,i3,i4,i5,i6,i7,i8,i9;
	int curSurfNum=0;
	nbDegeneratedTris = 0;

	float max[3],min[3];//les vertices max et min pour aabb
	for(int i=0;i<3;i++)
	{
		min[i]=999999.0f;
		max[i]=-999999.0f;
	}

	bool ret = false;
	FILE *f;

	//les vecteurs de chargement
	std::vector<float> v;
	v.reserve(1000);
	std::vector<float> vt;
	vt.reserve(1000);
	std::vector<float> vn;
	vn.reserve(1000);
	std::vector<int> v_index;
	v_index.reserve(1000);
	std::vector<int> t_index;
	t_index.reserve(1000);
	std::vector<int> n_index;
	n_index.reserve(1000);
	std::vector<int> s;//le numéro de surface associé à chaque vertex d'une face(pour interpolation des n,t etb)
	s.reserve(1000);

	f = fopen (path,"r");

	if(f!=NULL)
	{
		//on affecte le nom
		this->name = new char[strlen(_name)+1];
		strcpy(this->name,_name);
		this->name[strlen(_name)]='\0';

		//***** ON CHARGE LES DONNEES DU FICHIERS
		while(!feof(f))
		{
			//ouverture du fichier
			fscanf(f,"%s ",txt);

			//extraction des différentes données
			if(strcmp(txt,"s")==0)
			{
				//les différentes surfaces ne sont qu'un numéro
				fscanf(f," %d",&curSurfNum);
			}
			else if(strcmp(txt,"v")==0)
			{
				//les vertex sont toujour sur trois composantes
				fscanf(f," %f %f %f",&f1,&f2,&f3);
				v.push_back(f1);
				v.push_back(f2);
				v.push_back(f3);

				//calcul pour la AABB
				if(max[0]<f1) max[0]=f1;
				if(max[1]<f2) max[1]=f2;
				if(max[2]<f3) max[2]=f3;
				if(min[0]>f1) min[0]=f1;
				if(min[1]>f2) min[1]=f2;
				if(min[2]>f3) min[2]=f3;
			}
			else if(strcmp(txt,"vt")==0)
			{
				//les coordonnée de texture peuvent être sur 2 ou 3 composantes
				//	=> seul les 2 premières nous intéresse. S'il y en a une 3ème, elle sera filtré
				//automatiquement par le filtre de préfixe(v,vt,vn...).
				fscanf(f,"%f %f",&f1,&f2);
				vt.push_back(f1);
				vt.push_back(f2);
			}
			else if(strcmp(txt,"vn")==0)
			{
				//les normals sont toujour sur trois composantes
				fscanf(f," %f %f %f",&f1,&f2,&f3);
				vn.push_back(f1);
				vn.push_back(f2);
				vn.push_back(f3);
			}
			else if(strcmp(txt,"f")==0)
			{
				//les triangles ont toujour les info de vertex, texcoord et normal
				fscanf(f," %d/%d/%d %d/%d/%d %d/%d/%d",&i1,&i2,&i3,&i4,&i5,&i6,&i7,&i8,&i9);
				if(!isDegeneratedTriangle(i1-1,i4-1,i7-1,&v))
				{
					v_index.push_back(i1-1);
					t_index.push_back(i2-1);
					n_index.push_back(i3-1);
					v_index.push_back(i4-1);
					t_index.push_back(i5-1);
					n_index.push_back(i6-1);
					v_index.push_back(i7-1);
					t_index.push_back(i8-1);
					n_index.push_back(i9-1);
				
					s.push_back(curSurfNum);
					s.push_back(curSurfNum);
					s.push_back(curSurfNum);
				}
				else
				{
					//le triangle est dégénérer : on ne l'ajoute pas à la liste.
					nbDegeneratedTris++;
				}
			}

			txt[0] = ' ';	//pour éviter répetition à cause des ligne blanche de fin de fichier
		}

		//AABB
		this->theFrame.aabb.pos[0] = (max[0]+min[0])/2;
		this->theFrame.aabb.pos[1] = (max[1]+min[1])/2;
		this->theFrame.aabb.pos[2] = (max[2]+min[2])/2;
		this->theFrame.aabb.sizex  = (max[0]-min[0])/2;
		this->theFrame.aabb.sizey  = (max[1]-min[1])/2;
		this->theFrame.aabb.sizez  = (max[2]-min[2])/2;
		this->theFrame.aabb.computeLimit();

		std::vector<float> finalVertex;
		finalVertex.reserve(v_index.size()*3);
		std::vector<int> finalSurface;
		finalSurface.reserve(v_index.size()*3);
		std::vector<float> finalNormal;
		finalNormal.reserve(v_index.size()*3);
		std::vector<float> finalTexCoord;
		finalTexCoord.reserve(v_index.size()*3);
		std::vector<int> finalIndex;
		finalIndex.reserve(v_index.size()*3);
		//***** POUR CHAQUE FACE, NOUS GENERERONS LES VALEURS DANS LES VECTOR FINAUX QU'IL FAUT SANS DOUBLONS ET INDICE DE TABLEAU CORRESPONDANT
		nbTris = v_index.size()/3;	// TODO un bug lors de l'écriture ici sur les gros model!!!
		for(int i=0;i<nbTris;i++)//pour chaque faces...
		{
			int tmpind;
			//si le vertex 1 existe déjà sur la même surface : on prend cette indice
			tmpind = sameVertexSameSurfAlreadyExist(
			v[v_index[i*3]*3],v[v_index[i*3]*3+1],v[v_index[i*3]*3+2],
			vt[t_index[i*3]*2],vt[t_index[i*3]*2+1],
			vn[n_index[i*3]*3],vn[n_index[i*3]*3+1],vn[n_index[i*3]*3+2],
			s[i*3],&finalVertex,&finalTexCoord,&finalNormal,&finalSurface);
			if(tmpind!=-1)
			{
				//=> le vertex de la même surface est déjà présent : pas la peine de le recopier
				finalIndex.push_back(tmpind);
			}
			else
			{
				//=> le vertex est nouveau : on l'ajoute
				finalIndex.push_back(finalVertex.size()/3);//la taille est en fait l'indice du nouveau vertex
				finalVertex.push_back(v[v_index[i*3]*3]);
				finalVertex.push_back(v[v_index[i*3]*3+1]);
				finalVertex.push_back(v[v_index[i*3]*3+2]);
				finalNormal.push_back(vn[n_index[i*3]*3]);
				finalNormal.push_back(vn[n_index[i*3]*3+1]);
				finalNormal.push_back(vn[n_index[i*3]*3+2]);
				finalTexCoord.push_back(vt[t_index[i*3]*2]);
				finalTexCoord.push_back(vt[t_index[i*3]*2+1]);
				finalSurface.push_back(s[i*3]);//la surface de la face i (pas d'index)
			}
			//si le vertex 2 existe déjà sur la même surface : on prend cette indice
			tmpind = sameVertexSameSurfAlreadyExist(
			v[v_index[i*3+1]*3],v[v_index[i*3+1]*3+1],v[v_index[i*3+1]*3+2],
			vt[t_index[i*3+1]*2],vt[t_index[i*3+1]*2+1],
			vn[n_index[i*3+1]*3],vn[n_index[i*3+1]*3+1],vn[n_index[i*3+1]*3+2],
			s[i*3+1],&finalVertex,&finalTexCoord,&finalNormal,&finalSurface);
			if(tmpind!=-1)
			{
				//=> le vertex de la même surface est déjà présent : pas la peine de le recopier
				finalIndex.push_back(tmpind);
			}
			else
			{
				//=> le vertex est nouveau : on l'ajoute
				finalIndex.push_back(finalVertex.size()/3);//la taille est en fait l'indice du nouveau vertex
				finalVertex.push_back(v[v_index[i*3+1]*3]);
				finalVertex.push_back(v[v_index[i*3+1]*3+1]);
				finalVertex.push_back(v[v_index[i*3+1]*3+2]);
				finalNormal.push_back(vn[n_index[i*3+1]*3]);
				finalNormal.push_back(vn[n_index[i*3+1]*3+1]);
				finalNormal.push_back(vn[n_index[i*3+1]*3+2]);
				finalTexCoord.push_back(vt[t_index[i*3+1]*2]);
				finalTexCoord.push_back(vt[t_index[i*3+1]*2+1]);
				finalSurface.push_back(s[i*3+1]);//la surface de la face i (pas d'index)
			}
			//si le vertex 3 existe déjà sur la même surface : on prend cette indice
			tmpind = sameVertexSameSurfAlreadyExist(
			v[v_index[i*3+2]*3],v[v_index[i*3+2]*3+1],v[v_index[i*3+2]*3+2],
			vt[t_index[i*3+2]*2],vt[t_index[i*3+2]*2+1],
			vn[n_index[i*3+2]*3],vn[n_index[i*3+2]*3+1],vn[n_index[i*3+2]*3+2],
			s[i*3+2],&finalVertex,&finalTexCoord,&finalNormal,&finalSurface);
			if(tmpind!=-1)
			{
				//=> le vertex de la même surface est déjà présent : pas la peine de le recopier
				finalIndex.push_back(tmpind);
			}
			else
			{
				//=> le vertex est nouveau : on l'ajoute
				finalIndex.push_back(finalVertex.size()/3);//la taille est en fait l'indice du nouveau vertex
				finalVertex.push_back(v[v_index[i*3+2]*3]);
				finalVertex.push_back(v[v_index[i*3+2]*3+1]);
				finalVertex.push_back(v[v_index[i*3+2]*3+2]);
				finalNormal.push_back(vn[n_index[i*3+2]*3]);
				finalNormal.push_back(vn[n_index[i*3+2]*3+1]);
				finalNormal.push_back(vn[n_index[i*3+2]*3+2]);
				finalTexCoord.push_back(vt[t_index[i*3+2]*2]);
				finalTexCoord.push_back(vt[t_index[i*3+2]*2+1]);
				finalSurface.push_back(s[i*3+2]);//la surface de la face i (pas d'index)
			}
		}
		v.clear();
		vt.clear();
		vn.clear();
		v_index.clear();
		t_index.clear();
		n_index.clear();
		s.clear();

		//***** ON ALLOUE LA MEMOIRE POUR LES TABLEAUX FINAUX ET ON RENPLI A PARTIR DES VECTORS FINAUX
		this->triangles = new unsigned short[nbTris*3];
		this->texCoords = new float[finalTexCoord.size()];
		this->nbVertex = finalVertex.size()/3;
		this->theFrame.vertex = new float[nbVertex*3];//TACTIC : 3 composantes
		this->theFrame.normal = new float[finalNormal.size()];
		int *vertSurf = new int[nbVertex];
		for(int i=0;i<nbTris*3;i++)
		{//recopie des index
			this->triangles[i] = finalIndex[i];
		}
		for(int i=0;i<nbVertex;i++)
		{//recopie des vertex, normal et texCoords
			this->texCoords[i*2] = finalTexCoord[i*2];
			this->texCoords[i*2+1] = finalTexCoord[i*2+1];
			this->theFrame.vertex[i*3] = finalVertex[i*3];
			this->theFrame.vertex[i*3+1] = finalVertex[i*3+1];
			this->theFrame.vertex[i*3+2] = finalVertex[i*3+2];
			this->theFrame.normal[i*3] = finalNormal[i*3];
			this->theFrame.normal[i*3+1] = finalNormal[i*3+1];
			this->theFrame.normal[i*3+2] = finalNormal[i*3+2];
			vertSurf[i] = finalSurface[i];//la surface à laquel appartient le vertex
		}
		finalVertex.clear();
		finalSurface.clear();
		finalNormal.clear();
		finalTexCoord.clear();
		finalIndex.clear();

		//*****ON CALCUL LES NORMALES PAR TRIANGLE
		this->theFrame.trisNormal = new float[nbTris*3];
		for(int i=0;i<nbTris;i++)
		{
			float x1=this->theFrame.vertex[triangles[i*3]*3];
			float y1=this->theFrame.vertex[triangles[i*3]*3+1];
			float z1=this->theFrame.vertex[triangles[i*3]*3+2];
			float x2=this->theFrame.vertex[triangles[i*3+1]*3];
			float y2=this->theFrame.vertex[triangles[i*3+1]*3+1];
			float z2=this->theFrame.vertex[triangles[i*3+1]*3+2];
			float x3=this->theFrame.vertex[triangles[i*3+2]*3];
			float y3=this->theFrame.vertex[triangles[i*3+2]*3+1];
			float z3=this->theFrame.vertex[triangles[i*3+2]*3+2];

			float dx1=x2-x1;
			float dy1=y2-y1;
			float dz1=z2-z1;
			float dx2=x3-x1;
			float dy2=y3-y1;
			float dz2=z3-z1;

			float f1=dy1*dz2 - dz1*dy2;
			float f2=dz1*dx2 - dx1*dz2;
			float f3=dx1*dy2 - dy1*dx2;

			//on peut normalisé le vecteur : tout les triangle dégénerer on été supprimé
			float l = sqrt( f1*f1 + f2*f2 + f3*f3 );
			//si le produit vectoriel donne un vecteur nul on a un triangle dégénerer(on se fou du signe)
			theFrame.trisNormal[i*3] = f1/l;
			theFrame.trisNormal[i*3+1] = f2/l;
			theFrame.trisNormal[i*3+2] = f3/l;
		}

		delete [] vertSurf;
		ret = true;
	}

	return ret;
}

bool ModelStatic::isDegeneratedTriangle(int v1,int v2,int v3,std::vector <float> *v)
{
	bool degenerated = false;

	float x1=(*v)[v1*3];
	float y1=(*v)[v1*3+1];
	float z1=(*v)[v1*3+2];
	
	float x2=(*v)[v2*3];
	float y2=(*v)[v2*3+1];
	float z2=(*v)[v2*3+2];

	float x3=(*v)[v3*3];
	float y3=(*v)[v3*3+1];
	float z3=(*v)[v3*3+2];

	float dx1=x2-x1;
	float dy1=y2-y1;
	float dz1=z2-z1;
	float dx2=x3-x1;
	float dy2=y3-y1;
	float dz2=z3-z1;

	//si le produit vectoriel donne un vecteur nul on a un triangle dégénerer(on se fou du signe)
	float finalx = dy1*dz2 - dz1*dy2;
	float finaly = dz1*dx2 - dx1*dz2;
	float finalz = dx1*dy2 - dy1*dx2;
	if( (finalx==0) && (finaly==0) && (finalz==0))
	{
		degenerated=true;
	}

	return degenerated;
}


int ModelStatic::sameVertexSameSurfAlreadyExist(float x,float y,float z,float tx,float ty,float nx,float ny,float nz,int s,std::vector <float> *fvert, std::vector <float> *ftex, std::vector <float> *fnorm, std::vector <int> *surf)
{
	int fs=-1;

	for(int i=0;i<(int)surf->size();i++)
	{
		if(s==(*surf)[i])
		{
			//on est sur une surface suceptible de partager le vertex
			//=> on vérifie que la position du vertex est la même
			if( (x==(*fvert)[i*3]) && (y==(*fvert)[i*3+1]) && (z==(*fvert)[i*3+2]) &&
				 (tx==(*ftex)[i*2]) && (ty==(*ftex)[i*2+1]) && 
				 (nx==(*fnorm)[i*3]) && (ny==(*fnorm)[i*3+1]) && (nz==(*fnorm)[i*3+2])
				)
			{
				fs=i;	//l'indice sera celui du vertex partager
				break;
			}
		}
	}

	return fs;
}

void ModelStatic::initInstance(ModelInstance *mi)
{
	mi->vertex = this->theFrame.vertex;
	mi->normal = this->theFrame.normal;
	mi->trisNormal = this->theFrame.trisNormal;
	mi->texCoord = this->texCoords;
	mi->triangles = this->triangles;
	mi->model = this;
	mi->isGeometricalyStatic = true;
	mi->animState.curAnim = 0;
	mi->animState.curFrame = 0;
	mi->animState.curTime = 0;
	mi->animState.nextAnim = 0;
	mi->animState.nextFrame = 0;
	mi->animState.aabb = this->theFrame.aabb;
}

void ModelStatic::releaseInstance(ModelInstance *mi)
{
	mi->vertex = NULL;
	mi->normal = NULL;
	mi->trisNormal = NULL;
	mi->texCoord = NULL;
	mi->triangles = NULL;
	mi->model = NULL;
}

void ModelStatic::updateInstance_aabbAndAnimState(ModelInstance *mi, float time_ms)
{
	//rien a faire : le model est static géometriquement
}

void ModelStatic::updateInstance_geometry(ModelInstance *mi)
{
	//rien a faire : le model est static géometriquement
}

void ModelStatic::scale(float factor)
{
	//scale des vertices
	int i3;
	int nbV = this->nbVertex;
	for(int i=0;i<nbV;i++)
	{
		i3=i*3;
		theFrame.vertex[i3] *= factor;
		theFrame.vertex[i3+1] *= factor;
		theFrame.vertex[i3+2] *= factor;
	}
	//scale de la boudingBox
	this->theFrame.aabb.pos = this->theFrame.aabb.pos * factor;
	this->theFrame.aabb.sizex *= factor;
	this->theFrame.aabb.sizey *= factor;
	this->theFrame.aabb.sizez *= factor;
	this->theFrame.aabb.computeLimit();
}


