//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "cbase.h"
#include "rope_helpers.h"
#include "basetypes.h"
#include "mathlib/mathlib.h"
#include "rope_shared.h"
#include "rope_physics.h"
#include "networkvar.h"

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

class CHangRope : public CRopePhysics<512>
{
DECLARE_CLASS( CHangRope, CRopePhysics<512> );

// CRopePhysics overrides.
public:

	virtual void	GetNodeForces( CSimplePhysics::CNode *pNodes, int iNode, Vector *pAccel )
	{
		pAccel->Init( ROPE_GRAVITY );
	}

	
	virtual void ApplyConstraints( CSimplePhysics::CNode *pNodes, int nNodes )
	{
		// Apply spring forces.
		BaseClass::ApplyConstraints( pNodes, nNodes );


		// Lock the endpoints.
		pNodes[0].m_vPos = m_vEndPoints[0];
		pNodes[nNodes-1].m_vPos = m_vEndPoints[1];


		// Calculate how far it is hanging down and adjust if necessary.
		float flCurHangDist = 0;
		for ( int i=0; i < NumNodes(); i++ )
		{
			float hang = fabs( m_flStartZ - GetNode(i)->m_vPos.z );
			if ( hang > flCurHangDist )
				flCurHangDist = hang;
		}
		
		// Adjust our spring length accordingly.
		if ( flCurHangDist < m_flWantedHangDist )
			m_flCurSlack += 1;
		else
			m_flCurSlack -= 1;

		ApplyNewSpringLength();
	}


// Helpers.
public:
	
	void	ApplyNewSpringLength()
	{
		ResetSpringLength( (m_flRopeLength + m_flCurSlack + ROPESLACK_FUDGEFACTOR) / (NumNodes() - 1) );
	}


// Variables used to adjust the rope slack.
public:

	Vector	m_vEndPoints[2];
	bool	m_bAdjustSlack;
	
	float	m_flRopeLength;
	float	m_flCurSlack;

	float	m_flWantedHangDist;
	float	m_flStartZ;

};



void CalcRopeStartingConditions( 
	const Vector &vStartPos,
	const Vector &vEndPos,
	int const nNodes,
	float const desiredHang,
	float *pOutputLength,
	float *pOutputSlack
	)
{
	CHangRope rope;

	// Initialize the rope as a straight line with no slack as our first approximation.
	// We then relax the rope by adding slack until it hangs to the desired height.
	//	
	// The spring length equation is:
	// springLength = (ropeLength + slack + ROPESLACK_FUDGEFACTOR) / (nNodes - 1)
	//
	// We want our rope to be a straight line, so: 
	// springLength = ropeLength / (nNodes-1)
	//
	// Therefore our initial slack is -ROPESLACK_FUDGEFACTOR
	rope.m_flCurSlack = -ROPESLACK_FUDGEFACTOR;

	rope.m_vEndPoints[0] = vStartPos;
	rope.m_vEndPoints[1] = vEndPos;
	
	rope.m_flRopeLength = (vEndPos - vStartPos).Length();
	rope.m_flWantedHangDist = desiredHang;
	
	rope.m_flStartZ = MIN( vStartPos.z, vEndPos.z );	// Calculate hang as the Z distance from the
														// lowest endpoint to the bottom of the rope.
	
	rope.SetNumNodes( nNodes );

	// Set the node positions.
	for ( int i=0; i < rope.NumNodes(); i++ )
	{
		CSimplePhysics::CNode *pNode = rope.GetNode( i );

		float t = (float)i / (rope.NumNodes() - 1);
		VectorLerp( vStartPos, vEndPos, t, pNode->m_vPos );
		pNode->m_vPrevPos = pNode->m_vPos;
	}

	// Now simulate a little and stretch out to let it hang down.
	rope.Restart();
	rope.Simulate( 3 );

	// Set outputs.
	*pOutputLength = rope.m_flRopeLength;
	*pOutputSlack = rope.m_flCurSlack;
}