///////////////////////////////////////////////////////////////////////////////
// Content Friction Code
//
///////////////////////////////////////////////////////////////////////////////
//#include <windows.h>
#include <stdio.h>
#include <math.h>

#include	"p_user.h"
#include	"hooks.h"

#include	"common.h"
#include	"com_globals.h"

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

extern	serverState_t	*gstate;
extern	Vector			forward, right, up;

///////////////////////////////////////////////////////////////////////////////
//
//
///////////////////////////////////////////////////////////////////////////////

void	friction_apply (userEntity_t *self, frictionHook_t *fhook)
{
	Vector			dir, ang;
	float			pc, wobble_speed, wander_speed;
	float			invs_mass;

	pc = gstate->PointContents (self->s.origin);

	////////////////////////////////////////////////////////////////////
	// object is not in a liquid
	////////////////////////////////////////////////////////////////////

	if (pc != CONTENTS_LAVA && pc != CONTENTS_SLIME && pc != CONTENTS_WATER)
	{
		// not currently in a liquid
		if (fhook->last_content == CONTENTS_LAVA || fhook->last_content == CONTENTS_SLIME ||
			fhook->last_content == CONTENTS_WATER || qv_len (self->velocity) < fhook->max_vel)
		{
			if (fhook->last_content == CONTENTS_LAVA || fhook->last_content == CONTENTS_SLIME ||
				fhook->last_content == CONTENTS_WATER)
			{
				// just left a liquid, so play exit sound
				gstate->sound (self, CHAN_AUTO, fhook->exit_sound, 255, ATTN_NORM);
			}

			// just left a liquid, or velocity < max_vel
			// if this is a self-propelled MOVETYPE_FLYMISSILE 
			// then increase its velocity until it is at max_vel
			if (self->movetype == MOVETYPE_FLYMISSILE)
			{
				dir = Vector (self->velocity) * 1.25 * fhook->mass_scale;

				// cap velocity if necessary
				if (dir.vlen () > fhook->max_vel)
					dir = dir * (fhook->max_vel / dir.vlen ());

				dir.set (self->velocity);
			}
			
			// make sure gravity goes back to normal
			self->gravity = 1.0;
		}

		fhook->last_content = pc;

		return;
	}

	////////////////////////////////////////////////////////////////////
	// object is in a liquid
	////////////////////////////////////////////////////////////////////
	
	if (fhook->last_content != pc && fhook->last_content == CONTENTS_EMPTY)
	{
		// just hit liquid, so slow down
		
		// save position where water was first encountered
		// we will use this as a point to drift around
		fhook->liquid_pos = Vector (self->s.origin);
		
		// scale velocity down by mass_scale
		dir = Vector (self->velocity) * 0.5 * fhook->mass_scale;
		dir.set (self->velocity);
		
		// set change_time to current time
		fhook->change_time = gstate->time;
		
		if (fhook->flags & FR_SPIRAL)
		{
			if (rnd () < 0.5)
				fhook->spiral_dir = -1.0;
			else
				fhook->spiral_dir = 1.0;
		}

		// decrease effect of gravity
		self->gravity = 0.5 * fhook->mass_scale;		
		
		gstate->Con_Printf ("gravity decreased to %f\n", self->gravity);

		// play appropriate content hit sound
		gstate->sound (self, CHAN_AUTO, fhook->enter_sound, 255, ATTN_NORM);
	}
	else if ((self->movetype == MOVETYPE_BOUNCE) || (self->movetype == MOVETYPE_TOSS))
	{
		// this is an object affected by gravity which has already entered
		// a liquid on a previous frame
		if (fhook->change_time <= gstate->time)
		{
			invs_mass = 2.0 - fhook->mass_scale;

			// change the z of liquid_pos to 
			// approximately the objects current z
			if (fhook->flags & FR_SEMIFLOAT)
				fhook->liquid_pos.z = self->s.origin [2] + (rnd () - 0.2) * (rnd () * 224.0 + 32.0);
			else
				fhook->liquid_pos.z = self->s.origin [2];// + (rnd () - 0.2) * (rnd () * 256.0 + 32.0);

			// find the vector from current position to liquid_pos
			dir = Vector (fhook->liquid_pos) - Vector (self->s.origin);
			dir.Normalize ();

			com.VecToAngles (ang, dir);
			ang.AngleVectors (forward, right, up);

			if (fhook->flags & FR_SPIRAL)
			{
				right = right * fhook->spiral_dir;
				
				// less lateral movement for greater masses
				wobble_speed = 200 * invs_mass;

				dir = right * wobble_speed;
				if (fhook->flags & FR_SEMIFLOAT)
					dir = dir + forward * 200 * invs_mass;

				fhook->change_time = gstate->time + 0.05 * fhook->mass_scale * 2.0;
			}
			else
			{
				// randomly choose left or right
				if (rnd () < 0.5)
					right = right * -1.0;

				// randomly choose towards or away
				if (rnd () < 0.5)
					forward = forward * -1.0;

				wobble_speed = (60 + rnd () * 100) * fhook->mass_scale;
				wander_speed = (60 + rnd () * 100) * fhook->mass_scale;

				dir = right * wobble_speed + forward * wander_speed;

				// choose a random time to effect the next change
				fhook->change_time = gstate->time + rnd () * invs_mass + 0.1;
			}

			dir.set (self->velocity);

			// change angular velocity to make drift look more realistic
			com.SetVector (self->avelocity, fhook->size_scale * (rnd () - 0.5) * 700.0, fhook->size_scale * (rnd () - 0.5) * 700.0, fhook->size_scale * (rnd () - 0.5) * 700.0);
		}
	}

	fhook->last_content = pc;
}

