/*
	This file is part of Warzone 2100.
	Copyright (C) 1999-2004  Eidos Interactive
	Copyright (C) 2005-2007  Warzone Resurrection Project

	Warzone 2100 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.

	Warzone 2100 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 Warzone 2100; if not, write to the Free Software
	Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <math.h>

// we need BSPIMD defined if we want to read & use the BSP imd files

// Define this if we are compile ivis for the BSP generating tool, also for PIEBIN tool
//#define PIETOOL

#ifdef PIETOOL
#define BSPIMD
#define SAVEIMD
#endif

#include "ivisdef.h"
#include "imd.h"
#include "bug.h"
#include "tex.h"
#include "ivispatch.h"

#ifdef PIETOOL
#include "bspimd.h"
#endif

//*************************************************************************

//*************************************************************************

#define MAX_SHAPE_RADIUS	160

//*************************************************************************


//*************************************************************************

// Output BSP Tree to a file


// This stuff saves out the BSP tree as generated by the BSP tool
//
// Because of the complexities of the BSP generation tool. The IMD is stored in a different
// format when it is generated compared to when it is loaded and used in the game.
// This means that when saving out a BSP tree from the tool other functions need to be included
// most of them are in ptrlist.c which is no longer included in ivis. See the source for BSPIMD
// for this file. Becareful not to confuse this with the same named file in deliverance/src which
// might or might not be the same.
//
#ifdef PIETOOL

// Prototypes for the linked list handling
void *list_GetFirst(PSBSPPTRLIST pList);
void *list_GetNext(PSBSPPTRLIST pList, void *pData);


int BSPPolys, BSPNodes;
PSBSPTREENODE BSPNodeTable[iV_IMD_MAX_POLYS];
iIMDPoly *BSPPolyTable[iV_IMD_MAX_POLYS];


void OutputTriangleList(FILE *fp, PSBSPPTRLIST TriList)
{
	iIMDPoly *Triangle;
	int TriNum=0;
	int d;

	if (TriList == NULL) return;

	Triangle = list_GetFirst(TriList);

	while (Triangle != NULL) {
		fprintf(fp,"\t%8x %d",Triangle->flags,Triangle->npnts);
		for (d=0; d<Triangle->npnts; d++)
			fprintf(fp," %d",Triangle->pindex[d]);

		if (Triangle->flags & iV_IMD_TEXANIM) {

			if (Triangle->pTexAnim == NULL) {
				debug( LOG_NEVER, "No TexAnim pointer!\n" );
			} else {
				fprintf(fp," %d %d %d %d",
					Triangle->pTexAnim->nFrames,
					Triangle->pTexAnim->playbackRate,
					Triangle->pTexAnim->textureWidth,
					Triangle->pTexAnim->textureHeight);
			}
		}

		// if textured write texture uv's
		if (Triangle->flags & (iV_IMD_TEX | iV_IMD_PSXTEX)) {
			for (d = 0; d < Triangle->npnts; d++) {
				fprintf(fp," %d %d",Triangle->vrt[d].u,Triangle->vrt[d].v);
			}
		}
		fprintf(fp, "\n");
		Triangle = list_GetNext(TriList, Triangle);
	}
}


void CountTriangleList(PSBSPPTRLIST TriList)
{
	iIMDPoly *Triangle;

	assert(BSPPolys<iV_IMD_MAX_POLYS);

	if (TriList == NULL) return;
	if (TriList->iNumNodes == 0) return;

	Triangle=list_GetFirst(TriList);

	while (Triangle != NULL) {
		BSPPolyTable[BSPPolys] = Triangle;
		BSPPolys++;

		Triangle = list_GetNext(TriList, Triangle);
	}
}


void CountBSPPolys(PSBSPTREENODE psNode)
{
	if (psNode == NULL) {
		return;
	}

	BSPNodeTable[BSPNodes] = psNode;
	BSPNodes++;

	CountBSPPolys(psNode->link[LEFT]);
	CountTriangleList(psNode->psTriSameDir);
	CountTriangleList(psNode->psTriOppoDir);
	CountBSPPolys(psNode->link[RIGHT]);
}


void OutputBSPPolys(FILE *fp, PSBSPTREENODE psNode)
{
	if (psNode == NULL) {
		return;
	}
	OutputBSPPolys(fp, psNode->link[LEFT]);
	OutputTriangleList(fp, psNode->psTriSameDir);
	OutputTriangleList(fp, psNode->psTriOppoDir);
	OutputBSPPolys(fp, psNode->link[RIGHT]);
}


int HuntNodeList(PSBSPTREENODE psNode)
{
	int i;

	for (i = 0; i < BSPNodes; i++) {
		if (BSPNodeTable[i] == psNode) {
			return i;
		}
	}
	return -1;
}


int HuntPolyList(iIMDPoly *Poly)
{
	int i;

	for (i = 0; i < BSPPolys; i++) {
		if (BSPPolyTable[i] == Poly) {
			return i;
		}
	}
	return 999;
}


void DumpTriangleList(FILE *fp,PSBSPPTRLIST TriList)
{

	iIMDPoly *Triangle;

	assert(BSPPolys<iV_IMD_MAX_POLYS);

//	DBPRINTF(("Dumping TriList %p\n",TriList));

	if (TriList==NULL) return;

	Triangle = list_GetFirst(TriList);

	while (Triangle != NULL) {
		fprintf(fp,"%d ",HuntPolyList(Triangle));
		Triangle=list_GetNext(TriList,Triangle);
	}
	fprintf(fp,"-1 ");
}


void OutputBSPNodes(FILE *fp, PSBSPTREENODE psNode)
{
	if (psNode == NULL) {
		return;
	}
	fprintf(fp,"\t%d ",HuntNodeList(psNode->link[LEFT]));
	DumpTriangleList( fp, psNode->psTriSameDir );
	DumpTriangleList( fp, psNode->psTriOppoDir );
	fprintf(fp,"%d\n",HuntNodeList(psNode->link[RIGHT]));

	OutputBSPNodes( fp, psNode->link[LEFT]);

	OutputBSPNodes( fp, psNode->link[RIGHT]);
}


/*

	Count Polys & Nodes in bsp - also makes a table of them

*/
int GetBSPPolyCount(PSBSPTREENODE psNode, int *NodeCount)
{
	BSPPolys=0;
	BSPNodes=0;
	CountBSPPolys(psNode);
	*NodeCount=BSPNodes;
	return BSPPolys;
}

