/*
===========================================================================

Return to Castle Wolfenstein single player GPL Source Code
Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. 

This file is part of the Return to Castle Wolfenstein single player GPL Source Code (RTCW SP Source Code).  

RTCW SP Source Code 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 3 of the License, or
(at your option) any later version.

RTCW SP Source Code 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 RTCW SP Source Code.  If not, see <http://www.gnu.org/licenses/>.

In addition, the RTCW SP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW SP Source Code.  If not, please request a copy in writing from id Software at the address below.

If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.

===========================================================================
*/

//===========================================================================
//
// Name:         map_q2.c
// Function:     map loading and writing
// Programmer:   Mr Elusive (MrElusive@demigod.demon.nl)
// Last update:  1997-12-03
// Tab Size:     3
//===========================================================================

//===========================================================================
// ANSI, Area Navigational System Interface
// AAS,  Area Awareness System
//===========================================================================

#include "qbsp.h"
#include "l_mem.h"
#include "../botlib/aasfile.h"           //aas_bbox_t
#include "aas_store.h"       //AAS_MAX_BBOXES
#include "aas_cfg.h"
#include "aas_map.h"         //AAS_CreateMapBrushes
#include "l_bsp_q2.h"

#ifdef ME

#define NODESTACKSIZE       1024

int nodestack[NODESTACKSIZE];
int *nodestackptr;
int nodestacksize = 0;
int brushmodelnumbers[MAX_MAPFILE_BRUSHES];
int dbrushleafnums[MAX_MAPFILE_BRUSHES];
int dplanes2mapplanes[MAX_MAPFILE_PLANES];

#endif //ME

//====================================================================

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_CreateMapTexinfo( void ) {
	int i;

	for ( i = 0; i < numtexinfo; i++ )
	{
		memcpy( map_texinfo[i].vecs, texinfo[i].vecs, sizeof( float ) * 2 * 4 );
		map_texinfo[i].flags = texinfo[i].flags;
		map_texinfo[i].value = texinfo[i].value;
		strcpy( map_texinfo[i].texture, texinfo[i].texture );
		map_texinfo[i].nexttexinfo = 0;
	} //end for
} //end of the function Q2_CreateMapTexinfo

/*
===========
Q2_BrushContents
===========
*/
int Q2_BrushContents( mapbrush_t *b ) {
	int contents;
	side_t      *s;
	int i;
	int trans;

	s = &b->original_sides[0];
	contents = s->contents;
	trans = texinfo[s->texinfo].flags;
	for ( i = 1; i < b->numsides; i++, s++ )
	{
		s = &b->original_sides[i];
		trans |= texinfo[s->texinfo].flags;
		if ( s->contents != contents ) {
			Log_Print( "Entity %i, Brush %i: mixed face contents\n"
					   , b->entitynum, b->brushnum );
			Log_Print( "texture name = %s\n", texinfo[s->texinfo].texture );
			break;
		}
	}

	// if any side is translucent, mark the contents
	// and change solid to window
	if ( trans & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
		contents |= CONTENTS_Q2TRANSLUCENT;
		if ( contents & CONTENTS_SOLID ) {
			contents &= ~CONTENTS_SOLID;
			contents |= CONTENTS_WINDOW;
		}
	}

	return contents;
}

#ifdef ME

//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void MakeAreaPortalBrush( mapbrush_t *brush ) {
	int sn;
	side_t *s;

	brush->contents = CONTENTS_AREAPORTAL;

	for ( sn = 0; sn < brush->numsides; sn++ )
	{
		s = brush->original_sides + sn;
		//make sure the surfaces are not hint or skip
		s->surf &= ~( SURF_HINT | SURF_SKIP );
		//
		s->texinfo = 0;
		s->contents = CONTENTS_AREAPORTAL;
	} //end for
} //end of the function MakeAreaPortalBrush
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void DPlanes2MapPlanes( void ) {
	int i;

	for ( i = 0; i < numplanes; i++ )
	{
		dplanes2mapplanes[i] = FindFloatPlane( dplanes[i].normal, dplanes[i].dist );
	} //end for
} //end of the function DPlanes2MapPlanes
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void MarkVisibleBrushSides( mapbrush_t *brush ) {
	int n, i, planenum;
	side_t *side;
	dface_t *face;
	//
	for ( n = 0; n < brush->numsides; n++ )
	{
		side = brush->original_sides + n;
		//if this side is a bevel or the leaf number of the brush is unknown
		if ( ( side->flags & SFL_BEVEL ) || brush->leafnum < 0 ) {
			//this side is a valid splitter
			side->flags |= SFL_VISIBLE;
			continue;
		} //end if
		  //assum this side will not be used as a splitter
		side->flags &= ~SFL_VISIBLE;
		//check if the side plane is used by a visible face
		for ( i = 0; i < numfaces; i++ )
		{
			face = &dfaces[i];
			planenum = dplanes2mapplanes[face->planenum];
			if ( ( planenum & ~1 ) == ( side->planenum & ~1 ) ) {
				//this side is a valid splitter
				side->flags |= SFL_VISIBLE;
			} //end if
		} //end for
	} //end for
} //end of the function MarkVisibleBrushSides

