/* -------------------------------------------------------------------------------

Copyright (C) 1999-2007 id Software, Inc. and contributors.
For a list of contributors, see the accompanying CONTRIBUTORS file.

This file is part of GtkRadiant.

GtkRadiant 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.

GtkRadiant 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 GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

----------------------------------------------------------------------------------

This code has been altered significantly from its original form, to support
several games based on the Quake III Arena engine, in the form of "Q3Map2."

------------------------------------------------------------------------------- */



/* marker */
#define BSPFILE_RBSP_C



/* dependencies */
#include "q3map2.h"




/* -------------------------------------------------------------------------------

this file handles translating the bsp file format used by quake 3, rtcw, and ef
into the abstracted bsp file used by q3map2.

------------------------------------------------------------------------------- */

/* constants */
#define	LUMP_ENTITIES		0
#define	LUMP_SHADERS		1
#define	LUMP_PLANES			2
#define	LUMP_NODES			3
#define	LUMP_LEAFS			4
#define	LUMP_LEAFSURFACES	5
#define	LUMP_LEAFBRUSHES	6
#define	LUMP_MODELS			7
#define	LUMP_BRUSHES		8
#define	LUMP_BRUSHSIDES		9
#define	LUMP_DRAWVERTS		10
#define	LUMP_DRAWINDEXES	11
#define	LUMP_FOGS			12
#define	LUMP_SURFACES		13
#define	LUMP_LIGHTMAPS		14
#define	LUMP_LIGHTGRID		15
#define	LUMP_VISIBILITY		16
#define LUMP_LIGHTARRAY		17
#define	HEADER_LUMPS		18


/* types */
typedef struct
{
	char		ident[ 4 ];
	int			version;
	
	bspLump_t	lumps[ HEADER_LUMPS ];
}
rbspHeader_t;



/* light grid */
#define MAX_MAP_GRID		0xffff
#define MAX_MAP_GRIDARRAY	0x100000
#define LG_EPSILON			4


static void CopyLightGridLumps( rbspHeader_t *header )
{
	int				i;
	unsigned short	*inArray;
	bspGridPoint_t	*in, *out;
	
	
	/* get count */
	numBSPGridPoints = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTARRAY, sizeof( *inArray ) );
	
	/* allocate buffer */
	bspGridPoints = safe_malloc( numBSPGridPoints * sizeof( *bspGridPoints ) );
	memset( bspGridPoints, 0, numBSPGridPoints * sizeof( *bspGridPoints ) );
	
	/* copy */
	inArray = GetLump( (bspHeader_t*) header, LUMP_LIGHTARRAY );
	in = GetLump( (bspHeader_t*) header, LUMP_LIGHTGRID );
	out = bspGridPoints;
	for( i = 0; i < numBSPGridPoints; i++ )
	{
		memcpy( out, &in[ *inArray ], sizeof( *in ) );
		inArray++;
		out++;
	}
}


