#include "dkdata.h"

extern vec3_t adjust;
//==============================================================

/* LIGHTWAVE SPECIFIC STUFF */

// polygons are stored in surface order

int			useduvchunk = false;

extinfo_t	extrainfo;					// extra info header
extsurf_t	surface[MAX_SURFACES];		// surface information
extpoly_t	polygons[MAX_TRIANGLES];	// polygon information
//	Nelno:	see dkdata.h
extpoly_t	base_polygons[MAX_TRIANGLES];	// polygon information
int			glsurfpoints[16384];
int			numsurfpoints = 0;

lwpoly_t	controlpoly;				// for converting re-ordered polys
lwinfo_t	controlobject;
qboolean	polymapcreated = false;
int			polymapping[MAX_TRIANGLES];

/*
============
InitControlObject

This function initailizes the control object
used to compare against when each frame is grabbed
to make sure the poly order is the same as the base frame
============
*/
void InitControlObject (void)
{
	controlpoly.index_xyz = malloc (sizeof (short) * 3);
	controlpoly.index_xyz[0] = lwinfo.lwpoly[0].index_xyz[0];
	controlpoly.index_xyz[1] = lwinfo.lwpoly[0].index_xyz[1];
	controlpoly.index_xyz[2] = lwinfo.lwpoly[0].index_xyz[2];

	memcpy (&controlobject, &lwinfo, sizeof (lwinfo_t));
}

/*
============
CreatePolyMapping
============
*/
void CreatePolyMapping (void)
{
	int	i, j;

	// this expects lwinfo.num_polys to be equal to model.num_tris.
	// if they are not equal, an error would have been flagged and we 
	// would not be here
	for (i = 0; i < model.num_tris; i++)
	{
		for (j = 0; j < lwinfo.num_polys; j++)
		{
			if (controlobject.lwpoly[i].index_xyz[0] == lwinfo.lwpoly[j].index_xyz[0] &&
				controlobject.lwpoly[i].index_xyz[1] == lwinfo.lwpoly[j].index_xyz[1] &&
				controlobject.lwpoly[i].index_xyz[2] == lwinfo.lwpoly[j].index_xyz[2])
			{
				printf ("Test frame poly %03d mapped to Control frame poly %03d\n", j, i);
				polymapping[i] = j;
			}
		}
	}

	polymapcreated = true;
}

