//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//

#include "vbsp.h"

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

PORTAL FILE GENERATION

Save out name.prt for qvis to read
==============================================================================
*/


#define	PORTALFILE	"PRT1"

FILE	*pf;
int		num_visclusters;				// clusters the player can be in
int		num_visportals;

void WriteFloat (FILE *f, vec_t v)
{
	if ( fabs(v - RoundInt(v)) < 0.001 )
		fprintf (f,"%i ",(int)RoundInt(v));
	else
		fprintf (f,"%f ",v);
}

dportal_t *AddBSPPortal( portal_t *p, Vector& normal, float dist, qboolean backwards )
{
	int			i;
	dportal_t	*bspPortal;
	winding_t	*w;

	bspPortal = dportals + numportals;
	numportals++;

	w = p->winding;

	if ( numportals >= MAX_MAP_PORTALS )
	{
		Error( "Too many portals\n" );
	}

	if ( backwards )
	{
		bspPortal->cluster[0] = p->nodes[1]->cluster;
		bspPortal->cluster[1] = p->nodes[0]->cluster;
	}
	else
	{
		bspPortal->cluster[0] = p->nodes[0]->cluster;
		bspPortal->cluster[1] = p->nodes[1]->cluster;
	}

	bspPortal->planenum = FindFloatPlane( normal, dist );
	bspPortal->numportalverts = w->numpoints;
	bspPortal->firstportalvert = numportalverts;	// bottom of list
	
	// append poly to list
	for ( i = 0; i < w->numpoints; i++ )
	{
		dportalverts[numportalverts] = GetVertexnum( w->p[i] );
		numportalverts++;
		if ( numportalverts >= MAX_MAP_PORTALVERTS )
		{
			Error( "Too many portal verts" );
		}
	}

	return bspPortal;
}


/*
=================
WritePortalFile_r
=================
*/
void WritePortalFile_r (node_t *node)
{
	int			i, s;	
	portal_t	*p;
	winding_t	*w;
	Vector		normal;
	vec_t		dist;

	// decision node
	if (node->planenum != PLANENUM_LEAF)
	{
		WritePortalFile_r (node->children[0]);
		WritePortalFile_r (node->children[1]);
		return;
	}
	
	if (node->contents & CONTENTS_SOLID)
		return;

	for (p = node->portals ; p ; p=p->next[s])
	{
		w = p->winding;
		s = (p->nodes[1] == node);
		if (w && p->nodes[0] == node)
		{
			qboolean backwards;

			// Check visibility through this portal -- if not an open portal, it's a visible, solid face/polygon, so skip it
			if (!Portal_VisFlood (p))
				continue;
		// write out to the file
		
		// sometimes planes get turned around when they are very near
		// the changeover point between different axis.  interpret the
		// plane the same way vis will, and flip the side orders if needed
			// FIXME: is this still relevent?
			WindingPlane (w, normal, &dist);
			if ( DotProduct (p->plane.normal, normal) < 0.99 )
			{	// backwards...
				fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[1]->cluster, p->nodes[0]->cluster);
				backwards = true;
			}
			else
			{
				fprintf (pf,"%i %i %i ",w->numpoints, p->nodes[0]->cluster, p->nodes[1]->cluster);
				backwards = false;
			}
			
			AddBSPPortal( p, normal, dist, backwards );

			for (i=0 ; i<w->numpoints ; i++)
			{
				fprintf (pf,"(");
				WriteFloat (pf, w->p[i][0]);
				WriteFloat (pf, w->p[i][1]);
				WriteFloat (pf, w->p[i][2]);
				fprintf (pf,") ");
			}
			fprintf (pf,"\n");
		}
	}

}

/*
================
FillLeafNumbers_r

All of the leafs under node will have the same cluster
================
*/
void FillLeafNumbers_r (node_t *node, int num)
{
	if (node->planenum == PLANENUM_LEAF)
	{
		if (node->contents & CONTENTS_SOLID)
			node->cluster = -1;
		else
			node->cluster = num;
		return;
	}
	node->cluster = num;
	FillLeafNumbers_r (node->children[0], num);
	FillLeafNumbers_r (node->children[1], num);
}