static void AddLightGridLumps( FILE *file, rbspHeader_t *header )
{
	int				i, j, k, c, d;
	int				numGridPoints, maxGridPoints;
	bspGridPoint_t	*gridPoints, *in, *out;
	int				numGridArray;
	unsigned short	*gridArray;
	qboolean		bad;
	
	
	/* allocate temporary buffers */
	maxGridPoints = (numBSPGridPoints < MAX_MAP_GRID) ? numBSPGridPoints : MAX_MAP_GRID;
	gridPoints = safe_malloc( maxGridPoints * sizeof( *gridPoints ) );
	gridArray = safe_malloc( numBSPGridPoints * sizeof( *gridArray ) );
	
	/* zero out */
	numGridPoints = 0;
	numGridArray = numBSPGridPoints;
	
	/* for each bsp grid point, find an approximate twin */
	Sys_Printf( "Storing lightgrid: %d points\n", numBSPGridPoints );
	for( i = 0; i < numGridArray; i++ )
	{
		/* get points */
		in = &bspGridPoints[ i ];
		
		/* walk existing list */
		for( j = 0; j < numGridPoints; j++ )
		{
			/* get point */
			out = &gridPoints[ j ];
			
			/* compare styles */
			if( memcmp(in->styles, out->styles, MAX_LIGHTMAPS) )
				continue;
			
			/* compare direction */
			d = abs( in->latLong[ 0 ] - out->latLong[ 0 ] );
			if( d < (255 - LG_EPSILON) && d > LG_EPSILON )
				continue;
			d = abs( in->latLong[ 1 ] - out->latLong[ 1 ] );
			if( d < 255 - LG_EPSILON && d > LG_EPSILON )
				continue;
			
			/* compare light */
			bad = qfalse;
			for( k = 0; (k < MAX_LIGHTMAPS && bad == qfalse); k++ )
			{
				for( c = 0; c < 3; c++ )
				{
					if( abs( (int) in->ambient[ k ][ c ] - (int) out->ambient[ k ][ c ]) > LG_EPSILON ||
						abs( (int) in->directed[ k ][ c ] - (int) out->directed[ k ][ c ]) > LG_EPSILON )
					{
						bad = qtrue;
						break;
					}
				}
			}
			
			/* failure */
			if( bad )
				continue;
			
			/* this sample is ok */
			break;
		}
		
		/* set sample index */
		gridArray[ i ] = (unsigned short) j;
		
		/* if no sample found, add a new one */
		if( j >= numGridPoints && numGridPoints < maxGridPoints )
		{
			out = &gridPoints[ numGridPoints++ ];
			memcpy( out, in, sizeof( *in ) );
		}
	}
	
	/* swap array */
	for( i = 0; i < numGridArray; i++ )
		gridArray[ i ] = LittleShort( gridArray[ i ] );
	
	/* write lumps */
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTGRID, gridPoints, (numGridPoints * sizeof( *gridPoints )) );
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTARRAY, gridArray, (numGridArray * sizeof( *gridArray )) );
	
	/* free buffers */
	free( gridPoints );
	free( gridArray );
}



/*
LoadRBSPFile()
loads a raven bsp file into memory
*/

