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

#include "cbase.h"

class C_PropScalable : public C_BaseAnimating
{
	DECLARE_CLASS( C_PropScalable, C_BaseAnimating );
	DECLARE_CLIENTCLASS();
	DECLARE_DATADESC();

public:

	C_PropScalable();

	virtual void ApplyBoneMatrixTransform( matrix3x4_t& transform );
	virtual void GetRenderBounds( Vector &theMins, Vector &theMaxs );

	// Must be available to proxy functions
	float m_flScaleX;
	float m_flScaleY;
	float m_flScaleZ;

	float m_flLerpTimeX;
	float m_flLerpTimeY;
	float m_flLerpTimeZ;

	float m_flGoalTimeX;
	float m_flGoalTimeY;
	float m_flGoalTimeZ;

	float m_flCurrentScale[3];
	bool  m_bRunningScale[3];
	float m_flTargetScale[3];

private:

	void	CalculateScale( void );
	float	m_nCalcFrame;	// Frame the last calculation was made at
};

void RecvProxy_ScaleX( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	C_PropScalable *pCoreData = (C_PropScalable *) pStruct;

	pCoreData->m_flScaleX = pData->m_Value.m_Float;
	
	if ( pCoreData->m_bRunningScale[0] == true )
	{
		pCoreData->m_flTargetScale[0] = pCoreData->m_flCurrentScale[0];
	}
}

void RecvProxy_ScaleY( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	C_PropScalable *pCoreData = (C_PropScalable *) pStruct;

	pCoreData->m_flScaleY = pData->m_Value.m_Float;

	if ( pCoreData->m_bRunningScale[1] == true )
	{
		pCoreData->m_flTargetScale[1] = pCoreData->m_flCurrentScale[1];
	}
}

void RecvProxy_ScaleZ( const CRecvProxyData *pData, void *pStruct, void *pOut )
{
	C_PropScalable *pCoreData = (C_PropScalable *) pStruct;

	pCoreData->m_flScaleZ = pData->m_Value.m_Float;

	if ( pCoreData->m_bRunningScale[2] == true )
	{
		pCoreData->m_flTargetScale[2] = pCoreData->m_flCurrentScale[2];
	}
}

IMPLEMENT_CLIENTCLASS_DT( C_PropScalable, DT_PropScalable, CPropScalable )
	RecvPropFloat( RECVINFO( m_flScaleX ), 0, RecvProxy_ScaleX ),
	RecvPropFloat( RECVINFO( m_flScaleY ), 0, RecvProxy_ScaleY ),
	RecvPropFloat( RECVINFO( m_flScaleZ ), 0, RecvProxy_ScaleZ ),

	RecvPropFloat( RECVINFO( m_flLerpTimeX ) ),
	RecvPropFloat( RECVINFO( m_flLerpTimeY ) ),
	RecvPropFloat( RECVINFO( m_flLerpTimeZ ) ),

	RecvPropFloat( RECVINFO( m_flGoalTimeX ) ),
	RecvPropFloat( RECVINFO( m_flGoalTimeY ) ),
	RecvPropFloat( RECVINFO( m_flGoalTimeZ ) ),
END_RECV_TABLE()


BEGIN_DATADESC( C_PropScalable )
	DEFINE_AUTO_ARRAY( m_flTargetScale, FIELD_FLOAT ),
	DEFINE_AUTO_ARRAY( m_bRunningScale, FIELD_BOOLEAN ),
END_DATADESC()

C_PropScalable::C_PropScalable( void )
{
	m_flTargetScale[0] = 1.0f;
	m_flTargetScale[1] = 1.0f;
	m_flTargetScale[2] = 1.0f;

	m_bRunningScale[0] = false;
	m_bRunningScale[1] = false;
	m_bRunningScale[2] = false;

	m_nCalcFrame = 0;
}

//-----------------------------------------------------------------------------
// Purpose: Calculates the scake of the object once per frame
//-----------------------------------------------------------------------------
void C_PropScalable::CalculateScale( void )
{
	// Don't bother to calculate this for a second time in the same frame
	if ( m_nCalcFrame == gpGlobals->framecount )
		return;

	// Mark that we cached this value for the frame
	m_nCalcFrame = gpGlobals->framecount;

	float flVal[3] = { m_flTargetScale[0], m_flTargetScale[1], m_flTargetScale[2] };
	float *flTargetScale[3] = { &m_flTargetScale[0], &m_flTargetScale[1], &m_flTargetScale[2] };
	float flScale[3] = { m_flScaleX, m_flScaleY, m_flScaleZ };
	float flLerpTime[3] = { m_flLerpTimeX, m_flLerpTimeY, m_flLerpTimeZ };
	float flGoalTime[3] = { m_flGoalTimeX, m_flGoalTimeY, m_flGoalTimeZ };
	bool *bRunning[3] = { &m_bRunningScale[0], &m_bRunningScale[1], &m_bRunningScale[2] };

	for ( int i = 0; i < 3; i++ )
	{
		if ( *flTargetScale[i] != flScale[i] )
		{
			float deltaTime = (float)( gpGlobals->curtime - flGoalTime[i]) / flLerpTime[i];
			float flRemapVal = SimpleSplineRemapValClamped( deltaTime, 0.0f, 1.0f, *flTargetScale[i], flScale[i] );

			*bRunning[i] = true;

			if ( deltaTime >= 1.0f )
			{
				*flTargetScale[i] = flScale[i];
				*bRunning[i] = false;
			}

			flVal[i] = flRemapVal;
			m_flCurrentScale[i] = flVal[i];
		}
		else
		{
			m_flCurrentScale[i] = m_flTargetScale[i];
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Scales the bones based on the current scales
//-----------------------------------------------------------------------------
void C_PropScalable::ApplyBoneMatrixTransform( matrix3x4_t& transform )
{
	BaseClass::ApplyBoneMatrixTransform( transform );

	// Find the scale for this frame
	CalculateScale();

	VectorScale( transform[0], m_flCurrentScale[0], transform[0] );
	VectorScale( transform[1], m_flCurrentScale[1], transform[1] );
	VectorScale( transform[2], m_flCurrentScale[2], transform[2] );

	UpdateVisibility();
}

//-----------------------------------------------------------------------------
// Purpose: Ensures the render bounds match the scales
//-----------------------------------------------------------------------------
void C_PropScalable::GetRenderBounds( Vector &theMins, Vector &theMaxs )
{
	BaseClass::GetRenderBounds( theMins, theMaxs );

	// Find the scale for this frame
	CalculateScale();

	// Extend our render bounds to encompass the scaled object
	theMins.x *= m_flCurrentScale[0];
	theMins.y *= m_flCurrentScale[1];
	theMins.z *= m_flCurrentScale[2];

	theMaxs.x *= m_flCurrentScale[0];
	theMaxs.y *= m_flCurrentScale[1];
	theMaxs.z *= m_flCurrentScale[2];

	Assert( theMins.IsValid() && theMaxs.IsValid() );

}