/*
   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
 */
// csg4.c

#include "qbsp.h"

extern float subdivide_size;

char source[1024];
char name[1024];

vec_t microvolume = 1.0;
qboolean noprune;
qboolean glview;
qboolean nodetail;
qboolean fulldetail;
qboolean onlyents;
qboolean nomerge;
qboolean nowater;
qboolean nofill;
qboolean nocsg;
qboolean noweld;
qboolean noshare;
qboolean nosubdiv;
qboolean notjunc;
qboolean noopt;
qboolean leaktest;
qboolean verboseentities;

int block_xl = -8, block_xh = 7, block_yl = -8, block_yh = 7;

int entity_num;


node_t      *block_nodes[10][10];


/*
   ============
   BlockTree

   ============
 */
node_t  *BlockTree( int xl, int yl, int xh, int yh ){
	node_t  *node;
	vec3_t normal;
	float dist;
	int mid;

	if ( xl == xh && yl == yh ) {
		node = block_nodes[xl + 5][yl + 5];
		if ( !node ) { // return an empty leaf
			node = AllocNode();
			node->planenum = PLANENUM_LEAF;
			node->contents = 0; //CONTENTS_SOLID;
			return node;
		}
		return node;
	}

	// create a seperator along the largest axis
	node = AllocNode();

	if ( xh - xl > yh - yl ) { // split x axis
		mid = xl + ( xh - xl ) / 2 + 1;
		normal[0] = 1;
		normal[1] = 0;
		normal[2] = 0;
		dist = mid * 1024;
		node->planenum = FindFloatPlane( normal, dist );
		node->children[0] = BlockTree( mid, yl, xh, yh );
		node->children[1] = BlockTree( xl, yl, mid - 1, yh );
	}
	else
	{
		mid = yl + ( yh - yl ) / 2 + 1;
		normal[0] = 0;
		normal[1] = 1;
		normal[2] = 0;
		dist = mid * 1024;
		node->planenum = FindFloatPlane( normal, dist );
		node->children[0] = BlockTree( xl, mid, xh, yh );
		node->children[1] = BlockTree( xl, yl, xh, mid - 1 );
	}

	return node;
}

/*
   ============
   ProcessBlock_Thread

   ============
 */
int brush_start, brush_end;
void ProcessBlock_Thread( int blocknum ){
	int xblock, yblock;
	vec3_t mins, maxs;
	bspbrush_t  *brushes;
	tree_t      *tree;
	node_t      *node;

	yblock = block_yl + blocknum / ( block_xh - block_xl + 1 );
	xblock = block_xl + blocknum % ( block_xh - block_xl + 1 );

	Sys_FPrintf( SYS_VRB, "############### block %2i,%2i ###############\n", xblock, yblock );

	mins[0] = xblock * 1024;
	mins[1] = yblock * 1024;
	mins[2] = -4096;
	maxs[0] = ( xblock + 1 ) * 1024;
	maxs[1] = ( yblock + 1 ) * 1024;
	maxs[2] = 4096;

	// the makelist and chopbrushes could be cached between the passes...
	brushes = MakeBspBrushList( brush_start, brush_end, mins, maxs );
	if ( !brushes ) {
		node = AllocNode();
		node->planenum = PLANENUM_LEAF;
		node->contents = CONTENTS_SOLID;
		block_nodes[xblock + 5][yblock + 5] = node;
		return;
	}

	if ( !nocsg ) {
		brushes = ChopBrushes( brushes );
	}

	tree = BrushBSP( brushes, mins, maxs );

	block_nodes[xblock + 5][yblock + 5] = tree->headnode;
}

/*
   ============
   ProcessWorldModel

   ============
 */
