/*  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*/

/*-----------------------------------------------------------------------------------\
|                                         tnt.cp                                     |
|  ce fichier contient les structures, classes et fonctions nécessaires à la lecture |
| des fichiers tnt de total annihilation qui sont les fichiers contenant les cartes  |
| du jeu.                                                                            |
|                                                                                    |
\-----------------------------------------------------------------------------------*/

#include <allegro.h>
#include <alleggl.h>
#include <GL/glu.h>
#include "ta3dbase.h"
#include "3do.h"
#include "EngineClass.h"
#include "tdf.h"
#include "tnt.h"

MAP	*load_tnt_map(byte *data)		// Charge une map au format TA, extraite d'une archive HPI/UFO
{
	MAP	*map=new MAP;		// Crée une nouvelle carte
	map->init();
	map->tnt=true;
	TNTHEADER	header;		// Structure pour l'en-tête du fichier

	int i,x,y;

	header.IDversion=((int*)data)[0];
	header.Width=((int*)data)[1];
	header.Height=((int*)data)[2];
	header.PTRmapdata=((int*)data)[3];
	header.PTRmapattr=((int*)data)[4];
	header.PTRtilegfx=((int*)data)[5];
	header.tiles=((int*)data)[6];
	header.tileanims=((int*)data)[7];
	header.PTRtileanim=((int*)data)[8];
	header.sealevel=((int*)data)[9];
	header.PTRminimap=((int*)data)[10];
	header.unknown1=((int*)data)[11];
	header.pad1=((int*)data)[12];
	header.pad2=((int*)data)[13];
	header.pad3=((int*)data)[14];
	header.pad4=((int*)data)[15];

#ifdef TNT_DEBUG_MODE
	printf("IDversion=%d\n",header.IDversion);
	printf("Width=%d\n",header.Width);
	printf("Height=%d\n",header.Height);
	printf("tiles=%d\n",header.tiles);
	printf("tileanims=%d\n",header.tileanims);
	printf("sealevel=%d\n",header.sealevel);
#endif

	int TDF_index[header.tileanims];

	for(i=0;i<header.tileanims;i++) {			// Crée le tableau pour la correspondance des éléments
		TDF_index[i]=feature_manager.get_feature_index((char*)(data+header.PTRtileanim+4+(i*132)));
		if(TDF_index[i]==-1)
			printf("tdf non trouvé: %s\n",(char*)(data+header.PTRtileanim+4+(i*132)));
		}

	map->sealvl=header.sealevel*H_DIV;
	int f_pos;
		// Lit la minimap
	int w,h;
	f_pos=header.PTRminimap;
	w=*((int*)(data+f_pos));		f_pos+=4;
	h=*((int*)(data+f_pos));		f_pos+=4;
	map->mini_w=w;
	map->mini_h=h;
	map->mini=create_bitmap_ex(8,252,252);
	for(y=0;y<252;y++) {
		memcpy(map->mini->line[y],data+f_pos,252);
		f_pos+=252;
		}
	BITMAP *tmp=create_bitmap(map->mini->w,map->mini->h);
	blit(map->mini,tmp,0,0,0,0,tmp->w,tmp->h);
	destroy_bitmap(map->mini);
	map->mini=tmp;
	map->mini_w=0;
	map->mini_h=0;
	while(map->mini_w<252 && ((int*)(map->mini->line[0]))[map->mini_w]!=makecol(121,149,255)) map->mini_w++;
	while(map->mini_h<252 && ((int*)(map->mini->line[map->mini_h]))[0]!=makecol(121,149,255)) map->mini_h++;
	if(g_useTextureCompression)
		allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
	else
		allegro_gl_set_texture_format(GL_RGB8);
	map->glmini=allegro_gl_make_texture(map->mini);
	glBindTexture(GL_TEXTURE_2D,map->glmini);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

		// Lit les différents morceaux
	int n_bmp=(header.tiles+0x3F>>6);			// Nombre de textures 256x256 nécessaires pour mémoriser tout les morceaux
	BITMAP *bmp_tex[n_bmp];
	for(i=0;i<n_bmp;i++)
		bmp_tex[i]=create_bitmap_ex(8,256,256);

	f_pos=header.PTRtilegfx;
	for(i=0;i<header.tiles;i++) {		// Lit tout les morceaux
		int tex_num=i>>6;	// Numéro de la texture associée
		int tx=(i&0x7)<<5;			// Coordonnées sur la texture
		int ty=((i>>3)&0x7)<<5;
		for(y=0;y<32;y++) {			// Lit le morceau
			memcpy(bmp_tex[tex_num]->line[ty+y]+tx,data+f_pos,32);
			f_pos+=32;
			}
		}

	map->bloc_w=header.Width>>1;
	map->bloc_h=header.Height>>1;
	map->map_h=map->bloc_h<<4;
	map->map_w=map->bloc_w<<4;
	map->bmap=(unsigned short**) malloc(sizeof(unsigned short*)*map->bloc_h);
	map->bmap[0]=(unsigned short*) malloc(sizeof(unsigned short)*map->bloc_w*map->bloc_h);
	for(i=1;i<map->bloc_h;i++)
		map->bmap[i]=&(map->bmap[0][i*map->bloc_w]);
	map->view=(byte**) malloc(sizeof(byte*)*map->bloc_h);
	map->view[0]=(byte*) malloc(sizeof(byte)*map->bloc_w*map->bloc_h);
	map->path=(byte**) malloc(sizeof(byte*)*map->bloc_h<<1);
	map->path[0]=(byte*) malloc(sizeof(byte)*map->bloc_w*map->bloc_h<<2);
	map->map_data=(SECTOR**) malloc(sizeof(SECTOR*)*(map->bloc_h<<1));
	map->map_data[0]=(SECTOR*) malloc(sizeof(SECTOR)*(map->bloc_w*map->bloc_h<<2));
	map->h_map=(float**) malloc(sizeof(float*)*(map->bloc_h<<1));
	map->h_map[0]=(float*) malloc(sizeof(float)*(map->bloc_w*map->bloc_h<<2));
	map->ph_map=(float**) malloc(sizeof(float*)*(map->bloc_h<<1));
	map->ph_map[0]=(float*) malloc(sizeof(float)*(map->bloc_w*map->bloc_h<<2));
	for(i=1;i<(map->bloc_h<<1);i++) {
		map->h_map[i]=&(map->h_map[0][i*map->bloc_w<<1]);
		map->ph_map[i]=&(map->ph_map[0][i*map->bloc_w<<1]);
		map->map_data[i]=&(map->map_data[0][i*map->bloc_w<<1]);
		map->path[i]=&(map->path[0][i*map->bloc_w<<1]);
		if(i<map->bloc_h)
			map->view[i]=&(map->view[0][i*map->bloc_w]);
		}
	memset(map->view[0],0,map->bloc_w*map->bloc_h);
	map->nbbloc=header.tiles;		// Nombre de blocs nécessaires
	map->bloc=(BLOC*) malloc(sizeof(BLOC)*map->nbbloc);	// Alloue la mémoire pour les blocs
	map->ntex=n_bmp;
	map->tex=(GLuint*) malloc(sizeof(GLuint)*n_bmp);	// Tableau d'indices de texture OpenGl

	for(i=0;i<map->nbbloc;i++) {			// Crée les blocs
		map->bloc[i].init();
		int tex_num=i>>6;	// Numéro de la texture associée
		int tx=(i&0x7)<<5;			// Coordonnées sur la texture
		int ty=((i>>3)&0x7)<<5;
		int r=0,g=0,b=0;
		for(y=ty;y<ty+32;y++)
			for(x=tx;x<tx+32;x++) {
				int c=bmp_tex[tex_num]->line[y][x];
				r+=pal[c].r;
				g+=pal[c].g;
				b+=pal[c].b;
				}
		r>>=8;
		g>>=8;
		b>>=8;
		map->bloc[i].lava=(r>4 && g<(r>>2) && b<(r>>2));
		}
	
	if(g_useTextureCompression)
		allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
	else
		allegro_gl_set_texture_format(GL_RGB8);
	for(i=0;i<n_bmp;i++) {			// Finis de charger les textures et détruit les objets BITMAP
		tmp=create_bitmap(bmp_tex[i]->w,bmp_tex[i]->h);
		blit(bmp_tex[i],tmp,0,0,0,0,tmp->w,tmp->h);
		destroy_bitmap(bmp_tex[i]);
		bmp_tex[i]=tmp;
		map->tex[i]=allegro_gl_make_texture(bmp_tex[i]);
		glBindTexture(GL_TEXTURE_2D,map->tex[i]);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
		destroy_bitmap(bmp_tex[i]);
		}

	map->lvl=(POINTF**) malloc(sizeof(POINTF*)*map->bloc_w*map->bloc_h);
	for(i=0;i<map->bloc_w*map->bloc_h;i++)
		map->lvl[i]=NULL;

	for(i=0;i<map->nbbloc;i++) {		// Crée les blocs
		int t_n=i>>6;				// Numéro de texture
		int t_p=i&0x3F;
		float t_x=((float)(t_p&0x7))/8.0f;	// Position sur la texture
		float t_y=((float)((t_p>>3)&0x7))/8.0f;

		map->bloc[i].tex=map->tex[t_n];
		map->bloc[i].nbpoint=9;
		map->bloc[i].nbindex=12;
		if(i==0)
			map->bloc[i].index=new GLuint[map->bloc[i].nbindex];
		else
			map->bloc[i].index=map->bloc[0].index;
		map->bloc[i].texcoord=new float[map->bloc[i].nbpoint<<1];

		float c=1.0f/8.0f-1.0f/256.0f;
		t_x+=1.0f/512.0f;
		t_y+=1.0f/512.0f;
		map->bloc[i].texcoord[0]=t_x;				map->bloc[i].texcoord[1]=t_y;
		map->bloc[i].texcoord[2]=t_x+c*0.5f;		map->bloc[i].texcoord[3]=t_y;
		map->bloc[i].texcoord[4]=t_x+c;				map->bloc[i].texcoord[5]=t_y;
		map->bloc[i].texcoord[6]=t_x;				map->bloc[i].texcoord[7]=t_y+c*0.5f;
		map->bloc[i].texcoord[8]=t_x+c*0.5f;		map->bloc[i].texcoord[9]=t_y+c*0.5f;
		map->bloc[i].texcoord[10]=t_x+c;			map->bloc[i].texcoord[11]=t_y+c*0.5f;
		map->bloc[i].texcoord[12]=t_x;				map->bloc[i].texcoord[13]=t_y+c;
		map->bloc[i].texcoord[14]=t_x+c*0.5f;		map->bloc[i].texcoord[15]=t_y+c;
		map->bloc[i].texcoord[16]=t_x+c;			map->bloc[i].texcoord[17]=t_y+c;

		if(i==0) {
			map->bloc[i].index[0]=0;
			map->bloc[i].index[1]=3;
			map->bloc[i].index[2]=1;
			map->bloc[i].index[3]=4;
			map->bloc[i].index[4]=2;
			map->bloc[i].index[5]=5;
			map->bloc[i].index[6]=5;
			map->bloc[i].index[7]=8;
			map->bloc[i].index[8]=4;
			map->bloc[i].index[9]=7;
			map->bloc[i].index[10]=3;
			map->bloc[i].index[11]=6;
			}
		}

		// Charge les données sur la position des blocs
	f_pos=header.PTRmapdata;
	for(y=0;y<map->bloc_h;y++)
		for(x=0;x<map->bloc_w;x++) {
			map->bmap[y][x]=*((short*)(data+f_pos));
			f_pos+=2;
			}

		// Charge d'autres données sur les blocs
	map->water=false;
	f_pos=header.PTRmapattr;
	for(y=0;y<(map->bloc_h<<1);y++)
		for(x=0;x<(map->bloc_w<<1);x++) {
			int c=*((byte*)(data+f_pos));
			if(c<=header.sealevel) map->water=true;
			map->h_map[y][x]=map->ph_map[y][x]=c*H_DIV;
			f_pos+=4;
			}

	for(y=0;y<(map->bloc_h<<1);y++)				// Calcule les informations complémentaires sur la carte
		for(x=0;x<(map->bloc_w<<1);x++) {
			map->map_data[y][x].init();
			map->map_data[y][x].view=0;
			map->map_data[y][x].metal=0;
			map->map_data[y][x].underwater=(map->h_map[y][x]<map->sealvl);
			float dh=0.0f;
			if(y>0)
				dh=max(dh,fabs(map->h_map[y][x]-map->h_map[y-1][x]));
			if(x>0)
				dh=max(dh,fabs(map->h_map[y][x]-map->h_map[y][x-1]));
			if(y+1<(map->bloc_h<<1))
				dh=max(dh,fabs(map->h_map[y][x]-map->h_map[y+1][x]));
			if(x+1<(map->bloc_w<<1))
				dh=max(dh,fabs(map->h_map[y][x]-map->h_map[y][x+1]));
			map->map_data[y][x].dh=dh;
			}

	for(y=0;y<(map->bloc_h<<1);y++)
		for(x=0;x<(map->bloc_w<<1);x++) {			// Projete la carte du relief
			int rec_y;
			float h=map->ph_map[y][x];
			rec_y=(int)(0.5f+y-transform*h/16.0f);
			for(int cur_y=rec_y+1;cur_y<=y;cur_y++)
				if(cur_y>=0)
					map->ph_map[cur_y][x]=-1.0f;		// Valeur non spécifiée (un trou que l'on comblera plus tard)
			if(rec_y>=0)
				map->ph_map[rec_y][x]=h;
			}
	map->sea_dec=map->sealvl*transform*H_DIV;			// Calcule le décalage nécessaire pour restituer les océans
	for(y=0;y<(map->bloc_h<<1);y++)
		for(x=0;x<(map->bloc_w<<1);x++) {			// Lisse la carte du relief projeté
			if(map->ph_map[y][x]==-1.0f && y==0) {
				int cy=0;
				while(map->ph_map[cy][x]==-1.0f) cy++;
				float h=map->ph_map[cy][x];
				cy=0;
				while(map->ph_map[cy][x]==-1.0f) {
					map->ph_map[cy][x]=h;
					cy++;
					}
				}
			else if(map->ph_map[y][x]==-1.0f) {
				float h1=map->ph_map[y-1][x];
				int cy=y;
				while(cy<(map->bloc_h<<1) && map->ph_map[cy][x]==-1.0f) cy++;
				if(cy>=(map->bloc_h<<1)) cy=(map->bloc_h<<1)-1;
				float h2=map->ph_map[cy][x];
				if(h2==-1.0f) h2=h1;
				for(int ry=y;ry<cy;ry++)
					map->ph_map[ry][x]=h1+(h2-h1)*(ry-y+1)/(cy-y+1);
				}
			}

		// Ajoute divers éléments(végétation,...)
	f_pos=header.PTRmapattr+1;
	for(y=0;y<(map->bloc_h<<1);y++)
		for(x=0;x<(map->bloc_w<<1);x++) {
			unsigned short type=*((unsigned short*)(data+f_pos));
			map->map_data[y][x].stuff=-1;
			if(type<=header.tileanims) {
				VECTOR Pos;
				Pos.x=(x<<3)-0.5f*map->map_w+4.0f;
				Pos.z=(y<<3)-0.5f*map->map_h+4.0f;
				Pos.y=map->get_unit_h(Pos.x,Pos.z)+0.5f;
				map->map_data[y][x].stuff=features.add_feature(Pos,TDF_index[type]);
				}
			f_pos+=4;
			}
	return map;
}