#endif //ME

/*
=================
Q2_ParseBrush
=================
*/
void Q2_ParseBrush( script_t *script, entity_t *mapent ) {
	mapbrush_t *b;
	int i, j, k;
	int mt;
	side_t *side, *s2;
	int planenum;
	brush_texture_t td;
	int planepts[3][3];
	token_t token;

	if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) {
		Error( "nummapbrushes == MAX_MAPFILE_BRUSHES" );
	}

	b = &mapbrushes[nummapbrushes];
	b->original_sides = &brushsides[nummapbrushsides];
	b->entitynum = num_entities - 1;
	b->brushnum = nummapbrushes - mapent->firstbrush;
	b->leafnum = -1;

	do
	{
		if ( !PS_ReadToken( script, &token ) ) {
			break;
		}
		if ( !strcmp( token.string, "}" ) ) {
			break;
		}

		//IDBUG: mixed use of MAX_MAPFILE_? and MAX_MAP_? this could
		//			lead to out of bound indexing of the arrays
		if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) {
			Error( "MAX_MAPFILE_BRUSHSIDES" );
		}
		side = &brushsides[nummapbrushsides];

		//read the three point plane definition
		for ( i = 0; i < 3; i++ )
		{
			if ( i != 0 ) {
				PS_ExpectTokenString( script, "(" );
			}
			for ( j = 0; j < 3; j++ )
			{
				PS_ExpectAnyToken( script, &token );
				planepts[i][j] = atof( token.string );
			} //end for
			PS_ExpectTokenString( script, ")" );
		} //end for

		//
		//read the texturedef
		//
		PS_ExpectAnyToken( script, &token );
		strcpy( td.name, token.string );

		PS_ExpectAnyToken( script, &token );
		td.shift[0] = atol( token.string );
		PS_ExpectAnyToken( script, &token );
		td.shift[1] = atol( token.string );
		PS_ExpectAnyToken( script, &token );
		td.rotate = atol( token.string );
		PS_ExpectAnyToken( script, &token );
		td.scale[0] = atof( token.string );
		PS_ExpectAnyToken( script, &token );
		td.scale[1] = atof( token.string );

		//find default flags and values
		mt = FindMiptex( td.name );
		td.flags = textureref[mt].flags;
		td.value = textureref[mt].value;
		side->contents = textureref[mt].contents;
		side->surf = td.flags = textureref[mt].flags;

		//check if there's a number available
		if ( PS_CheckTokenType( script, TT_NUMBER, 0, &token ) ) {
			side->contents = token.intvalue;
			PS_ExpectTokenType( script, TT_NUMBER, 0, &token );
			side->surf = td.flags = token.intvalue;
			PS_ExpectTokenType( script, TT_NUMBER, 0, &token );
			td.value = token.intvalue;
		}

		// translucent objects are automatically classified as detail
		if ( side->surf & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
			side->contents |= CONTENTS_DETAIL;
		}
		if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
			side->contents |= CONTENTS_DETAIL;
		}
		if ( fulldetail ) {
			side->contents &= ~CONTENTS_DETAIL;
		}
		if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 )
								   | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST )  ) ) {
			side->contents |= CONTENTS_SOLID;
		}

		// hints and skips are never detail, and have no content
		if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) {
			side->contents = 0;
			side->surf &= ~CONTENTS_DETAIL;
		}

#ifdef ME
		//for creating AAS... this side is textured
		side->flags |= SFL_TEXTURED;
