/*  TA3D, a remake of Total Annihilation
    Copyright (C) 2005  Roland BROCHARD

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA*/

/*-----------------------------------------------------------------\
|                               tdf.cpp                            |
|   contient toutes les fonctions et classes permettant la gestion |
| des fichiers TDF du jeu Total Annihilation qui contienne divers  |
| éléments graphiques.                                             |
\-----------------------------------------------------------------*/

#include <allegro.h>
#include <alleggl.h>
#include <GL/glu.h>
#include "ta3dbase.h"
#include "tdf.h"

FEATURE_MANAGER		feature_manager;
FEATURES features;

	void FEATURE_MANAGER::load_tdf(char *data,int size)					// Charge un fichier tdf
	{
		char *pos=data;
		char *ligne=NULL;
		int nb=0;
		int index=0;
		int	first=nb_features;
		char *limit=data+size;
		do{
			do
			{
				nb++;
				if(ligne)
					delete ligne;
				ligne=get_line(pos);
				strlwr(ligne);
				while(pos[0]!=0 && pos[0]!=13 && pos[0]!=10)	pos++;
				while(pos[0]==13 || pos[0]==10)	pos++;

				if(ligne[0]=='[') {
					if(strstr(ligne,"]"))
						*(strstr(ligne,"]"))=0;
					index=add_feature(ligne+1);
					feature[index].m3d=false;
					}
				else if(strstr(ligne,"world=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].world=strdup(strstr(ligne,"world=")+6);
					}
				else if(strstr(ligne,"description=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].description=strdup(strstr(ligne,"description=")+12);
					}
				else if(strstr(ligne,"category=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].category=strdup(strstr(ligne,"category=")+9);
					}
				else if(strstr(ligne,"object=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].filename=strdup(strstr(ligne,"object=")+7);
					feature[index].m3d=true;
					}
				else if(strstr(ligne,"filename=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].filename=strdup(strstr(ligne,"filename=")+9);
					}
				else if(strstr(ligne,"seqname=")) {
					if(strstr(ligne,";"))	*(strstr(ligne,";"))=0;
					feature[index].seqname=strdup(strstr(ligne,"seqname=")+8);
					}
				else if(strstr(ligne,"animating="))
					feature[index].animating=(*(strstr(ligne,"animating=")+10)=='1');
				else if(strstr(ligne,"animtrans="))
					feature[index].animtrans=feature[index].animating=(*(strstr(ligne,"animtrans=")+10)=='1');
				else if(strstr(ligne,"shadtrans="))
					feature[index].shadtrans=(*(strstr(ligne,"shadtrans=")+10)=='1');
				else if(strstr(ligne,"indestructible="))
					feature[index].indestructible=(*(strstr(ligne,"indestructible=")+15)=='1');
				else if(strstr(ligne,"height="))
					feature[index].height=atoi(strstr(ligne,"height=")+7);
				else if(strstr(ligne,"hitdensity="))
					feature[index].hitdensity=atoi(strstr(ligne,"hitdensity=")+11);
				else if(strstr(ligne,"metal="))
					feature[index].metal=atoi(strstr(ligne,"metal=")+6);
				else if(strstr(ligne,"damage="))
					feature[index].damage=atoi(strstr(ligne,"damage=")+7);
				else if(strstr(ligne,"footprintx="))
					feature[index].footprintx=atoi(strstr(ligne,"footprintx=")+11);
				else if(strstr(ligne,"footprintz="))
					feature[index].footprintz=atoi(strstr(ligne,"footprintz=")+11);

			}while(strstr(ligne,"}")==NULL && nb<1000 && pos<limit);
			delete ligne;
			ligne=NULL;
			}while(pos[0]=='[' && nb<1000 && pos<limit);
		for(int i=first;i<nb_features;i++) {				// Charge les fichiers d'animation
			if(feature[i].category)
				feature[i].vent=(strstr(feature[i].category,"vents")!=NULL);
			if(feature[i].filename && feature[i].seqname && !feature[i].m3d) {
				char tmp[200];
				tmp[0]=0;
				strcat(tmp,"/anims/");
				strcat(tmp,feature[i].filename);
				strcat(tmp,".gaf");
				byte *gaf=load_file(tmp);
				if(gaf) {
					int index=get_gaf_entry_index(gaf,feature[i].seqname);
					if(index>=0) {
						feature[i].anim.load_gaf(gaf,get_gaf_entry_index(gaf,feature[i].seqname));
						feature[i].anim.convert(false,true);
						}
					free(gaf);
					if(index>=0 && feature[i].height<=10.0f && feature[i].height>1.0f && feature[i].anim.nb_bmp>0 && feature[i].anim.bmp[0]->w>=16 && feature[i].anim.bmp[0]->h>=16) {				// Tente une conversion en 3d
						char tmp[200];
						tmp[0]=0;
						strcat(tmp,feature[i].filename);
						strcat(tmp,"/");
						strcat(tmp,feature[i].seqname);
						model_manager.create_from_2d(feature[i].anim.bmp[0],feature[i].footprintx*8,feature[i].footprintz*8,feature[i].height,tmp);
						feature[i].model=NULL;
						feature[i].m3d=true;
						feature[i].converted=true;
						feature[i].anim.destroy();
						index=-1;
						}
					if(index>=0)
						feature[i].anim.clean();
					}
				}
			else if(feature[i].filename && feature[i].m3d)
				feature[i].model=NULL;
			}
	}

void load_all_features_in_hpi(char *filename)
{
	HPI_FILE hpi;
	hpi.load(filename);
	char *file;
	int n[10];
	int d_index=0;
	char *dir[10];
	int size;
	dir[0]=strdup("/features");
	n[0]=0;
	while(d_index>-1) {
		while(file=hpi.find(dir[d_index],n[d_index]++)) {
			if(hpi.is_dir(file)) {
				dir[++d_index]=strdup(file);
				n[d_index]=0;
				}
			if(strstr(strlwr(file),".tdf")!=NULL) {			// Si le fichier est au bon format
				char *data=(char*)hpi.extract_memory_file(file,&size);
				if(data) {
					feature_manager.load_tdf(data,size);
					free(data);
					}
				}
			delete file;
			}
		free(dir[d_index--]);
		}

	hpi.destroy();
}

void load_features()				// Charge tout les éléments
{
	al_ffblk search;

	if(al_findfirst("*.hpi",&search,FA_RDONLY | FA_ARCH)==0)
	do
	{
		load_all_features_in_hpi(search.name);
	}while(al_findnext(&search)==0);
	al_findclose(&search);
	if(al_findfirst("*.ufo",&search,FA_RDONLY | FA_ARCH)==0)
	do
	{
		load_all_features_in_hpi(search.name);
	}while(al_findnext(&search)==0);
	al_findclose(&search);
	if(al_findfirst("*.ccx",&search,FA_RDONLY | FA_ARCH)==0)
	do
	{
		load_all_features_in_hpi(search.name);
	}while(al_findnext(&search)==0);
	al_findclose(&search);

	load_all_features_in_hpi("rev31.gp3");

	for(int i=0;i<feature_manager.nb_features;i++)
		if(feature_manager.feature[i].m3d && feature_manager.feature[i].model==NULL && feature_manager.feature[i].filename!=NULL && feature_manager.feature[i].seqname!=NULL) {
			char tmp[200];
			tmp[0]=0;
			strcat(tmp,feature_manager.feature[i].filename);
			strcat(tmp,"/");
			strcat(tmp,feature_manager.feature[i].seqname);
			feature_manager.feature[i].model=model_manager.get_model(tmp);
			}
		else if(feature_manager.feature[i].m3d && feature_manager.feature[i].model==NULL && feature_manager.feature[i].filename!=NULL)
			feature_manager.feature[i].model=model_manager.get_model(feature_manager.feature[i].filename);
}

	void FEATURES::draw(CAMERA *cam)
	{
		if(nb_features<=0) return;
		cam->SetView();			// Positionne la caméra
		glAlphaFunc(GL_GREATER,0.1);
		glEnable(GL_ALPHA_TEST);

		glDisable(GL_CULL_FACE);
		glDisable(GL_LIGHTING);
		glColor4f(1.0f,1.0f,1.0f,1.0f);
		glEnable(GL_TEXTURE_2D);
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
		glEnable(GL_BLEND);
		float sq2=1.0f/sqrt(2.0f);
		GLuint old;
		bool texture_loaded=false;
		GLuint index[]={	0, 1, 2, 3,
							4, 1, 2, 5,
							6, 1, 2, 7,
							8, 9,10,11,
							1,12,13, 2,
							1,14,15, 2,
							1,16,17, 2};

		float texcoord[]={	0.0f,	0.0f,
							0.5f,	0.0f,
							0.5f,	1.0f,
							0.0f,	1.0f,
							0.0f,	0.0f,
							0.0f,	1.0f,
							0.0f,	0.0f,
							0.0f,	1.0f,
							0.0f,	0.0f,
							1.0f,	0.0f,
							1.0f,	1.0f,
							0.0f,	1.0f,
							1.0f,	0.0f,
							1.0f,	1.0f,
							1.0f,	0.0f,
							1.0f,	1.0f,
							1.0f,	0.0f,
							1.0f,	1.0f};
		bool set=true;
		glDisableClientState(GL_NORMAL_ARRAY);
		glDisableClientState(GL_COLOR_ARRAY);
		glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
		glEnableClientState(GL_TEXTURE_COORD_ARRAY);
		glTexCoordPointer(2, GL_FLOAT, 0, texcoord);
		for(int i=min_idx;i<=max_idx;i++)
			if(!feature_manager.feature[feature[i].type].m3d && feature_manager.feature[feature[i].type].anim.nb_bmp>0) {
				if(feature_manager.feature[feature[i].type].animating)
					feature[i].frame=(feature[i].frame+1)%feature_manager.feature[feature[i].type].anim.nb_bmp;

				if(!feature[i].draw)	continue;

				if(!texture_loaded || old!=feature_manager.feature[feature[i].type].anim.glbmp[feature[i].frame]) {
					old=feature_manager.feature[feature[i].type].anim.glbmp[feature[i].frame];
					texture_loaded=true;
					glBindTexture(GL_TEXTURE_2D,feature_manager.feature[feature[i].type].anim.glbmp[feature[i].frame]);
					}
				VECTOR Pos=feature[i].Pos;
				float	h=feature_manager.feature[feature[i].type].height*0.5f;
				float	dw=0.5f*feature_manager.feature[feature[i].type].anim.w[feature[i].frame];
				if(feature_manager.feature[feature[i].type].height>5.0f) {
					dw*=h/feature_manager.feature[feature[i].type].anim.h[feature[i].frame];
					float bdw=dw*sq2;

					if(!set) {
						set=true;
						glDisableClientState(GL_NORMAL_ARRAY);
						glDisableClientState(GL_COLOR_ARRAY);
						glEnableClientState(GL_VERTEX_ARRAY);		// Les sommets
						glEnableClientState(GL_TEXTURE_COORD_ARRAY);
						glTexCoordPointer(2, GL_FLOAT, 0, texcoord);
						}

					float points[]={	Pos.x,		Pos.y+h,	Pos.z-dw,
										Pos.x,		Pos.y+h,	Pos.z,
										Pos.x,		Pos.y,		Pos.z,
										Pos.x,		Pos.y,		Pos.z-dw,
										Pos.x-bdw,	Pos.y+h,	Pos.z-bdw,
										Pos.x-bdw,	Pos.y,		Pos.z-bdw,
										Pos.x+bdw,	Pos.y+h,	Pos.z-bdw,
										Pos.x+bdw,	Pos.y,		Pos.z-bdw,
										Pos.x-dw,	Pos.y+h,	Pos.z,
										Pos.x+dw,	Pos.y+h,	Pos.z,
										Pos.x+dw,	Pos.y,		Pos.z,
										Pos.x-dw,	Pos.y,		Pos.z,
										Pos.x-bdw,	Pos.y+h,	Pos.z+bdw,
										Pos.x-bdw,	Pos.y,		Pos.z+bdw,
										Pos.x+bdw,	Pos.y+h,	Pos.z+bdw,
										Pos.x+bdw,	Pos.y,		Pos.z+bdw,
										Pos.x,		Pos.y+h,	Pos.z+dw,
										Pos.x,		Pos.y,		Pos.z+dw};

					glVertexPointer( 3, GL_FLOAT, 0, points);
					glDrawElements(GL_QUADS, 28,GL_UNSIGNED_INT,index);		// dessine le tout
					}
				else {
					dw=feature_manager.feature[feature[i].type].footprintx*4.0f;
					h=feature_manager.feature[feature[i].type].footprintz*4.0f;

					glBegin(GL_QUADS);
						glTexCoord2f(0.0f,0.0f);		glVertex3f(Pos.x-dw,Pos.y,Pos.z-h);
						glTexCoord2f(1.0f,0.0f);		glVertex3f(Pos.x+dw,Pos.y,Pos.z-h);
						glTexCoord2f(1.0f,1.0f);		glVertex3f(Pos.x+dw,Pos.y,Pos.z+h);
						glTexCoord2f(0.0f,1.0f);		glVertex3f(Pos.x-dw,Pos.y,Pos.z+h);
					glEnd();
					}
				}
			else if(feature_manager.feature[feature[i].type].m3d && feature_manager.feature[feature[i].type].model!=NULL) {
				if(!feature[i].draw)	continue;

				glColor4f(1.0f,1.0f,1.0f,1.0f);
				glEnable(GL_LIGHTING);
				glDisable(GL_BLEND);
				glPushMatrix();
				glTranslatef(feature[i].Pos.x,feature[i].Pos.y,feature[i].Pos.z);
				feature_manager.feature[feature[i].type].model->draw(NULL,false);
				glPopMatrix();
				glEnable(GL_BLEND);
				glDisable(GL_LIGHTING);
				glDisable(GL_CULL_FACE);
				texture_loaded=false;
				set=false;
				}
		glDisable(GL_BLEND);
		glDisable(GL_ALPHA_TEST);
		glEnable(GL_CULL_FACE);
	}

	void FEATURES::draw_shadow(CAMERA *cam,VECTOR Dir)
	{
		if(nb_features<=0) return;
		cam->SetView();			// Positionne la caméra
		for(int i=min_idx;i<=max_idx;i++)
			if(!feature_manager.feature[feature[i].type].m3d && feature_manager.feature[feature[i].type].anim.nb_bmp>0) {
				}
			else if(feature_manager.feature[feature[i].type].m3d && feature_manager.feature[feature[i].type].model!=NULL) {
				if(!feature[i].draw)	continue;
				if(feature_manager.feature[feature[i].type].converted)	continue;		// Quelques problèmes (graphiques et plantages) avec les modèles convertis

				glPushMatrix();
				glTranslatef(feature[i].Pos.x,feature[i].Pos.y,feature[i].Pos.z);
				if(g_useStencilTwoSide)													// Si l'extension GL_EXT_stencil_two_side est disponible
					feature_manager.feature[feature[i].type].model->draw_shadow((sqrt(feature_manager.feature[feature[i].type].model->size)*2.0f)*Dir,NULL);
				else {																	// Sinon il faut faire le sale boulo
					GLuint list;
					glFrontFace(GL_CW);
					glStencilOp(GL_KEEP,GL_KEEP,GL_INCR);
					list=glGenLists(1);
					glNewList(list,GL_COMPILE_AND_EXECUTE);
					feature_manager.feature[feature[i].type].model->draw_shadow((sqrt(feature_manager.feature[feature[i].type].model->size)*2.0f)*Dir,NULL);
					glEndList();

					glFrontFace(GL_CCW);  
					glStencilOp(GL_KEEP,GL_KEEP,GL_DECR);
					glCallList(list);
					glDeleteLists(list,1);
					}
				glPopMatrix();
				}
	}
