/*
gl_refrag.c - store entity fragments
Copyright (C) 2010 Uncle Mike

This program 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 3 of the License, or
(at your option) any later version.

This program 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.
*/

#include "common.h"
#include "entity_types.h"
#include "studio.h"
#include "world.h" // BOX_ON_PLANE_SIDE
#include "client.h"
#include "xash3d_mathlib.h"

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

			ENTITY FRAGMENT FUNCTIONS

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

#define NUM_EFRAGS_ALLOC 64 // alloc 64 efrags (1-2kb each alloc)

static efrag_t	**lastlink;
static mnode_t	*r_pefragtopnode;
static vec3_t	r_emins, r_emaxs;
static cl_entity_t	*r_addent;
static int cl_efrags_num;
static efrag_t *cl_efrags;

static efrag_t *CL_AllocEfrags( int num )
{
	int i;
	efrag_t *efrags;

	if( !cl.worldmodel )
	{
		Host_Error( "%s: called with NULL world\n", __func__ );
		return NULL;
	}

	if( num == 0 )
		return NULL;

	// set world to be the owner, so it will get automatically cleaned up
	efrags = Mem_Calloc( cl.worldmodel->mempool, sizeof( *efrags ) * num );

	// initialize linked list
	for( i = 0; i < num - 1; i++ )
		efrags[i].entnext = &efrags[i + 1];

	cl_efrags_num += num;

	return efrags;
}

/*
==============
CL_ClearEfrags
==============
*/
void CL_ClearEfrags( void )
{
	cl_efrags_num = 0;
	cl_efrags = NULL;
}

/*
===================
R_SplitEntityOnNode
===================
*/
static void R_SplitEntityOnNode( mnode_t *node )
{
	efrag_t	*ef;
	mleaf_t	*leaf;
	int	sides;

	if( node->contents == CONTENTS_SOLID )
		return;

	// add an efrag if the node is a leaf
	if( node->contents < 0 )
	{
		if( !r_pefragtopnode )
			r_pefragtopnode = node;

		leaf = (mleaf_t *)node;

		// grab an efrag off the free list
		ef = cl_efrags;
		if( !ef )
			ef = CL_AllocEfrags( NUM_EFRAGS_ALLOC );

		cl_efrags = ef->entnext;
		ef->entity = r_addent;

		// add the entity link
		*lastlink = ef;
		lastlink = &ef->entnext;
		ef->entnext = NULL;

		// set the leaf links
		ef->leaf = leaf;
		ef->leafnext = leaf->efrags;
		leaf->efrags = ef;
		return;
	}

	// NODE_MIXED
	sides = BOX_ON_PLANE_SIDE( r_emins, r_emaxs, node->plane );

	if( sides == 3 )
	{
		// split on this plane
		// if this is the first splitter of this bmodel, remember it
		if( !r_pefragtopnode ) r_pefragtopnode = node;
	}

	// recurse down the contacted sides
	if( sides & 1 ) R_SplitEntityOnNode( node->children[0] );
	if( sides & 2 ) R_SplitEntityOnNode( node->children[1] );
}

/*
===========
R_AddEfrags
===========
*/
void R_AddEfrags( cl_entity_t *ent )
{
	matrix3x4	transform;
	vec3_t	outmins, outmaxs;
	int	i;

	if( !ent->model )
		return;

	r_addent = ent;
	lastlink = &ent->efrag;
	r_pefragtopnode = NULL;

	// handle entity rotation for right bbox expanding
	Matrix3x4_CreateFromEntity( transform, ent->angles, vec3_origin, 1.0f );
	Matrix3x4_TransformAABB( transform, ent->model->mins, ent->model->maxs, outmins, outmaxs );

	for( i = 0; i < 3; i++ )
	{
		r_emins[i] = ent->origin[i] + outmins[i];
		r_emaxs[i] = ent->origin[i] + outmaxs[i];
	}

	R_SplitEntityOnNode( cl.worldmodel->nodes );
	ent->topnode = r_pefragtopnode;
}

/*
================
R_StoreEfrags

================
*/
void R_StoreEfrags( efrag_t **ppefrag, int framecount )
{
	efrag_t *pefrag;
	cl_entity_t *pent;
	model_t *clmodel;

	while(( pefrag = *ppefrag ) != NULL )
	{
		pent = pefrag->entity;
		clmodel = pent->model;

		// how this could happen?
		if( unlikely( clmodel->type < mod_brush || clmodel->type > mod_studio ))
			continue;

		if( pent->visframe != framecount )
		{
			if( CL_AddVisibleEntity( pent, ET_FRAGMENTED ))
			{
				// mark that we've recorded this entity for this frame
				pent->curstate.messagenum = cl.parsecount;
				pent->visframe = framecount;
			}
		}

		ppefrag = &pefrag->leafnext;
	}
}