#endif //ME
	   //
	   // find the plane number
	   //
		planenum = PlaneFromPoints( planepts[0], planepts[1], planepts[2] );
		if ( planenum == -1 ) {
			Log_Print( "Entity %i, Brush %i: plane with no normal\n"
					   , b->entitynum, b->brushnum );
			continue;
		}

		//
		// see if the plane has been used already
		//
		for ( k = 0 ; k < b->numsides ; k++ )
		{
			s2 = b->original_sides + k;
			if ( s2->planenum == planenum ) {
				Log_Print( "Entity %i, Brush %i: duplicate plane\n"
						   , b->entitynum, b->brushnum );
				break;
			}
			if ( s2->planenum == ( planenum ^ 1 ) ) {
				Log_Print( "Entity %i, Brush %i: mirrored plane\n"
						   , b->entitynum, b->brushnum );
				break;
			}
		}
		if ( k != b->numsides ) {
			continue;       // duplicated

		}
		//
		// keep this side
		//

		side = b->original_sides + b->numsides;
		side->planenum = planenum;
		side->texinfo = TexinfoForBrushTexture( &mapplanes[planenum],
												&td, vec3_origin );

		// save the td off in case there is an origin brush and we
		// have to recalculate the texinfo
		side_brushtextures[nummapbrushsides] = td;

		nummapbrushsides++;
		b->numsides++;
	} while ( 1 );

	// get the content for the entire brush
	b->contents = Q2_BrushContents( b );

#ifdef ME
	if ( BrushExists( b ) ) {
		c_squattbrushes++;
		b->numsides = 0;
		return;
	} //end if

	if ( create_aas ) {
		//create AAS brushes, and add brush bevels
		AAS_CreateMapBrushes( b, mapent, true );
		//NOTE: if we return here then duplicate plane errors occur for the non world entities
		return;
	} //end if
#endif //ME

	// allow detail brushes to be removed
	if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) {
		b->numsides = 0;
		return;
	}

	// allow water brushes to be removed
	if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
		b->numsides = 0;
		return;
	}

	// create windings for sides and bounds for brush
	MakeBrushWindings( b );

	// brushes that will not be visible at all will never be
	// used as bsp splitters
	if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
		c_clipbrushes++;
		for ( i = 0 ; i < b->numsides ; i++ )
			b->original_sides[i].texinfo = TEXINFO_NODE;
	}

	//
	// origin brushes are removed, but they set
	// the rotation origin for the rest of the brushes
	// in the entity.  After the entire entity is parsed,
	// the planenums and texinfos will be adjusted for
	// the origin brush
	//
	if ( b->contents & CONTENTS_ORIGIN ) {
		char string[32];
		vec3_t origin;

		if ( num_entities == 1 ) {
			Error( "Entity %i, Brush %i: origin brushes not allowed in world"
				   , b->entitynum, b->brushnum );
			return;
		}

		VectorAdd( b->mins, b->maxs, origin );
		VectorScale( origin, 0.5, origin );

		sprintf( string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2] );
		SetKeyValue( &entities[b->entitynum], "origin", string );

		VectorCopy( origin, entities[b->entitynum].origin );

		// don't keep this brush
		b->numsides = 0;

		return;
	}

	AddBrushBevels( b );

	nummapbrushes++;
	mapent->numbrushes++;
}

/*
================
Q2_MoveBrushesToWorld

Takes all of the brushes from the current entity and
adds them to the world's brush list.

Used by func_group and func_areaportal
================
*/
void Q2_MoveBrushesToWorld( entity_t *mapent ) {
	int newbrushes;
	int worldbrushes;
	mapbrush_t  *temp;
	int i;

	// this is pretty gross, because the brushes are expected to be
	// in linear order for each entity

	newbrushes = mapent->numbrushes;
	worldbrushes = entities[0].numbrushes;

	temp = GetMemory( newbrushes * sizeof( mapbrush_t ) );
	memcpy( temp, mapbrushes + mapent->firstbrush, newbrushes * sizeof( mapbrush_t ) );

#if 0       // let them keep their original brush numbers
	for ( i = 0 ; i < newbrushes ; i++ )
		temp[i].entitynum = 0;
#endif

	// make space to move the brushes (overlapped copy)
	memmove( mapbrushes + worldbrushes + newbrushes,
			 mapbrushes + worldbrushes,
			 sizeof( mapbrush_t ) * ( nummapbrushes - worldbrushes - newbrushes ) );

	// copy the new brushes down
	memcpy( mapbrushes + worldbrushes, temp, sizeof( mapbrush_t ) * newbrushes );

	// fix up indexes
	entities[0].numbrushes += newbrushes;
	for ( i = 1 ; i < num_entities ; i++ )
		entities[i].firstbrush += newbrushes;
	FreeMemory( temp );

	mapent->numbrushes = 0;
}