#endif
#ifdef SAVEIMD
// new code the write out the connectors !
void _imd_save_connectors(FILE *fp, iIMDShape *s)
{
	iVector *p;
	int i;

	if (s->nconnectors != 0) {
		fprintf(fp,"CONNECTORS %d\n",s->nconnectors);
		p = s->connectors;
		for (i=0; i<s->nconnectors; i++, p++) {
			fprintf(fp,"\t%d %d %d\n", p->x,p->y,p->z);
		}
	}
}


//*************************************************************************
//*** save IMD file
//*
//* pre		shape successfully loaded
//*
//* params	filename = name of file to save to including .IMD extention
//*			s 			= pointer to IMD shape
//*
//* returns TRUE -> ok, FLASE -> error
//*
//******
iBool iV_IMDSave(STRING *filename, iIMDShape *s, BOOL PieIMD)
{
	FILE *fp;
	iIMDShape *sp;
	iIMDPoly *poly;
	int nlevel, i, j, d;

	if ((fp = fopen(filename,"w")) == NULL) {
		return FALSE;
  }

	if (PieIMD==TRUE) {
		fprintf(fp,"%s %d\n",PIE_NAME,PIE_VER);
	} else {
		fprintf(fp,"%s %d\n",IMD_NAME,IMD_VER);
	}
	fprintf(fp,"TYPE %x\n",s->flags);

	// if textured write tex page file info
	if (s->flags & iV_IMD_XTEX) {
		fprintf(fp,"TEXTURE %d %s %d %d\n",iV_TEXTYPE(s->texpage),
				iV_TEXNAME(s->texpage),iV_TEXWIDTH(s->texpage),
				iV_TEXHEIGHT(s->texpage));
	}

	// find number of levels in shape
	for (nlevel=0, sp = s; sp != NULL; sp = sp->next, nlevel++)
		;

	fprintf(fp,"LEVELS %d\n",nlevel);

	for (sp = s, i=0; i<nlevel; sp = sp->next, i++) {
		fprintf(fp,"LEVEL %d\n",(i+1));
		fprintf(fp,"POINTS %d\n",sp->npoints);

		// write shape points
		for (j = 0; j < sp->npoints; j++) {
			fprintf(fp,"\t%d %d %d\n",sp->points[j].x,sp->points[j].y,
					sp->points[j].z);
		}

		// write shape polys
#ifdef PIETOOL
		if (sp->BSPNode==NULL)
#endif
		{
			fprintf(fp,"POLYGONS %d\n",sp->npolys);
			for (poly = sp->polys, j=0; j<sp->npolys; j++, poly++) {
				fprintf(fp,"\t%8x %d",poly->flags,poly->npnts);
				for (d=0; d<poly->npnts; d++) {
					fprintf(fp," %d",poly->pindex[d]);
				}

				if (poly->flags & iV_IMD_TEXANIM) {

					if (poly->pTexAnim == NULL) {
						debug( LOG_3D, "No TexAnim pointer!\n" );
					} else {
						fprintf(fp," %d %d %d %d",
							poly->pTexAnim->nFrames,
							poly->pTexAnim->playbackRate,
							poly->pTexAnim->textureWidth,
							poly->pTexAnim->textureHeight);
					}
				}

				// if textured write texture uv's
				if (poly->flags & (iV_IMD_TEX | iV_IMD_PSXTEX)) {
					for (d=0; d<poly->npnts; d++) {
						fprintf(fp," %d %d",poly->vrt[d].u,poly->vrt[d].v);
					}
				}
				fprintf(fp,"\n");
			}
		}
#ifdef PIETOOL
		else
		{
			int NodeCount;

			fprintf(fp,"POLYGONS %d\n",GetBSPPolyCount(sp->BSPNode,&NodeCount) );
			OutputBSPPolys(fp,sp->BSPNode);	//

			fprintf(fp,"BSP %d\n",NodeCount );
			OutputBSPNodes(fp,sp->BSPNode);
		}
#endif

	}

	_imd_save_connectors(fp,s);	// Write out the connectors if they exist

	fclose(fp);

	return TRUE;
}
#endif


