// An adaptation of the Quake vis utility.

#include <string.h>
#include <stdio.h>

#include "zdbsp.h"
#include "nodebuild.h"
#include "rejectbuilder.h"
#include "templates.h"

bool		FastVis=0;
bool		NoPassageVis=1;
bool		NoSort;

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

/*
=============
GetThreadWork

=============
*/
int	FRejectBuilder::GetThreadWork ()
{
	int	r;
	int	f;

	if (dispatch == workcount)
	{
		return -1;
	}

	if (pacifier)
	{
		if (dispatch >= workcount-1)
		{
			fprintf (stderr, "100%%\n");
		}
		else if (oldcount < 0 || dispatch - oldcount > 200)
		{
			oldcount = dispatch;
			f = 100*dispatch / workcount;
			if (f != oldf)
			{
				oldf = f;
				fprintf (stderr, "% 3d%%\b\b\b\b", f);
			}
		}
	}

	r = dispatch++;

	return r;
}
void FRejectBuilder::RunThreadsOnIndividual (int workcnt, bool showpacifier, void(FRejectBuilder::*func)(int))
{
	int work;

	pacifier = showpacifier;
	workcount = workcnt;
	dispatch = 0;
	oldf = -1;
	oldcount = -1;

	while (-1 != (work = GetThreadWork ()))
	{
		(this->*func) (work);
	}
}

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

/*
=============
SortPortals

Sorts the portals from the least complex, so the later ones can reuse
the earlier information.
=============
*/
int FRejectBuilder::PComp (const void *a, const void *b)
{
	return (*(VPortal **)a)->nummightsee - (*(VPortal **)b)->nummightsee;
}
void FRejectBuilder::SortPortals ()
{
	for (int i = 0; i < numportals; i++)
	{
		sorted_portals[i] = &portals[i];
	}

	if (!NoSort)
	{
		qsort (sorted_portals, numportals, sizeof(sorted_portals[0]), PComp);
	}
}


/*
==============
LeafVectorFromPortalVector
==============
*/
int FRejectBuilder::LeafVectorFromPortalVector (BYTE *portalbits, BYTE *leafbits)
{
	int			i, j, leafnum;
	VPortal		*p;
	int			c_leafs;


	for (i = 0; i < numportals; i++)
	{
		if (portalbits[i>>3] & (1<<(i&7)) )
		{
			p = portals+i;
			leafbits[p->leaf>>3] |= (1<<(p->leaf&7));
			//printf ("pleaf: from %d to %d\n", i, p->leaf);
		}
	}

	for (j = 0; j < portalclusters; j++)
	{
		leafnum = j;
		while (leafs[leafnum].merged >= 0)
		{
			leafnum = leafs[leafnum].merged;
		}
		//if the merged leaf is visible then the original leaf is visible
		if (leafbits[leafnum>>3] & (1<<(leafnum&7)))
		{
			leafbits[j>>3] |= (1<<(j&7));
		}
	}

	c_leafs = CountBits (leafbits, portalclusters);

	return c_leafs;
}


/*
===============
ClusterMerge

Merges the portal visibility for a leaf
===============
*/
void FRejectBuilder::ClusterMerge (int leafnum)
{
	FLeaf		*leaf;
	BYTE		portalvector[MAX_PORTALS/8];
	BYTE		uncompressed[MAX_MAP_LEAFS/8];
	int			i, j;
	int			numvis, mergedleafnum;
	VPortal		*p;
	int			pnum;

	// OR together all the portalvis bits

	mergedleafnum = leafnum;
	while (leafs[mergedleafnum].merged >= 0)
	{
		mergedleafnum = leafs[mergedleafnum].merged;
	}

	memset (portalvector, 0, portalbytes);
	leaf = &leafs[mergedleafnum];
	for (i = 0; i < leaf->numportals; i++)
	{
		p = leaf->portals[i];
		if (p->removed)
			continue;

		if (p->status != STAT_Done)
		{
			throw exception("portal not done");
		}
		for (j = 0; j < portallongs; j++)
		{
			((long *)portalvector)[j] |= ((long *)p->portalvis)[j];
		}
		pnum = p - portals;
		portalvector[pnum>>3] |= 1<<(pnum&7);
	}

	memset (uncompressed, 0, leafbytes);

	// convert portal bits to leaf bits
	uncompressed[mergedleafnum>>3] |= (1<<(mergedleafnum&7));
	numvis = LeafVectorFromPortalVector (portalvector, uncompressed);

	totalvis += numvis;

	//printf ("cluster %4i : %4i visible\n", leafnum, numvis);

	memcpy (visBytes + leafnum*leafbytes, uncompressed, leafbytes);
}