/*
================
Q2_ParseMapEntity
================
*/
qboolean    Q2_ParseMapEntity( script_t *script ) {
	entity_t    *mapent;
	epair_t *e;
	side_t *s;
	int i, j;
	int startbrush, startsides;
	vec_t newdist;
	mapbrush_t *b;
	token_t token;

	if ( !PS_ReadToken( script, &token ) ) {
		return false;
	}

	if ( strcmp( token.string, "{" ) ) {
		Error( "ParseEntity: { not found" );
	}

	if ( num_entities == MAX_MAP_ENTITIES ) {
		Error( "num_entities == MAX_MAP_ENTITIES" );
	}

	startbrush = nummapbrushes;
	startsides = nummapbrushsides;

	mapent = &entities[num_entities];
	num_entities++;
	memset( mapent, 0, sizeof( *mapent ) );
	mapent->firstbrush = nummapbrushes;
	mapent->numbrushes = 0;
//	mapent->portalareas[0] = -1;
//	mapent->portalareas[1] = -1;

	do
	{
		if ( !PS_ReadToken( script, &token ) ) {
			Error( "ParseEntity: EOF without closing brace" );
		} //end if
		if ( !strcmp( token.string, "}" ) ) {
			break;
		}
		if ( !strcmp( token.string, "{" ) ) {
			Q2_ParseBrush( script, mapent );
		} //end if
		else
		{
			PS_UnreadLastToken( script );
			e = ParseEpair( script );
			e->next = mapent->epairs;
			mapent->epairs = e;
		} //end else
	} while ( 1 );

	GetVectorForKey( mapent, "origin", mapent->origin );

	//
	// if there was an origin brush, offset all of the planes and texinfo
	//
	if ( mapent->origin[0] || mapent->origin[1] || mapent->origin[2] ) {
		for ( i = 0 ; i < mapent->numbrushes ; i++ )
		{
			b = &mapbrushes[mapent->firstbrush + i];
			for ( j = 0 ; j < b->numsides ; j++ )
			{
				s = &b->original_sides[j];
				newdist = mapplanes[s->planenum].dist -
						  DotProduct( mapplanes[s->planenum].normal, mapent->origin );
				s->planenum = FindFloatPlane( mapplanes[s->planenum].normal, newdist );
				s->texinfo = TexinfoForBrushTexture( &mapplanes[s->planenum],
													 &side_brushtextures[s - brushsides], mapent->origin );
			}
			MakeBrushWindings( b );
		}
	}

	// group entities are just for editor convenience
	// toss all brushes into the world entity
	if ( !strcmp( "func_group", ValueForKey( mapent, "classname" ) ) ) {
		Q2_MoveBrushesToWorld( mapent );
		mapent->numbrushes = 0;
		return true;
	}

	// areaportal entities move their brushes, but don't eliminate
	// the entity
	if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) {
		char str[128];

		if ( mapent->numbrushes != 1 ) {
			Error( "Entity %i: func_areaportal can only be a single brush", num_entities - 1 );
		}

		b = &mapbrushes[nummapbrushes - 1];
		b->contents = CONTENTS_AREAPORTAL;
		c_areaportals++;
		mapent->areaportalnum = c_areaportals;
		// set the portal number as "style"
		sprintf( str, "%i", c_areaportals );
		SetKeyValue( mapent, "style", str );
		Q2_MoveBrushesToWorld( mapent );
		return true;
	}

	return true;
}

//===================================================================