GLuint load_tnt_minimap(byte *data,int *sw,int *sh)		// Charge une minimap d'une carte, extraite d'une archive HPI/UFO
{
	TNTHEADER	header;		// Structure pour l'en-tête du fichier

	int i,x,y;

	header.IDversion=((int*)data)[0];
	header.Width=((int*)data)[1];
	header.Height=((int*)data)[2];
	header.PTRmapdata=((int*)data)[3];
	header.PTRmapattr=((int*)data)[4];
	header.PTRtilegfx=((int*)data)[5];
	header.tiles=((int*)data)[6];
	header.tileanims=((int*)data)[7];
	header.PTRtileanim=((int*)data)[8];
	header.sealevel=((int*)data)[9];
	header.PTRminimap=((int*)data)[10];
	header.unknown1=((int*)data)[11];
	header.pad1=((int*)data)[12];
	header.pad2=((int*)data)[13];
	header.pad3=((int*)data)[14];
	header.pad4=((int*)data)[15];

	int f_pos;
		// Lit la minimap
	int w,h;
	f_pos=header.PTRminimap;
	w=*((int*)(data+f_pos));		f_pos+=4;
	h=*((int*)(data+f_pos));		f_pos+=4;
	BITMAP *mini=create_bitmap_ex(8,252,252);
	for(y=0;y<252;y++) {
		memcpy(mini->line[y],data+f_pos,252);
		f_pos+=252;
		}
	BITMAP *tmp=create_bitmap(mini->w,mini->h);
	blit(mini,tmp,0,0,0,0,tmp->w,tmp->h);
	destroy_bitmap(mini);
	mini=tmp;
	int mini_w=0;
	int mini_h=0;
	while(mini_w<252 && ((int*)(mini->line[0]))[mini_w]!=makecol(121,149,255)) mini_w++;
	while(mini_h<252 && ((int*)(mini->line[mini_h]))[0]!=makecol(121,149,255)) mini_h++;
	if(g_useTextureCompression)
		allegro_gl_set_texture_format(GL_COMPRESSED_RGB_ARB);
	else
		allegro_gl_set_texture_format(GL_RGB8);
	GLuint glmini=allegro_gl_make_texture(mini);
	glBindTexture(GL_TEXTURE_2D,glmini);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

	destroy_bitmap(mini);

	if(sw) *sw=mini_w;
	if(sh) *sh=mini_h;

	return glmini;
}