/*
==================
CalcPortalVis
==================
*/
void FRejectBuilder::CalcPortalVis ()
{
#ifdef MREDEBUG
	_printf("%6d portals out of %d", 0, numportals);
	//get rid of the counter
	RunThreadsOnIndividual (numportals, false, PortalFlow);
#else
	RunThreadsOnIndividual (numportals, true, PortalFlow);
#endif

}

/*
==================
CalcPassagePortalVis
==================
*/

void FRejectBuilder::CalcPassagePortalVis ()
{
	PassageMemory();

#ifdef MREDEBUG
	_printf("%6d portals out of %d", 0, numportals);
	RunThreadsOnIndividual (numportals, false, CreatePassages);
	_printf("\n");
	_printf("%6d portals out of %d", 0, numportals);
	RunThreadsOnIndividual (numportals, false, PassagePortalFlow);
	_printf("\n");
#else
	RunThreadsOnIndividual (numportals, true, CreatePassages);
	printf (" Vis 3: ");
	RunThreadsOnIndividual (numportals, true, PassagePortalFlow);
#endif
}

/*
==================
CalcFastVis
==================
*/
void FRejectBuilder::CalcFastVis ()
{
	// fastvis just uses mightsee for a very loose bound
	for (int i = 0; i < numportals; i++)
	{
		portals[i].portalvis = portals[i].portalflood;
		portals[i].status = STAT_Done;
	}
}

/*
==================
CalcVis
==================
*/
void FRejectBuilder::CalcVis ()
{
	printf (" Vis 1: ");
	RunThreadsOnIndividual (numportals, true, BasePortalVis);

//	RunThreadsOnIndividual (numportals, true, BetterPortalVis);

	SortPortals ();

	printf (" Vis 2: ");
	if (FastVis)
		CalcFastVis();
	else if (NoPassageVis)
		CalcPortalVis ();
	else
		CalcPassagePortalVis();
	//
	// assemble the leaf vis lists by oring and compressing the portal lists
	//
	printf ("creating leaf vis...\n");
	for (int i = 0; i < portalclusters; i++)
	{
		ClusterMerge (i);
	}
		
	printf ("Average clusters visible: %i\n", totalvis / portalclusters);
}

/*
=============
Winding_PlanesConcave
=============
*/
bool FRejectBuilder::Winding_PlanesConcave (const FWinding *w1, const FWinding *w2,
											const FLine &line1, const FLine &line2)
{
	// check if one of the points of winding 1 is at the front of the line of winding 2
	if (PointOnSide (w1->points[0], line2) < 0 ||
		PointOnSide (w1->points[1], line2) < 0)
	{
		return true;
	}

	// check if one of the points of winding 2 is at the front of the line of winding 1
	if (PointOnSide (w2->points[0], line1) < 0 ||
		PointOnSide (w2->points[1], line1) < 0)
	{
		return true;
	}

	return false;
}

/*
============
TryMergeLeaves
============
*/
bool FRejectBuilder::TryMergeLeaves (int l1num, int l2num)
{
	int i, j, numportals;
	FLeaf *l1, *l2;
	VPortal *p1, *p2;
	VPortal *portals[MAX_PORTALS_ON_LEAF];

	l1 = &leafs[l1num];
	for (i = 0; i < l1->numportals; i++)
	{
		p1 = l1->portals[i];
		if (p1->leaf == l2num) continue;
		l2 = &leafs[l2num];
		for (j = 0; j < l2->numportals; j++)
		{
			p2 = l2->portals[j];
			if (p2->leaf == l1num) continue;
			//
			if (Winding_PlanesConcave (&p1->winding, &p2->winding, p1->line, p2->line))
				return false;
		}
	}
	l1 = &leafs[l1num];
	l2 = &leafs[l2num];
	numportals = 0;
	//the leaves can be merged now
	for (i = 0; i < l1->numportals; i++)
	{
		p1 = l1->portals[i];
		if (p1->leaf == l2num)
		{
			p1->removed = true;
			continue;
		}
		portals[numportals++] = p1;
	}
	for (j = 0; j < l2->numportals; j++)
	{
		p2 = l2->portals[j];
		if (p2->leaf == l1num)
		{
			p2->removed = true;
			continue;
		}
		portals[numportals++] = p2;
	}
	delete[] l1->portals;
	l1->portals = NULL;
	l1->numportals = 0;
	delete[] l2->portals;
	l2->portals = new VPortal *[numportals];
	for (i = 0; i < numportals; i++)
	{
		l2->portals[i] = portals[i];
	}
	l2->numportals = numportals;
	l1->merged = l2num;
	return true;
}