/*
============
ReOrderPolys
============
*/
void ReOrderPolys (triangle_t *ptri, int numtriangles)
{
	int	i, j, k;

	if (lwinfo.num_polys != numtriangles)
		Error ("ReOrderPolys Error: Different poly count!\n");

	if (!polymapcreated)
		CreatePolyMapping ();

	for (i = 0; i < lwinfo.num_polys; i++)
	{
		polygons[i].surfid = lwinfo.lwpoly[polymapping[i]].index_surface - 1;

		for (j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				ptri[i].verts[j][k] = lwinfo.lwpoint[lwinfo.lwpoly[polymapping[i]].index_xyz[j]].point[k];
	}
}

/*
============
FindTextureIndexForPoint
============
*/
int FindTextureIndexForPoly (int index)
{
	return surface[polygons[index].surfid].skinindex;
}

/*
============
FindSurfaceFlagForPoly
============
*/
int FindSurfaceFlagForPoly (int index)
{
	return surface[polygons[index].surfid].flags;
}

/*
============
AttachPolysToSurfaces
============
*/
void AttachPolysToSurfaces (void)
{
	int	i, j;

	for (i = 0; i < extrainfo.num_surfs; i++)
	{
		for (j = 0; j < model.num_tris; j++)
		{
			if (polygons[j].surfid == i)
			{
				surface[i].num_polys++;
			}
		}
	}
}

/*
============
WriteExtraData
============
*/
void WriteExtraData (void)
{
	int			i;
	char		filename[1024];
	FILE		*f;
	extinfo_t	extratemp;

	sprintf (filename, "%s.ext", modelname);

	f = SafeOpenWrite (filename);

	VectorCopy (adjust, extrainfo.org);

	extrainfo.ident			= IDEXTRAHEADER;
	extrainfo.version		= EXTRA_VERSION;
	extrainfo.num_polys		= model.num_tris;
	extrainfo.num_glcmds	= numsurfpoints;
	extrainfo.ofs_surf		= sizeof (extinfo_t);												// offset of first surface
	extrainfo.ofs_poly		= extrainfo.ofs_surf + extrainfo.num_surfs * sizeof (extsurf_t);
	extrainfo.ofs_glcmds	= extrainfo.ofs_poly + extrainfo.num_polys * sizeof (extpoly_t);
	extrainfo.ofs_end		= extrainfo.ofs_glcmds + extrainfo.num_glcmds * 4;

	for (i=0 ; i<sizeof(extinfo_t)/4 ; i++)
		((int *)&extratemp)[i] = LittleLong (((int *)&extrainfo)[i]);

	SafeWrite (f, &extratemp, sizeof(extinfo_t));							// write header
	SafeWrite (f, surface, extrainfo.num_surfs * sizeof (extsurf_t));		// write surface info
	//	Nelno:	hacked to write out base_polygons for correct surfid info
	SafeWrite (f, base_polygons, extrainfo.num_polys * sizeof (extpoly_t));	// write polygon info
	SafeWrite (f, glsurfpoints, extrainfo.num_glcmds * 4);

	fclose (f);
}

/*
============
SwapYZ
============
*/
void SwapYZ (void)
{
	int		i;
	float	temp;

	for (i = 0; i < lwinfo.num_points; i++)
	{
		temp = lwinfo.lwpoint[i].point[1];
		lwinfo.lwpoint[i].point[1] = lwinfo.lwpoint[i].point[2];
		lwinfo.lwpoint[i].point[2] = temp;
	}
}

/*
============
LW_ParseChunkUVCV
============
*/
void LW_ParseChunkUVCV (byte *in, int chunklength)
{
	int		i;
	int		polynum, num_xyz;
	float	u, v;
	int		uservalue;
	byte	*chunkend;

	chunkend = in + chunklength;

	while (in < chunkend)
	{
		polynum = BigLong(*(int *) in);
		in += sizeof (int);

		num_xyz = BigLong(*(int *) in);
		in += sizeof (int);

		if (num_xyz != 3)
			Error ("LW_ParseChunkUVCV: Only triangles are supported\n");

		for (i = 0; i < num_xyz; i++)
		{
			u = BigFloat (*(float *) in);
			in += sizeof (float);

			v = BigFloat (*(float *) in);
			in += sizeof (float);

			uservalue = BigLong (*(int *) in);
			in += sizeof (int);

			uvdata[polynum][i][0] = u;
			uvdata[polynum][i][1] = v;
		}
		useduvchunk = TRUE;
	}

}

/*
============
LW_ParseChunkUVIM
============
*/
void LW_ParseChunkUVIM (byte *in, int chunklength)
{
	int		count;
	byte	*chunkend;

	count = 0;

	chunkend = in + chunklength;

	while (in < chunkend)
	{
		if (count > lwinfo.num_surfaces)
			Error ("LW_ParseChunkUVIM: Mismatched surface count");

		lwinfo.lwsurface[count].texmode[0] = TEXMODE_CTEX;

		// read in the surface name
		strcpy (lwinfo.lwsurface[count].timg[0], (char *) in);

		if (!strcmpi(lwinfo.lwsurface[count].timg[0], "NULL"))
			*lwinfo.lwsurface[count].timg[0] = 0x00;

		in += strlen((char *) in) + 1;
		if (!*in) 
			in++;

		count++;
	}
}

/*
============
LoadUVFromLWO
============
*/
void LoadUVFromLWO (char *filename)
{
	byte		*start, *buffer, *end, *chunkend;
	char		chunkname[5];
	int			chunklength;

	// open lwo file
	LW_OpenFile (filename, &start, &end);

	//point buffer to start of file
	buffer = start;

	// parse chunks
	while (buffer < end)
	{
		memcpy (chunkname, buffer, 4);
		chunkname[4] = 0x00;
		buffer += 4;

		chunklength = BigLong(*(int *)buffer);
		buffer += 4;
		chunkend = (buffer + align (chunklength));

		if (!strcmp(chunkname, "UVCV") || !strcmp(chunkname, "UV00"))
			LW_ParseChunkUVCV (buffer, chunklength);
		if (!strcmp(chunkname, "UVIM"))
			LW_ParseChunkUVIM (buffer, chunklength);

		buffer += chunklength;
	}
}

/*
============
CalcPolygonNormals
============
*/
void CalcPolygonNormals (void)
{
	int		i, j, k;
	vec3_t	point[3], temp1, temp2, normal;
	
	// set all normals to -1 (checked against in next step)
	for (i = 0; i < lwinfo.num_surfaces; i++)
		surface[i].normal[0] = -1;

	for (i = 0; i < lwinfo.num_polys; i++)
	{
		if ((surface[lwinfo.lwpoly[i].index_surface].normal[0]) == -1)
			continue;

		for (j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				point[j][k] = base_xyz[triangles[i].index_xyz[j]][k];

		VectorSubtract (point[0], point[1], temp1);
		VectorSubtract (point[0], point[2], temp2);
		CrossProduct (temp1, temp2, normal);
		VectorNormalize (normal, normal);
		VectorCopy (normal, surface[lwinfo.lwpoly[i].index_surface].normal);

	}
}

/*
===============
LoadLightwaveTriangles

Interface function for lightwave files
===============
*/
int LoadLightwaveTriangles (char *filename, triangle_t **pptri, int *numtriangles)
{
	int			i, j, k;
	byte		*start, *buffer, *end, *chunkend;
	char		chunkname[5];
	int			chunklength;
	triangle_t	*ptri;								
	long		mem;

	// open lwo file
	if (!LW_OpenFile (filename, &start, &end))
		return 0;

	//point buffer to start of file
	buffer = start;

	// parse chunks
	while (buffer < end)
	{
		memcpy (chunkname, buffer, 4);
		chunkname[4] = 0x00;
		buffer += 4;

		chunklength = BigLong(*(int *)buffer);
		buffer += 4;
		chunkend = (buffer + align (chunklength));

		if (!strcmp(chunkname, "PNTS"))
			LW_ParseChunkPNTS (buffer, chunklength);
		else if (!strcmp(chunkname, "POLS"))
			LW_ParseChunkPOLS (buffer, chunklength);

		buffer += chunklength;
	}

	SwapYZ ();

	mem = lwinfo.num_polys * sizeof (triangle_t);
	ptri = (triangle_t *) malloc (mem);
	if (!ptri)
		Error ("Failed to allocate %d bytes\n", lwinfo.num_polys * sizeof (triangle_t));

	// convert lwo polys to triangles
	for (i = 0; i < lwinfo.num_polys; i++)
	{
		polygons[i].surfid = lwinfo.lwpoly[i].index_surface - 1;

		for (j = 0; j < 3; j++)
			for (k = 0; k < 3; k++)
				ptri[i].verts[j][k] = lwinfo.lwpoint[lwinfo.lwpoly[i].index_xyz[j]].point[k];
	}

	*numtriangles = lwinfo.num_polys;
	*pptri = ptri;

	CalcPolygonNormals ();
}

/*
============
SetSurfaceFlags
============
*/
void SetSurfaceFlags (int surfnum)
{
	char	*pos, name[32];
	int		len, c;

	c = '_';

	if ((pos = strchr(surface[surfnum].name, c)) == NULL)
		return;

	len = pos - surface[surfnum].name;

	strcpy (name, surface[surfnum].name);

	name[len] = 0x00;

	if (!strcmp ("hr", name))
		surface[surfnum].flags = SRF_NODRAW;
	else if (!strcmp ("trans33", name))
		surface[surfnum].flags = SRF_TRANS33;
	else if (!strcmp ("trans66", name))
		surface[surfnum].flags = SRF_TRANS66;
	else if (!strcmp ("glo", name))
		surface[surfnum].flags = SRF_GLOW;
	else if (!strcmp ("glo33", name))
		surface[surfnum].flags = SRF_GLOW33;
	else if (!strcmp ("glo66", name))
		surface[surfnum].flags = SRF_GLOW66;
	else 
		surface[surfnum].flags = 0;
}

/*
============
CreateSurfaceData
============
*/
void CreateSurfaceData (int numtri)
{
	int			i, j, k;
	bitmap_t	bitmap;
	char		path[128];
	char		skinname[128];
	char		temp[128];

	for (i = 0; i < lwinfo.num_surfaces; i++)
	{
		// set surface name
		strcpy (surface[i].name, lwinfo.lwsurface[i].name);

		// set surface flags
		SetSurfaceFlags (i);

		// get surface  texture information
		for (j = 0; j < MAX_TEXCHUNKS; j++)
		{
			// we only support CTEX chunks for now
			if (lwinfo.lwsurface[i].texmode[j] != TEXMODE_CTEX)
				continue;
			
			ExtractLowestSubdir (lwinfo.lwsurface[i].timg[j], temp);
			
			if (temp[0] != 0)
				strcpy (lwinfo.lwsurface[i].timg[j], temp);

			sprintf (path, "%s/maps/%s", modelname, lwinfo.lwsurface[i].timg[j]);

			LoadBMP (path, &bitmap);
			if((surface[i].skinwidth = bitmap.width) > 256)
				Error ("Image width is larger than 256\n");
			if((surface[i].skinheight = bitmap.height) > 256)
				Error ("Image height is larger than 256\n");
			FreeBMP (&bitmap);

			sprintf (skinname, "models/%s/%s", modelname, lwinfo.lwsurface[i].timg[j]);
			strlwr (skinname);

			// check to see if name is already in list
			for (k = 0; k < model.num_skins; k++)
			{
				if (!strcmp (g_skins[k], skinname))
				{
					surface[i].skinindex = k;
					goto skinexists;
				}
			}

			surface[i].skinindex = model.num_skins;
			strcpy (g_skins[model.num_skins++], skinname);
skinexists:;
		}
	}
}

/*
============
LoadSurfaces
============
*/
void LoadSurfaces (char	*filename)
{
	byte	*start, *buffer, *end, *chunkend;
	char	chunkname[5];
	int		chunklength;

	// open lwo file
	LW_OpenFile (filename, &start, &end);

	//point buffer to start of file
	buffer = start;

	// parse chunks
	while (buffer < end)
	{
		memcpy (chunkname, buffer, 4);
		chunkname[4] = 0x00;
		buffer += 4;

		chunklength = BigLong(*(int *)buffer);
		buffer += 4;
		chunkend = (buffer + align (chunklength));

		if (!strcmp(chunkname, "SRFS"))
			LW_ParseChunkSRFS (buffer, chunklength);
		else if (!strcmp(chunkname, "SURF"))
			LW_ParseChunkSURF (buffer, chunklength);

		buffer += chunklength;
	}

	extrainfo.num_surfs = lwinfo.num_surfaces;
}
