/*
   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
 */
// trace.c
/*
   #include "cmdlib.h"
   #include "mathlib.h"
   #include "bspfile.h"
 */

#include "qrad.h"

#define ON_EPSILON  0.1

typedef struct tnode_s
{
	int type;
	vec3_t normal;
	float dist;
	int children[2];
	int pad;
} tnode_t;

tnode_t     *tnodes, *tnode_p;

/*
   ==============
   MakeTnode

   Converts the disk node structure into the efficient tracing structure
   ==============
 */
void MakeTnode( int nodenum ){
	tnode_t         *t;
	dplane_t        *plane;
	int i;
	dnode_t         *node;

	t = tnode_p++;

	node = dnodes + nodenum;
	plane = dplanes + node->planenum;

	t->type = plane->type;
	VectorCopy( plane->normal, t->normal );
	t->dist = plane->dist;

	for ( i = 0 ; i < 2 ; i++ )
	{
		if ( node->children[i] < 0 ) {
			t->children[i] = ( dleafs[-node->children[i] - 1].contents & CONTENTS_SOLID ) | ( 1 << 31 );
		}
		else
		{
			t->children[i] = tnode_p - tnodes;
			MakeTnode( node->children[i] );
		}
	}

}


/*
   =============
   MakeTnodes

   Loads the node structure out of a .bsp file to be used for light occlusion
   =============
 */
void MakeTnodes( dmodel_t *bm ){
	// 32 byte align the structs
	tnodes = malloc( ( numnodes + 1 ) * sizeof( tnode_t ) );
	tnodes = (tnode_t *)( ( (int)tnodes + 31 ) & ~31 );
	tnode_p = tnodes;

	MakeTnode( 0 );
}


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


int TestLine_r( int node, vec3_t start, vec3_t stop ){
	tnode_t *tnode;
	float front, back;
	vec3_t mid;
	float frac;
	int side;
	int r;

	if ( node & ( 1 << 31 ) ) {
		return node & ~( 1 << 31 ); // leaf node

	}
	tnode = &tnodes[node];
	switch ( tnode->type )
	{
	case PLANE_X:
		front = start[0] - tnode->dist;
		back = stop[0] - tnode->dist;
		break;
	case PLANE_Y:
		front = start[1] - tnode->dist;
		back = stop[1] - tnode->dist;
		break;
	case PLANE_Z:
		front = start[2] - tnode->dist;
		back = stop[2] - tnode->dist;
		break;
	default:
		front = ( start[0] * tnode->normal[0] + start[1] * tnode->normal[1] + start[2] * tnode->normal[2] ) - tnode->dist;
		back = ( stop[0] * tnode->normal[0] + stop[1] * tnode->normal[1] + stop[2] * tnode->normal[2] ) - tnode->dist;
		break;
	}

	if ( front >= -ON_EPSILON && back >= -ON_EPSILON ) {
		return TestLine_r( tnode->children[0], start, stop );
	}

	if ( front < ON_EPSILON && back < ON_EPSILON ) {
		return TestLine_r( tnode->children[1], start, stop );
	}

	side = front < 0;

	frac = front / ( front - back );

	mid[0] = start[0] + ( stop[0] - start[0] ) * frac;
	mid[1] = start[1] + ( stop[1] - start[1] ) * frac;
	mid[2] = start[2] + ( stop[2] - start[2] ) * frac;

	r = TestLine_r( tnode->children[side], start, mid );
	if ( r ) {
		return r;
	}
	return TestLine_r( tnode->children[!side], mid, stop );
}

int TestLine( vec3_t start, vec3_t stop ){
	return TestLine_r( 0, start, stop );
}

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

   LINE TRACING

   The major lighting operation is a point to point visibility test, performed
   by recursive subdivision of the line by the BSP tree.

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

typedef struct
{
	vec3_t backpt;
	int side;
	int node;
} tracestack_t;


/*
   ==============
   TestLine
   ==============
 */
qboolean _TestLine( vec3_t start, vec3_t stop ){
	int node;
	float front, back;
	tracestack_t    *tstack_p;
	int side;
	float frontx,fronty, frontz, backx, backy, backz;
	tracestack_t tracestack[64];
	tnode_t         *tnode;

	frontx = start[0];
	fronty = start[1];
	frontz = start[2];
	backx = stop[0];
	backy = stop[1];
	backz = stop[2];

	tstack_p = tracestack;
	node = 0;

	while ( 1 )
	{
		if ( node == CONTENTS_SOLID ) {
#if 0
			float d1, d2, d3;

			d1 = backx - frontx;
			d2 = backy - fronty;
			d3 = backz - frontz;

			if ( d1 * d1 + d2 * d2 + d3 * d3 > 1 )
#endif
			return false;       // DONE!
		}

		while ( node < 0 )
		{
			// pop up the stack for a back side
			tstack_p--;
			if ( tstack_p < tracestack ) {
				return true;
			}
			node = tstack_p->node;

			// set the hit point for this plane

			frontx = backx;
			fronty = backy;
			frontz = backz;

			// go down the back side

			backx = tstack_p->backpt[0];
			backy = tstack_p->backpt[1];
			backz = tstack_p->backpt[2];

			node = tnodes[tstack_p->node].children[!tstack_p->side];
		}

		tnode = &tnodes[node];

		switch ( tnode->type )
		{
		case PLANE_X:
			front = frontx - tnode->dist;
			back = backx - tnode->dist;
			break;
		case PLANE_Y:
			front = fronty - tnode->dist;
			back = backy - tnode->dist;
			break;
		case PLANE_Z:
			front = frontz - tnode->dist;
			back = backz - tnode->dist;
			break;
		default:
			front = ( frontx * tnode->normal[0] + fronty * tnode->normal[1] + frontz * tnode->normal[2] ) - tnode->dist;
			back = ( backx * tnode->normal[0] + backy * tnode->normal[1] + backz * tnode->normal[2] ) - tnode->dist;
			break;
		}

		if ( front > -ON_EPSILON && back > -ON_EPSILON ) {
//		if (front > 0 && back > 0)
			node = tnode->children[0];
			continue;
		}

		if ( front < ON_EPSILON && back < ON_EPSILON ) {
//		if (front <= 0 && back <= 0)
			node = tnode->children[1];
			continue;
		}

		side = front < 0;

		front = front / ( front - back );

		tstack_p->node = node;
		tstack_p->side = side;
		tstack_p->backpt[0] = backx;
		tstack_p->backpt[1] = backy;
		tstack_p->backpt[2] = backz;

		tstack_p++;

		backx = frontx + front * ( backx - frontx );
		backy = fronty + front * ( backy - fronty );
		backz = frontz + front * ( backz - frontz );

		node = tnode->children[side];
	}
}