/*
============
UpdatePortals
============
*/
void FRejectBuilder::UpdatePortals ()
{
	int i;
	VPortal *p;

	for (i = 0; i < numportals; i++)
	{
		p = &portals[i];
		if (!p->removed)
		{
			while (leafs[p->leaf].merged >= 0)
			{
				p->leaf = leafs[p->leaf].merged;
			}
		}
	}
}

/*
============
MergeLeaves

try to merge leaves but don't merge through hint splitters
============
*/
void FRejectBuilder::MergeLeaves ()
{
	int i, j, nummerges, totalnummerges;
	FLeaf *leaf;
	VPortal *p;

	totalnummerges = 0;
	do
	{
		printf (".");
		nummerges = 0;
		for (i = 0; i < portalclusters; i++)
		{
			leaf = &leafs[i];
			//if this leaf is merged already
			if (leaf->merged >= 0)
				continue;
			//
			for (j = 0; j < leaf->numportals; j++)
			{
				p = leaf->portals[j];
				//
				if (p->removed)
					continue;
				//never merge through hint portals
				if (p->hint)
					continue;
				if (TryMergeLeaves(i, p->leaf))
				{
					UpdatePortals();
					nummerges++;
					break;
				}
			}
		}
		totalnummerges += nummerges;
	} while (nummerges);
	printf("\r%6d leaves merged\n", totalnummerges);
}

/*
============
TryMergeWinding
============
*/

FRejectBuilder::FWinding *FRejectBuilder::TryMergeWinding (FWinding *f1, FWinding *f2, const FLine &line)
{
	static FWinding result;
	int i, j;

	//
	// find a common point
	//
	for (i = 0; i < 2; ++i)
	{
		for (j = 0; j < 2; ++j)
		{
			if (f1->points[i].x == f2->points[j].x &&
				f1->points[i].y == f2->points[j].y)
			{
				goto found;
			}
		}
	}

	// no shared point
	return NULL;

found:
	//
	// if the lines are colinear, the point can be removed
	//
	if (PointOnSide (f2->points[0], line) != 0 ||
		PointOnSide (f2->points[1], line) != 0)
	{ // not colinear
		return NULL;
	}

	//
	// build the new segment
	//
	if (i == 0)
	{
		result.points[0] = f2->points[!j];
		result.points[1] = f1->points[1];
	}
	else
	{
		result.points[0] = f1->points[0];
		result.points[1] = f2->points[!j];
	}
	return &result;
}

/*
============
MergeLeafPortals
============
*/
void FRejectBuilder::MergeLeafPortals ()
{
	int i, j, k, nummerges, hintsmerged;
	FLeaf *leaf;
	VPortal *p1, *p2;
	FWinding *w;

	nummerges = 0;
	hintsmerged = 0;
	for (i = 0; i < portalclusters; i++)
	{
		leaf = &leafs[i];
		if (leaf->merged >= 0) continue;
		for (j = 0; j < leaf->numportals; j++)
		{
			p1 = leaf->portals[j];
			if (p1->removed)
				continue;
			for (k = j+1; k < leaf->numportals; k++)
			{
				p2 = leaf->portals[k];
				if (p2->removed)
					continue;
				if (p1->leaf == p2->leaf)
				{
					w = TryMergeWinding (&p1->winding, &p2->winding, p1->line);
					if (w)
					{
						p1->winding = *w;
						if (p1->hint && p2->hint)
							hintsmerged++;
						p1->hint |= p2->hint;
						p2->removed = true;
						nummerges++;
						i--;
						break;
					}
				}
			}
			if (k < leaf->numportals)
				break;
		}
	}
	printf("%6d portals merged\n", nummerges);
	printf("%6d hint portals merged\n", hintsmerged);
}

/*
============
CountActivePortals
============
*/
int FRejectBuilder::CountActivePortals ()
{
	int num, hints, j;
	VPortal *p;

	num = 0;
	hints = 0;
	for (j = 0; j < numportals; j++)
	{
		p = portals + j;
		if (p->removed)
			continue;
		if (p->hint)
			hints++;
		num++;
	}
	printf("%6d of %d active portals\n", num, numportals);
	printf("%6d hint portals\n", hints);
	return num;
}