//*************************************************************************
//*** print IMD file info
//*
//* pre		shape successfully loaded
//*
//* params	s = pointer to IMD shape
//*
//******
void iV_IMDDebug(iIMDShape *s)
{
	iIMDShape *sp;
	iIMDPoly *poly;
	int nlevel, i, j, d;

	iV_DEBUG0("iV_IMDSave = file info *****************************\n");

	iV_DEBUG1("SHAPE\nflags\t%x\n",s->flags);
	iV_DEBUG1("texpage\t%d\n",s->texpage);
	iV_DEBUG1("oradius\t%d\n",s->oradius);
	iV_DEBUG1("sradius\t%d\n",s->sradius);
	iV_DEBUG1("radius\t%d\n",s->radius);
	iV_DEBUG2("xmin, xmax\t%d, %d\n",s->xmin,s->xmax);
	iV_DEBUG2("ymin, ymax\t%d, %d\n",s->ymin,s->ymax);
	iV_DEBUG2("zmin, zmax\t%d, %d\n",s->zmin,s->zmax);
	iV_DEBUG3("ocen\t%d %d %d\n",s->ocen.x,s->ocen.y,s->ocen.z);
	iV_DEBUG1("npoints\t%d\n",s->npoints);
	iV_DEBUG1("npolys\t%d\n",s->npolys);
	iV_DEBUG1("points\t%p\n",s->points);
	iV_DEBUG1("polys\t%p\n",s->polys);
	iV_DEBUG1("ntexanims\t%d\n",s->ntexanims);
	iV_DEBUG1("texanims\t%p\n",s->ntexanims);
	iV_DEBUG1("next\t%p\n",s->next);

	// find number of levels in shape

	for (nlevel=0, sp = s; sp != NULL; sp = sp->next, nlevel++)
		;

	iV_DEBUG1("nlevels\t%d\n",nlevel);

	for (sp = s, i=0; i<nlevel; sp = sp->next, i++) {
		iV_DEBUG1("POINTS %d\n",sp->npoints);

		for (j=0; j<sp->npoints; j++)
			iV_DEBUG3("\t%d %d %d\n",sp->points[j].x,sp->points[j].y,
					sp->points[j].z);

		iV_DEBUG1("POLYGONS %d\n",sp->npolys);

		// write shape polys
		for (poly = sp->polys, j=0; j<sp->npolys; j++, poly++) {
			iV_DEBUG2("\t%8x %d",poly->flags,poly->npnts);
			for (d=0; d<poly->npnts; d++)
				iV_DEBUG1(" %d",poly->pindex[d]);

			// if textured write texture uv's
			if (poly->flags & iV_IMD_TEX) {
				for (d=0; d<poly->npnts; d++)
					iV_DEBUG2(" %d %d",poly->vrt[d].u,poly->vrt[d].v);
			}

			iV_DEBUG0("\n");
		}
	}
}


//*************************************************************************
//*** free IMD shape memory
//*
//* pre		shape successfully allocated
//*
//* params	shape = pointer to IMD shape
//*
//******
void iV_IMDRelease(iIMDShape *s)
{
   int i;
   iIMDShape *d;

   if (s) {

		if (s->flags & iV_IMD_BINARY) {
			iV_HeapFree(s,0);
			return;
		}

		if (s->points) {
			iV_HeapFree(s->points,s->npoints * sizeof(iVector));
		}
		if (s->connectors) {
			iV_HeapFree(s->connectors,s->nconnectors * sizeof(iVector));
		}
		if (s->BSPNode) {
				FREE(s->BSPNode);	// I used MALLOC() so i'm going to use FREE()
		}
		if (s->polys) {
			for (i = 0; i < s->npolys; i++) {
				if (s->polys[i].pindex) {
					iV_HeapFree(s->polys[i].pindex,s->polys[i].npnts * sizeof(int));
				}
				if (s->polys[i].pTexAnim) {
					iV_HeapFree(s->polys[i].pTexAnim,sizeof(iTexAnim));
				}
				if (s->polys[i].vrt) {
					iV_HeapFree(s->polys[i].vrt,s->polys[i].npnts * sizeof(iVertex));
				}
			}
			iV_HeapFree(s->polys,s->npolys * sizeof(iIMDPoly));
		}
		iV_DEBUG0("imd[IMDRelease] = release successful\n");
		d = s->next;
		iV_HeapFree(s,sizeof(iIMDShape));
		iV_IMDRelease(d);
	}
}
