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

#ifndef TF_SHIELD_SHARED_H
#define TF_SHIELD_SHARED_H
#ifdef _WIN32
#pragma once
#endif

#include "mathlib/mathlib.h"
#include "mathlib/vector.h"
#include "mathlib/vmatrix.h"
#include "utlvector.h"
#include "SheetSimulator.h"
#include "predictable_entity.h"


//-----------------------------------------------------------------------------
// Purpose: Shield (mobile version)
//-----------------------------------------------------------------------------
enum
{
	SHIELD_NUM_HORIZONTAL_POINTS = 8,
	SHIELD_NUM_VERTICAL_POINTS = 8,
	SHIELD_NUM_CONTROL_POINTS = SHIELD_NUM_HORIZONTAL_POINTS * SHIELD_NUM_VERTICAL_POINTS,
	SHIELD_INITIAL_THETA = 135,
	SHIELD_INITIAL_PHI = 90,
	SHIELD_HORIZONTAL_PANEL_COUNT = (SHIELD_NUM_HORIZONTAL_POINTS - 1),
	SHIELD_VERTICAL_PANEL_COUNT = (SHIELD_NUM_VERTICAL_POINTS - 1),
	SHIELD_PANELS_COUNT = (SHIELD_HORIZONTAL_PANEL_COUNT * SHIELD_VERTICAL_PANEL_COUNT),
	SHIELD_VERTEX_BYTES = (SHIELD_NUM_CONTROL_POINTS + 7) >> 3,
	SHIELD_TIME_SUBVISIBIONS = 2
};

//--------------------------------------------------------------------------
// Mobile shield state flags
//--------------------------------------------------------------------------
enum
{
	SHIELD_MOBILE_EMP = 0x1
};


//--------------------------------------------------------------------------
// Shield grenade state
//--------------------------------------------------------------------------
enum
{
	SHIELD_FLAT_EMP = 0x1,
	SHIELD_FLAT_INACTIVE = 0x2
};

enum
{
	SHIELD_FLAT_SHUTDOWN_TIME = 1
};

enum
{
	SHIELD_GRENADE_WIDTH = 150,
	SHIELD_GRENADE_HEIGHT = 150,
};


#define SHIELD_DAMAGE_CHANGE_TIME	1.5f


//-----------------------------------------------------------------------------
// Amount of time it takes to fade the shield in or out due to EMP 
//-----------------------------------------------------------------------------
#define SHIELD_EMP_FADE_TIME 0.7f


//-----------------------------------------------------------------------------
// Amount of time it takes a point to wobble when EMPed
//-----------------------------------------------------------------------------
#define SHIELD_EMP_WOBBLE_TIME 0.1f


//-----------------------------------------------------------------------------
// Methods we must install into the effect
//-----------------------------------------------------------------------------
class IActiveVertList
{
public:
	virtual int		GetActiveVertState( int iVert ) = 0;
	virtual void	SetActiveVertState( int iVert, int bOn ) = 0;
};


class CShieldEffect
{
	DECLARE_CLASS_NOBASE( CShieldEffect );
	DECLARE_PREDICTABLE();

public:
	CShieldEffect();

	void Precache();
	void Spawn(const Vector& currentPosition, const QAngle& currentAngles);

	// Sets the collision group
	void SetCollisionGroup( int group );

	// Computes the opacity....
	float ComputeOpacity( const Vector& pt, const Vector& center ) const;

	// Computes the bounds
	void ComputeBounds( Vector& mins, Vector& maxs );

	// Simulation
	void Simulate( float dt );

	// Sets desired orientation + position
	void SetDesiredOrigin( const Vector& origin );
	void SetDesiredAngles( const QAngle& angles );
	const QAngle& GetDesiredAngles() const;

	// Hooks in active bits...
	void SetActiveVertexList( IActiveVertList *pActiveVerts );

	// Gets a point...
	const Vector& GetPoint( int x, int y ) const;
	const Vector& GetPoint( int i ) const;
	Vector& GetPoint( int i );