//////////////////////////////
// friction_init
//////////////////////////////

frictionHook_t	*friction_init (userEntity_t *self, frictionHook_t *fhook)
{
	fhook = (frictionHook_t *) com.malloc_hook (sizeof (frictionHook_t), MEM_MALLOC);

	// get current content
	fhook->last_content = gstate->PointContents (self->s.origin);

	fhook->change_time = gstate->time;

	fhook->mass_scale = 1.0;
	fhook->size_scale = 1.0;

	// current position
//	fhook->last_pos = Vector (self->s.origin);
	
	if (fhook->last_content != CONTENTS_EMPTY)
		fhook->liquid_pos = Vector (self->s.origin);
	else
		fhook->liquid_pos = Vector (0, 0, 0);

	fhook->max_vel = 1000.0;
	fhook->flags = 0;

	fhook->enter_sound = "shared/bloop4.wav";
	fhook->exit_sound = "shared/bloop4.wav";

	return	fhook;
}

//////////////////////////////
// friction_set_sounds
//////////////////////////////

void	friction_set_sounds (userEntity_t *self, frictionHook_t *fhook, char *enter, char *exit)
{
	fhook->enter_sound = enter;
	fhook->exit_sound = exit;
}

//////////////////////////////
// friction_set_physics
//////////////////////////////

void	friction_set_physics (userEntity_t *self, frictionHook_t *fhook, float size, float mass, float max_vel, int flags)
{
	if (mass > 2.0)
		mass = 2.0;
	if (size > 2.0)
		size = 2.0;

	fhook->mass_scale = mass;
	fhook->size_scale = size;
	fhook->max_vel = max_vel;
	fhook->flags = flags;
}

//////////////////////////////
//	friction_remove
//////////////////////////////

void	friction_remove (frictionHook_t *fhook)
{
	if (fhook)
		com.free (fhook);
}

///////////////////////////////////////////////////////////////////////////////
//	com_InitFriction
//
//	set up pointers to com_friction.cpp functions
///////////////////////////////////////////////////////////////////////////////

void	com_InitFriction (void)
{
	com.friction_apply = friction_apply;
	com.friction_init = friction_init;
	com.friction_set_sounds = friction_set_sounds;
	com.friction_set_physics = friction_set_physics;
	com.friction_remove = friction_remove;
}
