//////////////////////////////////////////////////////////////////////////
//	floating, bobbing crap
//
//	FIXME:	move to physics DLL.  Make floating a movetype
//////////////////////////////////////////////////////////////////////////

#include	"world.h"
#include	"floater.h"

///////////////////////////////////////////////////////////////////////////////
//	exports
///////////////////////////////////////////////////////////////////////////////

DllExport	void	func_floater (userEntity_t *self);

///////////////////////////////////////////////////////////////////////////////
//	typedefs
///////////////////////////////////////////////////////////////////////////////

typedef void	(submerged_t) (userEntity_t *self, int &contents, float &percent_submerged);

typedef	struct	floaterHook_s
{
	float	obj_density;
	float	fluid_density;
	float	fluid_viscosity;
	float	ratio;

	//	testing these right now
	float	dissipate;
	float	velocity_cap;

	int		contents;

	submerged_t	*submerged_func;
} floaterHook_t;

///////////////////////////////////////////////////////////////////////////////
//	globals
///////////////////////////////////////////////////////////////////////////////

static	int	floater_count;

//////////////////////////////////////////////////
//	float_percent_submerged
//
//	Quake 1 hack to find percent of an object that is submerged
//	uses multiple calls to PointContents
//
//	Q2FIXE:	change to a traceline instead of multiple pointcontents checks
//////////////////////////////////////////////////

void	float_percent_submerged (userEntity_t *self, int &contents, float &percent_submerged)
{
	vec3_t	top, bottom, move;
	float	bbox_height, water_height;
//	int		contents = CONTENTS_EMPTY;

	com->CopyVector (top, self->s.origin);
	top [2] += self->s.maxs [2];

	com->CopyVector (bottom, self->s.origin);
	bottom [2] += self->s.mins [2];

	bbox_height = (self->s.maxs [2] - self->s.mins [2]);

//	gstate->UnlinkEntity (self);

	tr = gstate->trace_q2 (top, NULL, NULL, bottom, NULL, MASK_WATER);
	if (tr.allsolid && tr.startsolid)
		water_height = bbox_height;
	else
		water_height = bbox_height - (bbox_height * tr.fraction);

//	gstate->LinkEntity (self);

/*
	water_height = bbox_height;

	while (water_height >= 0)
	{
		com->CopyVector (move, bottom);
		move [2] += water_height;

		contents = gstate->PointContents (move);

		if (contents & (CONTENTS_WATER | CONTENTS_SLIME | CONTENTS_LAVA))
			break;

		water_height--;
	}
*/
	if (water_height <= 0)
		percent_submerged = 0.0;
	else if (water_height >= bbox_height)
		percent_submerged = 1.0;
	else
		percent_submerged = 1 / (bbox_height / water_height);

//	gstate->Con_Printf ("%s water_height = %.2f bb_h = %.2f p_s = %.2f\n", self->netname, water_height, bbox_height, percent_submerged);
}

//////////////////////////////////////////////////////////////////
//	float_float
//
//	use as a prethink
//////////////////////////////////////////////////////////////////

void float_float (userEntity_t *self)
{
	floaterHook_t	*bhook = (floaterHook_t *) self->ptr1;
	float		percent_submerged, g, v_vel;
	int			contents;

	//	find percent submerged
	bhook->submerged_func (self, contents, percent_submerged);

	//	recalc ratio if contents changed
	if (contents != bhook->contents)
	{
		if (contents & CONTENTS_WATER)
			bhook->fluid_density = 1.0;
		else if (contents & CONTENTS_LAVA)
			bhook->fluid_density = 2.0;
		else if (contents & CONTENTS_SLIME)
			bhook->fluid_density = 1.2;
	
		bhook->ratio = bhook->fluid_density / bhook->obj_density;
		bhook->contents = contents;
	}

	g = gstate->GetCvar ("sv_gravity");
	if (self->gravity != 0.0)
		g *= self->gravity;

	if (percent_submerged > 0.0)
	{
		self->velocity [2] += g * (gstate->frametime * (bhook->ratio * percent_submerged - 1.0));
//		gstate->Con_Printf ("%s = %.2f\n", self->netname, self->velocity [2]);
	}
	else
		self->velocity [2] += -g * gstate->frametime;

	if (fabs (self->velocity [2]) > bhook->velocity_cap)
		self->velocity [2] *= bhook->dissipate;

	self->flags -= (self->flags & FL_ONGROUND);
}