	// Computes control points
	void ComputeControlPoints();

	// The current angles (computed by Simulate on the server)
	const QAngle& GetCurrentAngles() const;
	void SetCurrentAngles( const QAngle& angles);

	// The current position (computed by Simulate on the server)
	const Vector& GetCurrentPosition();
	void SetCurrentPosition( const Vector& pos );

	// Compute vertex activity
	void ComputeVertexActivity();

	// Recompute whether the panels are active or not
	void ComputePanelActivity();

	// Is a particular vertex active?
	bool IsVertexActive( int x, int y ) const;

	// Is a particular panel active?
	bool IsPanelActive( int x, int y ) const;

	// Gets a control point (for collision)
	const Vector& GetControlPoint( int i ) const { return m_pControlPoint[i]; }

	// Returns the panel size (for collision testing)
	void GetPanelSize( Vector& mins, Vector& maxs ) const;

	// Change the angular spring constant. This affects how fast the shield rotates to face the angles
	// given in SetAngles. Higher numbers are more responsive, but if you go too high (around 40), it will
	// jump past the specified angles and wiggle a little bit.
	void SetAngularSpringConstant( float flConstant );

	// Set the shield theta & phi
	void SetThetaPhi( float flTheta, float flPhi );

	// Returns the render bounds
	const Vector& GetRenderMins() const;
	const Vector& GetRenderMaxs() const;

private:
	// Simulation set up
	void ComputeRestPositions();
	void SetShieldPanelSize( Vector& mins, Vector& maxs );
	void SimulateTranslation( float dt );
	void SimulateRotation( float dt, const Vector& forward );
	void ComputeOrientationMatrix();

	float m_RestLength;
	float m_PlaneDist;
	float m_ShieldTheta;
	float m_ShieldPhi;

	// Spring constants
	float m_SpringConstant;
	float m_DampConstant;
	float m_ViscousDrag;
	float m_Mass;

	float m_AngularSpringConstant;
	float m_AngularViscousDrag;

	// collision group
	int		m_CollisionGroup;

	// Directions of the control points in shield space
	Vector	m_pFixedDirection[SHIELD_NUM_CONTROL_POINTS];

	// Position of the control points in world space
	Vector	m_pControlPoint[SHIELD_NUM_CONTROL_POINTS];

	// Bitfield indicating which vertices are active
	IActiveVertList *m_pActiveVerts;

	// Bitfield indicating which panels are active
	bool	m_pActivePanels[SHIELD_PANELS_COUNT];

	// Which point on the shield to test next
	int		m_TestPoint;
	int		m_PointList[SHIELD_NUM_CONTROL_POINTS];

	// desired position + orientation
	Vector	m_vecDesiredOrigin;
	QAngle	m_angDesiredAngles;

	// collision box
	Vector  m_PanelBoxMin;
	Vector	m_PanelBoxMax;

	// Render bounds (shield space)
	Vector  m_vecRenderMins;
	Vector	m_vecRenderMaxs;

	// Actual center position (relative to m_Origin)
	// + velocity (world space)
	Vector	m_Position;
	Vector	m_Velocity;

	// our current orientation....
	QAngle  m_CurrentAngles;
	VMatrix	m_Orientation;
	float	m_Theta;
	float	m_Phi;
	float	m_ThetaVelocity;
	float	m_PhiVelocity;
};


//-----------------------------------------------------------------------------
// Inline methods
//-----------------------------------------------------------------------------
inline const QAngle& CShieldEffect::GetCurrentAngles() const 
{ 
	return m_CurrentAngles; 
}

//-----------------------------------------------------------------------------
// Returns the render bounds
//-----------------------------------------------------------------------------
inline const Vector& CShieldEffect::GetRenderMins() const
{
	return m_vecRenderMins;
}

inline const Vector& CShieldEffect::GetRenderMaxs() const
{
	return m_vecRenderMaxs;
}


#endif // TF_SHIELD_SHARED_H