// The old code used the same algorithm as DoomBSP:
//
//   Represent each sector by its bounding box. Then for each pair of
//   sectors, see if any chains of one-sided lines can walk from one
//   side of the convex hull for that pair to the other side.
//
//   It works, but it's far from being perfect. It's quite easy for
//   this algorithm to consider two sectors as being visible from
//   each other when they are really not. But it won't erroneously
//   flag two sectors as obstructed when they're really not, and that's
//   the only thing that really matters when building a REJECT lump.
//
// Because that was next to useless, I scrapped that code and adapted
// Quake's vis utility to work in a 2D world. Since this is basically vis,
// it depends on GL nodes being present to function. As the usefulness
// of a REJECT lump is debatable, I have chosen to not compile this module
// in with ZDBSP. Save yourself some space and run ZDBSP with the -r option.

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

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

bool		MergeVis=1;

FRejectBuilder::FRejectBuilder (FLevel &level)
	: Level (level), testlevel (2), totalvis (0)
{
	LoadPortals ();

	if (MergeVis)
	{
		MergeLeaves ();
		MergeLeafPortals ();
	}

	CountActivePortals ();
	CalcVis ();

	BuildReject ();
}

FRejectBuilder::~FRejectBuilder ()
{
}

BYTE *FRejectBuilder::GetReject ()
{
	WORD *sectormap;
	int i, j; 

	int rejectSize = (Level.NumSectors*Level.NumSectors + 7) / 8;
	BYTE *reject = new BYTE[rejectSize];
	memset (reject, 0xff, rejectSize);

	int pvs_size = (Level.NumGLSubsectors * Level.NumGLSubsectors) + 7 / 8;
	Level.GLPVS = new BYTE[pvs_size];
	Level.GLPVSSize = pvs_size;
	memset (Level.GLPVS, 0, pvs_size);

	sectormap = new WORD[Level.NumGLSubsectors];
	for (i = 0; i < Level.NumGLSubsectors; ++i)
	{
		const MapSegGLEx *seg = &Level.GLSegs[Level.GLSubsectors[i].firstline];
		sectormap[i] = Level.Sides[Level.Lines[seg->linedef].sidenum[seg->side]].sector;
	}

	for (i = 0; i < Level.NumGLSubsectors; ++i)
	{
		int rowpvs = i*Level.NumGLSubsectors;
		int rowrej = sectormap[i]*Level.NumSectors;
		BYTE *bytes = visBytes + i*leafbytes;
		for (j = 0; j < Level.NumGLSubsectors; ++j)
		{
			if (bytes[j>>3] & (1<<(j&7)))
			{
				int mark = rowpvs + j;
				Level.GLPVS[mark>>3] |= 1<<(mark&7);

				mark = rowrej + sectormap[j];
				reject[mark>>3] &= ~(1<<(mark&7));
			}
		}
	}

	return reject;
}

void FRejectBuilder::BuildReject ()
{
}

inline const WideVertex *FRejectBuilder::GetVertex (WORD vertnum)
{
	return &Level.Vertices[vertnum];
}

FRejectBuilder::FLeaf::FLeaf ()
	: numportals (0), merged (-1), portals (NULL)
{
}

FRejectBuilder::FLeaf::~FLeaf ()
{
	if (portals != NULL)
	{
		delete[] portals;
	}
}

int FRejectBuilder::PointOnSide (const FPoint &point, const FLine &line)
{
	return FNodeBuilder::PointOnSide (point.x, point.y, line.x, line.y, line.dx, line.dy);
}

void FRejectBuilder::LoadPortals ()
{
	WORD		*segleaf;
	int			i, j, k, max;
	VPortal		*p;
	FLeaf		*l;
	FWinding	*w;
	
	portalclusters = Level.NumGLSubsectors;

	for (numportals = 0, i = 0; i < Level.NumGLSegs; ++i)
	{
		if (Level.GLSegs[i].partner != DWORD_MAX)
		{
			++numportals;
		}
	}

	// these counts should take advantage of 64 bit systems automatically
	leafbytes = ((portalclusters+63)&~63)>>3;
	leaflongs = leafbytes/sizeof(long);

	portalbytes = ((numportals+63)&~63)>>3;
	portallongs = portalbytes/sizeof(long);

	portals = new VPortal[numportals];
	memset (portals, 0, numportals*sizeof(VPortal));
	
	leafs = new FLeaf[portalclusters];

	numVisBytes = portalclusters*leafbytes;
	visBytes = new BYTE[numVisBytes];

	segleaf = new WORD[Level.NumGLSegs];
	for (i = 0; i < Level.NumGLSubsectors; ++i)
	{
		j = Level.GLSubsectors[i].firstline;
		max = j + Level.GLSubsectors[i].numlines;

		for (; j < max; ++j)
		{
			segleaf[j] = i;
		}
	}

	p = portals;
	l = leafs;
	for (i = 0; i < Level.NumGLSubsectors; ++i, ++l)
	{
		j = Level.GLSubsectors[i].firstline;
		max = j + Level.GLSubsectors[i].numlines;

		// Count portals in this leaf
		for (; j < max; ++j)
		{
			if (Level.GLSegs[j].partner != DWORD_MAX)
			{
				++l->numportals;
			}
		}

		if (l->numportals == 0)
		{
			continue;
		}

		l->portals = new VPortal *[l->numportals];

		for (k = 0, j = Level.GLSubsectors[i].firstline; j < max; ++j)
		{
			const MapSegGLEx *seg = &Level.GLSegs[j];

			if (seg->partner == DWORD_MAX)
			{
				continue;
			}

			// create portal from seg
			l->portals[k++] = p;
			
			w = &p->winding;
			w->points[0] = GetVertex (seg->v1);
			w->points[1] = GetVertex (seg->v2);

			p->hint = seg->linedef != NO_INDEX;
			p->line.x = w->points[1].x;
			p->line.y = w->points[1].y;
			p->line.dx = w->points[0].x - p->line.x;
			p->line.dy = w->points[0].y - p->line.y;
			p->leaf = segleaf[seg->partner];

			p++;
		}
	}

	delete[] segleaf;
}