//////////////////////////////////////////////////////////////////
//	float_init
//
//////////////////////////////////////////////////////////////////

int	float_init (userEntity_t *self, float obj_mass, float obj_volume, float fluid_density, float dissipate, float velocity_cap, void *ptr)
{
	floaterHook_t	*bhook;

	//	already has a ptr1
	if (self->ptr1)
		return	FALSE;

	bhook = (floaterHook_t *) com->malloc (sizeof (floaterHook_t), MEM_MALLOC);
	self->ptr1 = bhook;

	self->movetype = MOVETYPE_FLY;	// don't let engine calc gravity effect, float_float does that

	bhook->obj_density = obj_mass / obj_volume;
	bhook->fluid_density = fluid_density;
//	bhook->fluid_viscosity = fluid_viscosity;
	bhook->ratio = fluid_density / bhook->obj_density;

	bhook->dissipate = dissipate;
	bhook->velocity_cap = velocity_cap;

	if (ptr)
		bhook->submerged_func = (submerged_t *) ptr;
	else
		bhook->submerged_func = float_percent_submerged;

	self->prethink = float_float;

	return	TRUE;
}

//////////////////////////////////////////////////////////////////
//	float_pain
//
//	called for pain function if floater has FLOATER_TAKEDAMAGE spawnflag set
//
//	allows floater to be moved by explosion damage without
//	ever being destroyed
//////////////////////////////////////////////////////////////////

void	float_pain (userEntity_t *self, userEntity_t *other, float kick, int damage)
{
	self->health = 65535;
}

//////////////////////////////////////////////////////////////////
//	float_die
//
//	called for die function if floater has FLOATER_TAKEDAMAGE spawnflag set
//
//	allows floater to be moved by explosion damage without
//	ever being destroyed
//////////////////////////////////////////////////////////////////

void	float_die (userEntity_t *self, userEntity_t *inflictor, userEntity_t *attacker, int damage, vec3_t point)
{
	self->health = 65535;
}

/////////////////////////////////////////////////
//	func_floater
/////////////////////////////////////////////////

void	func_floater (userEntity_t *self)
{
	int			i;
	floaterHook_t	*hook;
	float		dissipate = 0.99, velocity_cap = 32.0;
	float		fluid_density = 1.0, fluid_viscosity = 1.0;
	float		object_mass = 1.0, object_volume = 4.0;

	floater_count++;

	self->modelName = "models/floater/floater.md2";

	for (i = 0; self->epair [i].key; i++)
	{
		if (stricmp (self->epair [i].key, "model") == 0)
			self->modelName = self->epair [i].value;
		else if (stricmp (self->epair [i].key, "object_mass") == 0)
			object_mass = atof (self->epair [i].value);
		else if (stricmp (self->epair [i].key, "object_volume") == 0)
			object_volume = atof (self->epair [i].value);
		else if (stricmp (self->epair [i].key, "fluid_density") == 0)
			fluid_density = atof (self->epair [i].value);
		else if (stricmp (self->epair [i].key, "dissipate") == 0)
			dissipate = atof (self->epair [i].value);
		else if (stricmp (self->epair [i].key, "velocity_cap") == 0)
			velocity_cap = atof (self->epair [i].value);
	}
	
//	self->netname = (char *) com->malloc (256, MEM_HUNK);
//	sprintf (self->netname, "floater %i", floater_count);

	self->solid = SOLID_BBOX;
	self->movetype = MOVETYPE_FLY;
	self->className = "func_floater";

	self->flags |= FL_PUSHABLE;
	self->mass = object_mass;
	self->takedamage = DAMAGE_YES;
	self->pain = float_pain;
	self->die = float_die;

	self->s.modelindex = gstate->ModelIndex (self->modelName);

	if (!stricmp  (self->modelName, "models/bigbox.mdl"))
		gstate->SetSize (self, -32.0, -32.0, -32.0, 32.0, 32.0, 32.0);
	else 
		gstate->SetSize (self, -16.0, -16.0, -16.0, 16.0, 16.0, 16.0);
	gstate->SetOrigin (self, self->s.origin [0], self->s.origin [1], self->s.origin [2]);

	com->SetVector (self->avelocity, rnd () * 60 - 30, rnd () * 120 - 60, 0);

	float_init (self, object_mass, object_volume, fluid_density, dissipate, velocity_cap, NULL);
}