/*
================
LoadMapFile
================
*/
void Q2_LoadMapFile( char *filename ) {
	int i;
	script_t *script;

	Log_Print( "-- Q2_LoadMapFile --\n" );
#ifdef ME
	//loaded map type
	loadedmaptype = MAPTYPE_QUAKE2;
	//reset the map loading
	ResetMapLoading();
#endif //ME

	script = LoadScriptFile( filename );
	if ( !script ) {
		Log_Print( "couldn't open %s\n", filename );
		return;
	} //end if
	  //white spaces and escape characters inside a string are not allowed
	SetScriptFlags( script, SCFL_NOSTRINGWHITESPACES |
					SCFL_NOSTRINGESCAPECHARS |
					SCFL_PRIMITIVE );

	nummapbrushsides = 0;
	num_entities = 0;

	while ( Q2_ParseMapEntity( script ) )
	{
	}

	ClearBounds( map_mins, map_maxs );
	for ( i = 0 ; i < entities[0].numbrushes ; i++ )
	{
		if ( mapbrushes[i].mins[0] > 4096 ) {
			continue;   // no valid points
		}
		AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs );
		AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs );
	} //end for

	PrintMapInfo();

	//free the script
	FreeScript( script );
//	TestExpandBrushes ();
	//
	Q2_CreateMapTexinfo();
} //end of the function Q2_LoadMapFile