/*
================
NumberLeafs_r
================
*/
void NumberLeafs_r (node_t *node)
{
	portal_t	*p;

	if (node->planenum != PLANENUM_LEAF)
	{	// decision node
		node->cluster = -99;
		NumberLeafs_r (node->children[0]);
		NumberLeafs_r (node->children[1]);
		return;
	}
	
	// either a leaf or a detail cluster

	if ( node->contents & CONTENTS_SOLID )
	{	// solid block, viewpoint never inside
		node->cluster = -1;
		return;
	}

	FillLeafNumbers_r (node, num_visclusters);
	num_visclusters++;

	// count the portals
	for (p = node->portals ; p ; )
	{
		if (p->nodes[0] == node)		// only write out from first leaf
		{
			if (Portal_VisFlood (p))
				num_visportals++;
			p = p->next[0];
		}
		else
			p = p->next[1];		
	}
}


/*
================
CreateVisPortals_r
================
*/
void CreateVisPortals_r (node_t *node)
{
	// stop as soon as we get to a leaf
	if (node->planenum == PLANENUM_LEAF )
		return;

	MakeNodePortal (node);
	SplitNodePortals (node);

	CreateVisPortals_r (node->children[0]);
	CreateVisPortals_r (node->children[1]);
}

int		clusterleaf;
void SaveClusters_r (node_t *node)
{
	if (node->planenum == PLANENUM_LEAF)
	{
		dleafs[clusterleaf++].cluster = node->cluster;
		return;
	}
	SaveClusters_r (node->children[0]);
	SaveClusters_r (node->children[1]);
}

//-----------------------------------------------------------------------------
// Purpose: Build dclusterportals and dclusters
//-----------------------------------------------------------------------------
void SaveClusterPortalList( void )
{
	int i, j;
	unsigned short *list = dclusterportals;
	dportal_t *p;

	// SaveClusters_r counts this, store it in the model
	numclusters = clusterleaf;
	
	// clear the cluster portal list so we can build it
	numclusterportals = 0;

	// Go through each cluster, find all portals leading into or out of it and add them
	// its cluster list in dclusterportals
	for ( i = 0; i < numclusters; i++ )
	{
		dclusters[i].numportals = 0;
		dclusters[i].firstportal = numclusterportals;

		p = dportals;
		for ( j = 0; j < numportals; j++ )
		{
			if ( p->cluster[0] == i || p->cluster[1] == i )
			{
				dclusters[i].numportals++;
				*list = (unsigned short )j;
				list++;
				numclusterportals++;
			}
			p++;
		}
		if ( numclusterportals > numportals*2 )
			Error( "numclusterportals != numportals * 2" );
	}
}

/*
================
WritePortalFile
================
*/
void WritePortalFile (tree_t *tree)
{
	char	filename[1024];
	node_t *headnode;
	int start = Plat_FloatTime();

	qprintf ("--- WritePortalFile ---\n");

	sprintf (filename, "%s.prt", source);
	Msg ("writing %s...", filename);

	headnode = tree->headnode;

	FreeTreePortals_r (headnode);
	MakeHeadnodePortals (tree);

	CreateVisPortals_r (headnode);

// set the cluster field in every leaf and count the total number of portals
	num_visclusters = 0;
	num_visportals = 0;
	NumberLeafs_r (headnode);
	
// write the file
	pf = fopen (filename, "w");
	if (!pf)
		Error ("Error opening %s", filename);
		
	fprintf (pf, "%s\n", PORTALFILE);
	fprintf (pf, "%i\n", num_visclusters);
	fprintf (pf, "%i\n", num_visportals);

	qprintf ("%5i visclusters\n", num_visclusters);
	qprintf ("%5i visportals\n", num_visportals);

	WritePortalFile_r (headnode);

	fclose (pf);

	// we need to store the clusters out now because ordering
	// issues made us do this after writebsp...
	clusterleaf = 1;
	SaveClusters_r (headnode);

	SaveClusterPortalList();

	Msg("done (%d)\n", (int)(Plat_FloatTime() - start) );
}