void ProcessWorldModel( void ){
	entity_t    *e;
	tree_t      *tree;
	qboolean leaked;
	qboolean optimize;
	xmlNodePtr polyline, leaknode;
	char level[ 2 ];

	e = &entities[entity_num];

	brush_start = e->firstbrush;
	brush_end = brush_start + e->numbrushes;
	leaked = false;

	//
	// perform per-block operations
	//
	if ( block_xh * 1024 > map_maxs[0] ) {
		block_xh = floor( map_maxs[0] / 1024.0 );
	}
	if ( ( block_xl + 1 ) * 1024 < map_mins[0] ) {
		block_xl = floor( map_mins[0] / 1024.0 );
	}
	if ( block_yh * 1024 > map_maxs[1] ) {
		block_yh = floor( map_maxs[1] / 1024.0 );
	}
	if ( ( block_yl + 1 ) * 1024 < map_mins[1] ) {
		block_yl = floor( map_mins[1] / 1024.0 );
	}

	if ( block_xl < -4 ) {
		block_xl = -4;
	}
	if ( block_yl < -4 ) {
		block_yl = -4;
	}
	if ( block_xh > 3 ) {
		block_xh = 3;
	}
	if ( block_yh > 3 ) {
		block_yh = 3;
	}

	for ( optimize = false ; optimize <= true ; optimize++ )
	{
		Sys_FPrintf( SYS_VRB, "--------------------------------------------\n" );

		RunThreadsOnIndividual( ( block_xh - block_xl + 1 ) * ( block_yh - block_yl + 1 ),
								!verbose, ProcessBlock_Thread );

		//
		// build the division tree
		// oversizing the blocks guarantees that all the boundaries
		// will also get nodes.
		//

		Sys_FPrintf( SYS_VRB, "--------------------------------------------\n" );

		tree = AllocTree();
		tree->headnode = BlockTree( block_xl - 1, block_yl - 1, block_xh + 1, block_yh + 1 );

		tree->mins[0] = ( block_xl ) * 1024;
		tree->mins[1] = ( block_yl ) * 1024;
		tree->mins[2] = map_mins[2] - 8;

		tree->maxs[0] = ( block_xh + 1 ) * 1024;
		tree->maxs[1] = ( block_yh + 1 ) * 1024;
		tree->maxs[2] = map_maxs[2] + 8;

		//
		// perform the global operations
		//
		MakeTreePortals( tree );

		if ( FloodEntities( tree ) ) {
			FillOutside( tree->headnode );
		}
		else
		{

			Sys_FPrintf( SYS_NOXML, "**********************\n" );
			Sys_FPrintf( SYS_NOXML, "******* leaked *******\n" );
			Sys_FPrintf( SYS_NOXML, "**********************\n" );
			polyline = LeakFile( tree );
			leaknode = xmlNewNode( NULL, "message" );
			xmlNodeAddContent( leaknode, "MAP LEAKED\n" );
			xmlAddChild( leaknode, polyline );
			level[0] = (int) '0' + SYS_ERR;
			level[1] = 0;
			xmlSetProp( leaknode, "level", (char*) &level );
			xml_SendNode( leaknode );
			if ( leaktest ) {
				Sys_Printf( "--- MAP LEAKED, ABORTING LEAKTEST ---\n" );
				exit( 0 );
			}
			leaked = true;
/*
            Sys_Printf ("**** leaked ****\n");
            leaked = true;
            LeakFile (tree);
            if (leaktest)
            {
                Sys_Printf ("--- MAP LEAKED ---\n");
                exit (0);
            } */
		}

		MarkVisibleSides( tree, brush_start, brush_end );
		if ( noopt || leaked ) {
			break;
		}
		if ( !optimize ) {
			FreeTree( tree );
		}
	}

	FloodAreas( tree );
	if ( glview ) {
		WriteGLView( tree, source );
	}
	MakeFaces( tree->headnode );
	FixTjuncs( tree->headnode );

	if ( !noprune ) {
		PruneNodes( tree->headnode );
	}

	WriteBSP( tree->headnode );

	if ( !leaked ) {
		WritePortalFile( tree );
	}

	FreeTree( tree );
}

/*
   ============
   ProcessSubModel

   ============
 */
void ProcessSubModel( void ){
	entity_t    *e;
	int start, end;
	tree_t      *tree;
	bspbrush_t  *list;
	vec3_t mins, maxs;

	e = &entities[entity_num];

	start = e->firstbrush;
	end = start + e->numbrushes;

	mins[0] = mins[1] = mins[2] = -4096;
	maxs[0] = maxs[1] = maxs[2] = 4096;
	list = MakeBspBrushList( start, end, mins, maxs );
	if ( !nocsg ) {
		list = ChopBrushes( list );
	}
	tree = BrushBSP( list, mins, maxs );
	MakeTreePortals( tree );
	MarkVisibleSides( tree, start, end );
	MakeFaces( tree->headnode );
	FixTjuncs( tree->headnode );
	WriteBSP( tree->headnode );
	FreeTree( tree );
}

/*
   ============
   ProcessModels
   ============
 */
void ProcessModels( void ){
	BeginBSPFile();

	for ( entity_num = 0 ; entity_num < num_entities ; entity_num++ )
	{
		if ( !entities[entity_num].numbrushes ) {
			continue;
		}

		Sys_FPrintf( SYS_VRB, "############### model %i ###############\n", nummodels );
		BeginModel();
		if ( entity_num == 0 ) {
			ProcessWorldModel();
		}
		else{
			ProcessSubModel();
		}
		EndModel();

		//if (!verboseentities)
		//	verbose = false;	// don't bother printing submodels
	}

	EndBSPFile();
}


/*
   ============
   main
   ============
 */
int BSP_Main(){
	double start, end;
	char path[1024];
	int total_bsp_time;

	Sys_Printf( "\n----- BSP ----\n\n" );


	start = I_FloatTime();

	ThreadSetDefault();
	SetQdirFromPath( mapname );

	strcpy( source, ExpandArg( mapname ) );
	StripExtension( source );

	// delete portal and line files
	sprintf( path, "%s.prt", source );
	remove( path );
	sprintf( path, "%s.lin", source );
	remove( path );

	strcpy( name, ExpandArg( mapname ) );
	DefaultExtension( name, ".map" );    // might be .reg

	//
	// if onlyents, just grab the entites and resave
	//
	if ( onlyents ) {
		char out[1024];

		sprintf( out, "%s.bsp", source );
		LoadBSPFile( out );
		num_entities = 0;

		LoadMapFile( name );
		SetModelNumbers();
		SetLightStyles();

		UnparseEntities();

		WriteBSPFile( out );
	}
	else
	{
		//
		// start from scratch
		//
		LoadMapFile( name );
		SetModelNumbers();
		SetLightStyles();

		ProcessModels();
	}

	end = I_FloatTime();
	total_bsp_time = (int) ( end - start );
	Sys_Printf( "\nBSP Time: " );
	if ( total_bsp_time > 59 ) {
		Sys_Printf( "%d Minutes ", total_bsp_time / 60 );
	}
	Sys_Printf( "%d Seconds\n", total_bsp_time % 60 );


	return 0;
}