#ifdef ME       //Begin MAP loading from BSP file
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_SetLeafBrushesModelNumbers( int leafnum, int modelnum ) {
	int i, brushnum;
	dleaf_t *leaf;

	leaf = &dleafs[leafnum];
	for ( i = 0; i < leaf->numleafbrushes; i++ )
	{
		brushnum = dleafbrushes[leaf->firstleafbrush + i];
		brushmodelnumbers[brushnum] = modelnum;
		dbrushleafnums[brushnum] = leafnum;
	} //end for
} //end of the function Q2_SetLeafBrushesModelNumbers
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_InitNodeStack( void ) {
	nodestackptr = nodestack;
	nodestacksize = 0;
} //end of the function Q2_InitNodeStack
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_PushNodeStack( int num ) {
	*nodestackptr = num;
	nodestackptr++;
	nodestacksize++;
	//
	if ( nodestackptr >= &nodestack[NODESTACKSIZE] ) {
		Error( "Q2_PushNodeStack: stack overflow\n" );
	} //end if
} //end of the function Q2_PushNodeStack
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
int Q2_PopNodeStack( void ) {
	//if the stack is empty
	if ( nodestackptr <= nodestack ) {
		return -1;
	}
	//decrease stack pointer
	nodestackptr--;
	nodestacksize--;
	//return the top value from the stack
	return *nodestackptr;
} //end of the function Q2_PopNodeStack
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_SetBrushModelNumbers( entity_t *mapent ) {
	int n, pn;
	int leafnum;

	//
	Q2_InitNodeStack();
	//head node (root) of the bsp tree
	n = dmodels[mapent->modelnum].headnode;
	pn = 0;

	do
	{
		//if we are in a leaf (negative node number)
		if ( n < 0 ) {
			//number of the leaf
			leafnum = ( -n ) - 1;
			//set the brush numbers
			Q2_SetLeafBrushesModelNumbers( leafnum, mapent->modelnum );
			//walk back into the tree to find a second child to continue with
			for ( pn = Q2_PopNodeStack(); pn >= 0; n = pn, pn = Q2_PopNodeStack() )
			{
				//if we took the first child at the parent node
				if ( dnodes[pn].children[0] == n ) {
					break;
				}
			} //end for
			  //if the stack wasn't empty (if not processed whole tree)
			if ( pn >= 0 ) {
				//push the parent node again
				Q2_PushNodeStack( pn );
				//we proceed with the second child of the parent node
				n = dnodes[pn].children[1];
			} //end if
		} //end if
		else
		{
			//push the current node onto the stack
			Q2_PushNodeStack( n );
			//walk forward into the tree to the first child
			n = dnodes[n].children[0];
		} //end else
	} while ( pn >= 0 );
} //end of the function Q2_SetBrushModelNumbers
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_BSPBrushToMapBrush( dbrush_t *bspbrush, entity_t *mapent ) {
	mapbrush_t *b;
	int i, k, n;
	side_t *side, *s2;
	int planenum;
	dbrushside_t *bspbrushside;
	dplane_t *bspplane;

	if ( nummapbrushes >= MAX_MAPFILE_BRUSHES ) {
		Error( "nummapbrushes >= MAX_MAPFILE_BRUSHES" );
	}

	b = &mapbrushes[nummapbrushes];
	b->original_sides = &brushsides[nummapbrushsides];
	b->entitynum = mapent - entities;
	b->brushnum = nummapbrushes - mapent->firstbrush;
	b->leafnum = dbrushleafnums[bspbrush - dbrushes];

	for ( n = 0; n < bspbrush->numsides; n++ )
	{
		//pointer to the bsp brush side
		bspbrushside = &dbrushsides[bspbrush->firstside + n];

		if ( nummapbrushsides >= MAX_MAPFILE_BRUSHSIDES ) {
			Error( "MAX_MAPFILE_BRUSHSIDES" );
		} //end if
		  //pointer to the map brush side
		side = &brushsides[nummapbrushsides];
		//if the BSP brush side is textured
		if ( brushsidetextured[bspbrush->firstside + n] ) {
			side->flags |= SFL_TEXTURED;
		} else { side->flags &= ~SFL_TEXTURED;}
		//ME: can get side contents and surf directly from BSP file
		side->contents = bspbrush->contents;
		//if the texinfo is TEXINFO_NODE
		if ( bspbrushside->texinfo < 0 ) {
			side->surf = 0;
		} else { side->surf = texinfo[bspbrushside->texinfo].flags;}

		// translucent objects are automatically classified as detail
		if ( side->surf & ( SURF_TRANS33 | SURF_TRANS66 ) ) {
			side->contents |= CONTENTS_DETAIL;
		}
		if ( side->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
			side->contents |= CONTENTS_DETAIL;
		}
		if ( fulldetail ) {
			side->contents &= ~CONTENTS_DETAIL;
		}
		if ( !( side->contents & ( ( LAST_VISIBLE_CONTENTS - 1 )
								   | CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP | CONTENTS_MIST )  ) ) {
			side->contents |= CONTENTS_SOLID;
		}

		// hints and skips are never detail, and have no content
		if ( side->surf & ( SURF_HINT | SURF_SKIP ) ) {
			side->contents = 0;
			side->surf &= ~CONTENTS_DETAIL;
		}

		//ME: get a plane for this side
		bspplane = &dplanes[bspbrushside->planenum];
		planenum = FindFloatPlane( bspplane->normal, bspplane->dist );
		//
		// see if the plane has been used already
		//
		//ME: this really shouldn't happen!!!
		//ME: otherwise the bsp file is corrupted??
		//ME: still it seems to happen, maybe Johny Boy's
		//ME: brush bevel adding is crappy ?
		for ( k = 0; k < b->numsides; k++ )
		{
			s2 = b->original_sides + k;
//			if (DotProduct (mapplanes[s2->planenum].normal, mapplanes[planenum].normal) > 0.999
//							&& fabs(mapplanes[s2->planenum].dist - mapplanes[planenum].dist) < 0.01 )

			if ( s2->planenum == planenum ) {
				Log_Print( "Entity %i, Brush %i: duplicate plane\n"
						   , b->entitynum, b->brushnum );
				break;
			}
			if ( s2->planenum == ( planenum ^ 1 ) ) {
				Log_Print( "Entity %i, Brush %i: mirrored plane\n"
						   , b->entitynum, b->brushnum );
				break;
			}
		}
		if ( k != b->numsides ) {
			continue;       // duplicated

		}
		//
		// keep this side
		//
		//ME: reset pointer to side, why? hell I dunno (pointer is set above already)
		side = b->original_sides + b->numsides;
		//ME: store the plane number
		side->planenum = planenum;
		//ME: texinfo is already stored when bsp is loaded
		//NOTE: check for TEXINFO_NODE, otherwise crash in Q2_BrushContents
		if ( bspbrushside->texinfo < 0 ) {
			side->texinfo = 0;
		} else { side->texinfo = bspbrushside->texinfo;}

		// save the td off in case there is an origin brush and we
		// have to recalculate the texinfo
		// ME: don't need to recalculate because it's already done
		//     (for non-world entities) in the BSP file
//		side_brushtextures[nummapbrushsides] = td;

		nummapbrushsides++;
		b->numsides++;
	} //end for

	// get the content for the entire brush
	b->contents = bspbrush->contents;
	Q2_BrushContents( b );

	if ( BrushExists( b ) ) {
		c_squattbrushes++;
		b->numsides = 0;
		return;
	} //end if

	//if we're creating AAS
	if ( create_aas ) {
		//create the AAS brushes from this brush, don't add brush bevels
		AAS_CreateMapBrushes( b, mapent, false );
		return;
	} //end if

	// allow detail brushes to be removed
	if ( nodetail && ( b->contents & CONTENTS_DETAIL ) ) {
		b->numsides = 0;
		return;
	} //end if

	// allow water brushes to be removed
	if ( nowater && ( b->contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
		b->numsides = 0;
		return;
	} //end if

	// create windings for sides and bounds for brush
	MakeBrushWindings( b );

	//mark brushes without winding or with a tiny window as bevels
	MarkBrushBevels( b );

	// brushes that will not be visible at all will never be
	// used as bsp splitters
	if ( b->contents & ( CONTENTS_PLAYERCLIP | CONTENTS_MONSTERCLIP ) ) {
		c_clipbrushes++;
		for ( i = 0; i < b->numsides; i++ )
			b->original_sides[i].texinfo = TEXINFO_NODE;
	} //end for

	//
	// origin brushes are removed, but they set
	// the rotation origin for the rest of the brushes
	// in the entity.  After the entire entity is parsed,
	// the planenums and texinfos will be adjusted for
	// the origin brush
	//
	//ME: not needed because the entities in the BSP file already
	//    have an origin set
//	if (b->contents & CONTENTS_ORIGIN)
//	{
//		char	string[32];
//		vec3_t	origin;
//
//		if (num_entities == 1)
//		{
//			Error ("Entity %i, Brush %i: origin brushes not allowed in world"
//				, b->entitynum, b->brushnum);
//			return;
//		}
//
//		VectorAdd (b->mins, b->maxs, origin);
//		VectorScale (origin, 0.5, origin);
//
//		sprintf (string, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
//		SetKeyValue (&entities[b->entitynum], "origin", string);
//
//		VectorCopy (origin, entities[b->entitynum].origin);
//
//		// don't keep this brush
//		b->numsides = 0;
//
//		return;
//	}

	//ME: the bsp brushes already have bevels, so we won't try to
	//    add them again (especially since Johny Boy's bevel adding might
	//    be crappy)
//	AddBrushBevels(b);

	nummapbrushes++;
	mapent->numbrushes++;
} //end of the function Q2_BSPBrushToMapBrush
//===========================================================================
//===========================================================================
void Q2_ParseBSPBrushes( entity_t *mapent ) {
	int i;

	//give all the brushes that belong to this entity the number of the
	//BSP model used by this entity
	Q2_SetBrushModelNumbers( mapent );
	//now parse all the brushes with the correct mapent->modelnum
	for ( i = 0; i < numbrushes; i++ )
	{
		if ( brushmodelnumbers[i] == mapent->modelnum ) {
			Q2_BSPBrushToMapBrush( &dbrushes[i], mapent );
		} //end if
	} //end for
} //end of the function Q2_ParseBSPBrushes
//===========================================================================
//===========================================================================
qboolean Q2_ParseBSPEntity( int entnum ) {
	entity_t    *mapent;
	char *model;
	int startbrush, startsides;

	startbrush = nummapbrushes;
	startsides = nummapbrushsides;

	mapent = &entities[entnum]; //num_entities];
	mapent->firstbrush = nummapbrushes;
	mapent->numbrushes = 0;
	mapent->modelnum = -1;  //-1 = no model

	model = ValueForKey( mapent, "model" );
	if ( model && strlen( model ) ) {
		if ( *model != '*' ) {
			Error( "Q2_ParseBSPEntity: model number without leading *" );
		} //end if
		  //get the model number of this entity (skip the leading *)
		mapent->modelnum = atoi( &model[1] );
	} //end if

	GetVectorForKey( mapent, "origin", mapent->origin );

	//if this is the world entity it has model number zero
	//the world entity has no model key
	if ( !strcmp( "worldspawn", ValueForKey( mapent, "classname" ) ) ) {
		mapent->modelnum = 0;
	} //end if
	  //if the map entity has a BSP model (a modelnum of -1 is used for
	  //entities that aren't using a BSP model)
	if ( mapent->modelnum >= 0 ) {
		//parse the bsp brushes
		Q2_ParseBSPBrushes( mapent );
	} //end if
	  //
	  //the origin of the entity is already taken into account
	  //
	  //func_group entities can't be in the bsp file
	  //
	  //check out the func_areaportal entities
	if ( !strcmp( "func_areaportal", ValueForKey( mapent, "classname" ) ) ) {
		c_areaportals++;
		mapent->areaportalnum = c_areaportals;
		return true;
	} //end if
	return true;
} //end of the function Q2_ParseBSPEntity
//===========================================================================
//
// Parameter:				-
// Returns:					-
// Changes Globals:		-
//===========================================================================
void Q2_LoadMapFromBSP( char *filename, int offset, int length ) {
	int i;

	Log_Print( "-- Q2_LoadMapFromBSP --\n" );
	//loaded map type
	loadedmaptype = MAPTYPE_QUAKE2;

	Log_Print( "Loading map from %s...\n", filename );
	//load the bsp file
	Q2_LoadBSPFile( filename, offset, length );

	//create an index from bsp planes to map planes
	//DPlanes2MapPlanes();
	//clear brush model numbers
	for ( i = 0; i < MAX_MAPFILE_BRUSHES; i++ )
		brushmodelnumbers[i] = -1;

	nummapbrushsides = 0;
	num_entities = 0;

	Q2_ParseEntities();
	//
	for ( i = 0; i < num_entities; i++ )
	{
		Q2_ParseBSPEntity( i );
	} //end for

	//get the map mins and maxs from the world model
	ClearBounds( map_mins, map_maxs );
	for ( i = 0; i < entities[0].numbrushes; i++ )
	{
		if ( mapbrushes[i].mins[0] > 4096 ) {
			continue;   //no valid points
		}
		AddPointToBounds( mapbrushes[i].mins, map_mins, map_maxs );
		AddPointToBounds( mapbrushes[i].maxs, map_mins, map_maxs );
	} //end for

	PrintMapInfo();
	//
	Q2_CreateMapTexinfo();
} //end of the function Q2_LoadMapFromBSP

void Q2_ResetMapLoading( void ) {
	//reset for map loading from bsp
	memset( nodestack, 0, NODESTACKSIZE * sizeof( int ) );
	nodestackptr = NULL;
	nodestacksize = 0;
	memset( brushmodelnumbers, 0, MAX_MAPFILE_BRUSHES * sizeof( int ) );
} //end of the function Q2_ResetMapLoading

//End MAP loading from BSP file
#endif //ME

//====================================================================

/*
================
TestExpandBrushes

Expands all the brush planes and saves a new map out
================
*/
void TestExpandBrushes( void ) {
	FILE    *f;
	side_t  *s;
	int i, j, bn;
	winding_t   *w;
	char    *name = "expanded.map";
	mapbrush_t  *brush;
	vec_t dist;

	Log_Print( "writing %s\n", name );
	f = fopen( name, "wb" );
	if ( !f ) {
		Error( "Can't write %s\n", name );
	}

	fprintf( f, "{\n\"classname\" \"worldspawn\"\n" );

	for ( bn = 0 ; bn < nummapbrushes ; bn++ )
	{
		brush = &mapbrushes[bn];
		fprintf( f, "{\n" );
		for ( i = 0 ; i < brush->numsides ; i++ )
		{
			s = brush->original_sides + i;
			dist = mapplanes[s->planenum].dist;
			for ( j = 0 ; j < 3 ; j++ )
				dist += fabs( 16 * mapplanes[s->planenum].normal[j] );

			w = BaseWindingForPlane( mapplanes[s->planenum].normal, dist );

			fprintf( f,"( %i %i %i ) ", (int)w->p[0][0], (int)w->p[0][1], (int)w->p[0][2] );
			fprintf( f,"( %i %i %i ) ", (int)w->p[1][0], (int)w->p[1][1], (int)w->p[1][2] );
			fprintf( f,"( %i %i %i ) ", (int)w->p[2][0], (int)w->p[2][1], (int)w->p[2][2] );

			fprintf( f, "%s 0 0 0 1 1\n", texinfo[s->texinfo].texture );
			FreeWinding( w );
		}
		fprintf( f, "}\n" );
	}
	fprintf( f, "}\n" );

	fclose( f );

	Error( "can't proceed after expanding brushes" );
} //end of the function TestExpandBrushes