void LoadRBSPFile( const char *filename )
{
	rbspHeader_t	*header;
	
	
	/* load the file header */
	LoadFile( filename, (void**) &header );

	/* swap the header (except the first 4 bytes) */
	SwapBlock( (int*) ((byte*) header + sizeof( int )), sizeof( *header ) - sizeof( int ) );
	
	/* make sure it matches the format we're trying to load */
	if( force == qfalse && *((int*) header->ident) != *((int*) game->bspIdent) )
		Error( "%s is not a %s file", filename, game->bspIdent );
	if( force == qfalse && header->version != game->bspVersion )
		Error( "%s is version %d, not %d", filename, header->version, game->bspVersion );
	
	/* load/convert lumps */
	numBSPShaders = CopyLump_Allocate( (bspHeader_t*) header, LUMP_SHADERS, (void **) &bspShaders, sizeof( bspShader_t ), &allocatedBSPShaders );
	
	numBSPModels = CopyLump_Allocate( (bspHeader_t*) header, LUMP_MODELS, (void **) &bspModels, sizeof( bspModel_t ), &allocatedBSPModels );
	
	numBSPPlanes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_PLANES, (void **) &bspPlanes, sizeof( bspPlane_t ), &allocatedBSPPlanes );
	
	numBSPLeafs = CopyLump( (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, sizeof( bspLeaf_t ) );
	
	numBSPNodes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_NODES, (void **) &bspNodes, sizeof( bspNode_t ), &allocatedBSPNodes );
	
	numBSPLeafSurfaces = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFSURFACES, (void **) &bspLeafSurfaces, sizeof( bspLeafSurfaces[ 0 ] ), &allocatedBSPLeafSurfaces );
	
	numBSPLeafBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_LEAFBRUSHES, (void **) &bspLeafBrushes, sizeof( bspLeafBrushes[ 0 ] ), &allocatedBSPLeafBrushes );
	
	numBSPBrushes = CopyLump_Allocate( (bspHeader_t*) header, LUMP_BRUSHES, (void **) &bspBrushes, sizeof( bspBrush_t ), &allocatedBSPLeafBrushes );
	
	numBSPBrushSides = CopyLump_Allocate( (bspHeader_t*) header, LUMP_BRUSHSIDES, (void **) &bspBrushSides, sizeof( bspBrushSide_t ), &allocatedBSPBrushSides );
	
	numBSPDrawVerts = GetLumpElements( (bspHeader_t*) header, LUMP_DRAWVERTS, sizeof( bspDrawVerts[ 0 ] ) );
		SetDrawVerts( numBSPDrawVerts );
		CopyLump( (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, sizeof( bspDrawVerts[ 0 ] ) );
	
	numBSPDrawSurfaces = GetLumpElements( (bspHeader_t*) header, LUMP_SURFACES, sizeof( bspDrawSurfaces[ 0 ] ) );
		SetDrawSurfaces( numBSPDrawSurfaces );
		CopyLump( (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, sizeof( bspDrawSurfaces[ 0 ] ) );
	
	numBSPFogs = CopyLump( (bspHeader_t*) header, LUMP_FOGS, bspFogs, sizeof( bspFogs[ 0 ] ) );
	
	numBSPDrawIndexes = CopyLump( (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, sizeof( bspDrawIndexes[ 0 ] ) );
	
	numBSPVisBytes = CopyLump( (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, 1 );
	
	numBSPLightBytes = GetLumpElements( (bspHeader_t*) header, LUMP_LIGHTMAPS, 1 );
		bspLightBytes = safe_malloc( numBSPLightBytes );
		CopyLump( (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, 1 );
	
	bspEntDataSize = CopyLump_Allocate( (bspHeader_t*) header, LUMP_ENTITIES, (void **) &bspEntData, 1, &allocatedBSPEntData);
	
	CopyLightGridLumps( header );
	
	/* free the file buffer */
	free( header );
}



/*
WriteRBSPFile()
writes a raven bsp file
*/

void WriteRBSPFile( const char *filename )
{		
	rbspHeader_t	outheader, *header;
	FILE			*file;
	time_t			t;
	char			marker[ 1024 ];
	int				size;
	
	
	/* set header */
	header = &outheader;
	memset( header, 0, sizeof( *header ) );
	
	//%	Swapfile();
	
	/* set up header */
	*((int*) (bspHeader_t*) header->ident) = *((int*) game->bspIdent);
	header->version = LittleLong( game->bspVersion );
	
	/* write initial header */
	file = SafeOpenWrite( filename );
	SafeWrite( file, (bspHeader_t*) header, sizeof( *header ) );	/* overwritten later */
	
	/* add marker lump */
	time( &t );
	sprintf( marker, "I LOVE MY Q3MAP2 %s on %s)", Q3MAP_VERSION, asctime( localtime( &t ) ) );
	AddLump( file, (bspHeader_t*) header, 0, marker, strlen( marker ) + 1 );
	
	/* add lumps */
	AddLump( file, (bspHeader_t*) header, LUMP_SHADERS, bspShaders, numBSPShaders * sizeof( bspShader_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_PLANES, bspPlanes, numBSPPlanes * sizeof( bspPlane_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFS, bspLeafs, numBSPLeafs * sizeof( bspLeaf_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_NODES, bspNodes, numBSPNodes * sizeof( bspNode_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_BRUSHES, bspBrushes, numBSPBrushes*sizeof( bspBrush_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_BRUSHSIDES, bspBrushSides, numBSPBrushSides * sizeof( bspBrushSides[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFSURFACES, bspLeafSurfaces, numBSPLeafSurfaces * sizeof( bspLeafSurfaces[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_LEAFBRUSHES, bspLeafBrushes, numBSPLeafBrushes * sizeof( bspLeafBrushes[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_MODELS, bspModels, numBSPModels * sizeof( bspModel_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_DRAWVERTS, bspDrawVerts, numBSPDrawVerts * sizeof( bspDrawVerts[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_SURFACES, bspDrawSurfaces, numBSPDrawSurfaces * sizeof( bspDrawSurfaces[ 0 ] ) );
	AddLump( file, (bspHeader_t*) header, LUMP_VISIBILITY, bspVisBytes, numBSPVisBytes );
	AddLump( file, (bspHeader_t*) header, LUMP_LIGHTMAPS, bspLightBytes, numBSPLightBytes );
	AddLightGridLumps( file, header );
	AddLump( file, (bspHeader_t*) header, LUMP_ENTITIES, bspEntData, bspEntDataSize );
	AddLump( file, (bspHeader_t*) header, LUMP_FOGS, bspFogs, numBSPFogs * sizeof( bspFog_t ) );
	AddLump( file, (bspHeader_t*) header, LUMP_DRAWINDEXES, bspDrawIndexes, numBSPDrawIndexes * sizeof( bspDrawIndexes[ 0 ] ) );
	
	/* emit bsp size */
	size = ftell( file );
	Sys_Printf( "Wrote %.1f MB (%d bytes)\n", (float) size / (1024 * 1024), size );
	
	/* write the completed header */
	fseek( file, 0, SEEK_SET );
	SafeWrite( file, header, sizeof( *header ) );
	
	/* close the file */
	fclose( file );	
}
