//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//=============================================================================//
#include "cbase.h"
#include "physics.h"
#include "te_effect_dispatch.h"

// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"

static int BestAxisMatchingNormal( matrix3x4_t &matrix, const Vector &normal )
{
	float bestDot = -1;
	int best = 0;
	for ( int i = 0; i < 3; i++ )
	{
		Vector tmp;
		MatrixGetColumn( matrix, i, tmp );
		float dot = fabs(DotProduct( tmp, normal ));
		if ( dot > bestDot )
		{
			bestDot = dot;
			best = i;
		}
	}

	return best;
}

void PhysicsSplash( IPhysicsFluidController *pFluid, IPhysicsObject *pObject, CBaseEntity *pEntity )
{
	Vector normal;
	float dist;
	pFluid->GetSurfacePlane( &normal, &dist );

	matrix3x4_t &matrix = pEntity->EntityToWorldTransform();
	
	// Find the local axis that best matches the water surface normal
	int bestAxis = BestAxisMatchingNormal( matrix, normal );

	Vector tangent, binormal;
	MatrixGetColumn( matrix, (bestAxis+1)%3, tangent );
	binormal = CrossProduct( normal, tangent );
	VectorNormalize( binormal );
	tangent = CrossProduct( binormal, normal );
	VectorNormalize( tangent );

	// Now we have a basis tangent to the surface that matches the object's local orientation as well as possible
	// compute an OBB using this basis
	
	// Get object extents in basis
	Vector tanPts[2], binPts[2];
	tanPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -tangent );
	tanPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), tangent );
	binPts[0] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), -binormal );
	binPts[1] = physcollision->CollideGetExtent( pObject->GetCollide(), pEntity->GetAbsOrigin(), pEntity->GetAbsAngles(), binormal );

	// now compute the centered bbox
	float mins[2], maxs[2], center[2], extents[2];
	mins[0] = DotProduct( tanPts[0], tangent );
	maxs[0] = DotProduct( tanPts[1], tangent );

	mins[1] = DotProduct( binPts[0], binormal );
	maxs[1] = DotProduct( binPts[1], binormal );

	center[0] = 0.5 * (mins[0] + maxs[0]);
	center[1] = 0.5 * (mins[1] + maxs[1]);

	extents[0] = maxs[0] - center[0];
	extents[1] = maxs[1] - center[1];

	Vector centerPoint = center[0] * tangent + center[1] * binormal + dist * normal;

	Vector axes[2];
	axes[0] = (maxs[0] - center[0]) * tangent;
	axes[1] = (maxs[1] - center[1]) * binormal;

	// visualize OBB hit
	/*
	Vector corner1 = centerPoint - axes[0] - axes[1];
	Vector corner2 = centerPoint + axes[0] - axes[1];
	Vector corner3 = centerPoint + axes[0] + axes[1];
	Vector corner4 = centerPoint - axes[0] + axes[1];
	NDebugOverlay::Line( corner1, corner2, 0, 0, 255, false, 10 );
	NDebugOverlay::Line( corner2, corner3, 0, 0, 255, false, 10 );
	NDebugOverlay::Line( corner3, corner4, 0, 0, 255, false, 10 );
	NDebugOverlay::Line( corner4, corner1, 0, 0, 255, false, 10 );
	*/

	Vector	corner[4];

	corner[0] = centerPoint - axes[0] - axes[1];
	corner[1] = centerPoint + axes[0] - axes[1];
	corner[2] = centerPoint + axes[0] + axes[1];
	corner[3] = centerPoint - axes[0] + axes[1];

	CEffectData	data;

	if ( pObject->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL )
	{
		/*
		data.m_vOrigin = centerPoint;
		data.m_vNormal = normal;
		VectorAngles( normal, data.m_vAngles );
		data.m_flScale = random->RandomFloat( 8, 10 );

		DispatchEffect( "watersplash", data );
		
		int		splashes = 4;
		Vector	point;

		for ( int i = 0; i < splashes; i++ )
		{
			point = RandomVector( -32.0f, 32.0f );
			point[2] = 0.0f;

			point += corner[i];

			data.m_vOrigin = point;
			data.m_vNormal = normal;
			VectorAngles( normal, data.m_vAngles );
			data.m_flScale = random->RandomFloat( 4, 6 );

			DispatchEffect( "watersplash", data );
		}
		*/

		//FIXME: This code will not work correctly given how the ragdoll/fluid collision is acting currently
		return;
	}

	Vector vel;
	pObject->GetVelocity( &vel, NULL );
	float rawSpeed = -DotProduct( normal, vel );

	// proportional to cross-sectional area times velocity squared (fluid pressure)
	float speed = rawSpeed * rawSpeed * extents[0] * extents[1] * (1.0f / 2500000.0f) * pObject->GetMass() * (0.01f);

	speed = clamp( speed, 0, 50 );

	bool bRippleOnly = false;

	// allow the entity to perform a custom splash effect
	if ( pEntity->PhysicsSplash( centerPoint, normal, rawSpeed, speed ) )
		return;

	//Deny really weak hits
	//FIXME: We still need to ripple the surface in this case
	if ( speed <= 0.35f )
	{
		if ( speed <= 0.1f )
			return;

		bRippleOnly = true;
	}

	float size = RemapVal( speed, 0.35, 50, 8, 18 );

	//Find the surface area
	float	radius = extents[0] * extents[1];
	//int	splashes = clamp ( radius / 128.0f, 1, 2 );	//One splash for every three square feet of area

	//Msg( "Speed: %.2f, Size: %.2f\n, Radius: %.2f, Splashes: %d", speed, size, radius, splashes );

	Vector point;

	data.m_fFlags = 0;
	data.m_vOrigin = centerPoint;
	data.m_vNormal = normal;
	VectorAngles( normal, data.m_vAngles );
	data.m_flScale = size + random->RandomFloat( 0, 2 );
	if ( pEntity->GetWaterType() & CONTENTS_SLIME )
	{
		data.m_fFlags |= FX_WATER_IN_SLIME;
	}

	if ( bRippleOnly )
	{
		DispatchEffect( "waterripple", data );
	}
	else
	{
		DispatchEffect( "watersplash", data );
	}

	if ( radius > 500.0f )
	{
		int splashes = random->RandomInt( 1, 4 );

		for ( int i = 0; i < splashes; i++ )
		{
			point = RandomVector( -4.0f, 4.0f );
			point[2] = 0.0f;

			point += corner[i];

			data.m_fFlags = 0;
			data.m_vOrigin = point;
			data.m_vNormal = normal;
			VectorAngles( normal, data.m_vAngles );
			data.m_flScale = size + random->RandomFloat( -3, 1 );
 			if ( pEntity->GetWaterType() & CONTENTS_SLIME )
			{
				data.m_fFlags |= FX_WATER_IN_SLIME;
			}

			if ( bRippleOnly )
			{
				DispatchEffect( "waterripple", data );
			}
			else
			{
				DispatchEffect( "watersplash", data );
			}
		}
	}

	/*
	for ( i = 0; i < splashes; i++ )
	{
		point = RandomVector( -8.0f, 8.0f );
		point[2] = 0.0f;

		point += centerPoint + axes[0] * random->RandomFloat( -1, 1 ) + axes[1] * random->RandomFloat( -1, 1 );

		data.m_vOrigin = point;
		data.m_vNormal = normal;
		VectorAngles( normal, data.m_vAngles );
		data.m_flScale = size + random->RandomFloat( -2, 4 );

		DispatchEffect( "watersplash", data );
	}
	*/
}
