
///////////////////////////////////////////////////////////////////////////////
//
// File   : AVP_InventoryWeapon_Flamethrower.cpp
// Author : Emil Popovic
// Date   : 12-Sep-2008
//
// Copyright (C) 2008 Rebellion, All Rights Reserved.
//
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// Include files...
////////////////////////////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header...
#include "Inventory/Items/Weapons/AVP_InventoryWeapon_Flamethrower.h"

// Asura independent header-files...
#include "Blueprint/Asura_Blueprint_System.h"
#include "Core/Asura_Spline.h"
#include "Debugging/Asura_DebugLineRender.h"
#include "Debugging/Asura_DebugText.h"
#include "Entity/PhysicalObject/Moves/Asura_Movement_System.h"

// Game independent header-files...
#include "Actor/Vertebrates/AVP_Vertebrate_InputHandler.h"
#include "Blueprint/AVP_Blueprint_HashIDs.h"
#include "Entity/AVP_Messages.h"
#include "Entity/PhysicalObject/Actor/AVP_ClientEntity_Actor.h"
#include "Entity/PhysicalObject/Projectile/AVP_Projectile_Messages.h"
#include "Entity/PhysicalObject/Projectile/AVP_GenericProjectileData.h"
#include "Inventory/AVP_AmmoStore.h"
#include "Inventory/AVP_Inventory_Group.h"
#include "Inventory/AVP_Inventory_System.h"
#include "Inventory/Items/Weapons/AVP_RangedWeapon_ClientEffectsHandler.h"



//#define DEBUG_ENABLE_LINE_INFO
//#define USE_SEGMENTED_LINE



//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
// This class is substitute for SplineLine. It has exactly same usage, however, it doesn't calculate spline, but instead it is counting only
// straight lines. So it should be faster. However, I am not using it at all for the moment.
#ifdef USE_SEGMENTED_LINE

class SegmentedLine
{
public:
	void  AddPoint( const Asura_Vector_3& xPoint );
	void  GetPositionFromDistance( float fDistance, Asura_Vector_3& xPoint );
	void  RenderLines( Asura_Quat xOri, Asura_Vector_3 xPos );
	float GetTotalLength()				{return m_fJoinedDistance;}

	SegmentedLine( u_int uMaxPoints );
	~SegmentedLine()					{	Destroy();	}

private:
	Asura_Vector_3*	m_pxPoints;
	float*			m_pfDistanceToNextPoint;
	float			m_fJoinedDistance;
	u_int			m_uUsedPoints;
	u_int			m_uMaxPoints;

	void			GetPoint( u_int pointId, Asura_Vector_3& xPoint );
	void			GetLastPoint( Asura_Vector_3& xPoint )	{	GetPoint(m_uUsedPoints-1, xPoint);	}
	void			Destroy();
};

SegmentedLine::SegmentedLine( u_int uMaxPoints ) : m_fJoinedDistance(0.0f), m_uUsedPoints(0), m_uMaxPoints(uMaxPoints)
{	
	m_pxPoints = new Asura_Vector_3 [m_uMaxPoints];
	m_pfDistanceToNextPoint = new float [m_uMaxPoints];

	if( !m_pxPoints || !m_pfDistanceToNextPoint )
	{
		Asura_Assert(false, "Not enough memory for allocating part of the flamethrower.");
		Destroy();
		return;
	}


	for( u_int u=0; u<m_uMaxPoints; u++ )
	{
		m_pxPoints[u].Zero();
		m_pfDistanceToNextPoint[u] = 0.0f;
	}
}

void SegmentedLine::Destroy()
{
	if( m_pxPoints )
	{
		delete [] m_pxPoints;
		m_pxPoints = NULL;
	}

	if( m_pfDistanceToNextPoint )
	{
		delete [] m_pfDistanceToNextPoint;
		m_pfDistanceToNextPoint = NULL;
	}

	m_fJoinedDistance = 0.0f;
	m_uUsedPoints = 0;
	m_uMaxPoints = 0;
}

void SegmentedLine::AddPoint( const Asura_Vector_3& xPoint )
{
	if( m_uUsedPoints>=m_uMaxPoints )
	{
		return;
	}

	m_pxPoints[m_uUsedPoints] = xPoint;


	if ( m_uUsedPoints>=1 && m_uUsedPoints< m_uMaxPoints - 1 )
	{
		Asura_Vector_3 xV = m_pxPoints[m_uUsedPoints] - m_pxPoints[m_uUsedPoints-1];
		m_pfDistanceToNextPoint[m_uUsedPoints-1] = xV.Magnitude();
		m_fJoinedDistance += m_pfDistanceToNextPoint[m_uUsedPoints-1];
	}

	m_uUsedPoints++;
}

void SegmentedLine::GetPoint( u_int pointId, Asura_Vector_3& xPoint )
{
	if( pointId>=m_uUsedPoints )
	{
		xPoint.Zero();
	}
	else
	{
		xPoint = m_pxPoints[pointId];
	}
}

void SegmentedLine::GetPositionFromDistance( float fDistance, Asura_Vector_3& xPoint )
{
	if( fDistance >= m_fJoinedDistance )
	{
		GetLastPoint( xPoint );
	}
	else if( fDistance<=0.0f )
	{
		GetPoint( 0, xPoint );
	}
	else
	{
		int c = 0;
		while ( fDistance > m_pfDistanceToNextPoint[c] )
		{
			fDistance -= m_pfDistanceToNextPoint[c];
			c++;
		}

		xPoint.Lerp( m_pxPoints[c], m_pxPoints[c+1], fDistance / m_pfDistanceToNextPoint[c] );
	}
}

void SegmentedLine::RenderLines( Asura_Quat xOri, Asura_Vector_3 xPos )
{
	for( u_int u=1; u< m_uUsedPoints; u++ )
	{
		Asura_Vector_3 xPos1 = m_pxPoints[u-1];
		Asura_Vector_3 xPos2 = m_pxPoints[u  ];

		xPos1 = xOri.RotateVector( xPos1 );
		xPos2 = xOri.RotateVector( xPos2 );

		Asura_DebugLineRender::AddLine( xPos1+xPos, xPos2+xPos, 0xFF0000FF );
	}
}


#endif // USE_SEGMENTED_LINE


//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------

bool AVP_BoneDataModifier_AnimationFlame::FindAllProjectilesFlametrowerFiredOut( Asura_List< AVP_ClientProjectile_Flamethrower > &xList, Asura_Guid uOwnerGuid, const Asura_Vector_3& xCurrentMuzzlePos )
{
	for (Asura_Hashed_List_It<AVP_ClientProjectile_Flamethrower> xActorIter(AVP_ClientProjectile_Flamethrower::GetList()); !xActorIter.Done(); xActorIter.Next())
	{
		AVP_ClientProjectile_Flamethrower* pxProjectile = xActorIter.GetCurrent();

		const Asura_Guid uProjectileOwnerGuid = pxProjectile->GetOwnerGuid();
		const Asura_Vector_3& xCurrentPos = pxProjectile->GetCurrentPosition();
		const bool bIsDetachedFromFlame  = pxProjectile->IsDetachedFromFlame();
		const bool bIsNearByMuzzlePoint  = xCurrentMuzzlePos.ApproximatelyEquals( xCurrentPos, 0.1f );
		const bool bIsEnoughtTimeInAir   = (pxProjectile->GetTimeInAir()>=0.1f);

		// Make sure that this is the projectile which belongs to this flamethrower. And also that it is not very close to the muzzle point, otherwise there will be graphical artifacts.
		if( uProjectileOwnerGuid==uOwnerGuid && bIsDetachedFromFlame==false && bIsNearByMuzzlePoint==false && bIsEnoughtTimeInAir )
		{
			//Asura_DebugLineRender::AddSphere( xCurrentPos, 0.2f, 0xFF00FF00 );
			xList.InsertInList( pxProjectile, AVP_BoneDataModifier_AnimationFlame::CompareProjectilesByGuid );
		}
	}

	// If there are no projectiles in the air, don't modify flame. This is case where the flame is not shown at all.
	if( xList.IsEmpty() )
	{
		return false;
	}

	return true;
}


void AVP_BoneDataModifier_AnimationFlame::UpdateBoneData(const int iFirstBone, const int iLastBone)
{
	// Update Parent
	Asura_Animation_BoneDataModifier::UpdateBoneData( iFirstBone, iLastBone);

	// Update can be called twice since due to our wonderful animation system it gets called a second time in render
	// just make sure we only do it once per frame
	if( m_uFrameNo==Asura_Timers::GetFrameNumber() )
	{
//		return;
	}

	m_uFrameNo = Asura_Timers::GetFrameNumber();


#ifdef DEBUG_ENABLE_LINE_INFO
	Debug_PrintBoneInfo();
#endif


	// this is actually muzzle position and rotation, but it is get from Animation.
	Asura_Vector_3 xCurrentMuzPos;
	Asura_Vector_3 xAnimPosition = m_pxAnimation->GetPosition();
	Asura_Quat xAnimInverseOri   = m_pxAnimation->GetOrientQuat();
	xAnimInverseOri.Invert();

	// Get current muzzle position and forward vector and store it for further use.
	if( m_pxAttachmentAnimation==NULL || m_pxAttachmentAnimation->GetMarkerLocationFromHash( m_xAttachmentHash, &xCurrentMuzPos, NULL, true )==false )	//flameFx_Start
	{
		Asura_Assert(false, "Flamethrower: Can't find any muzzle from where the flame should be released.");
	}

	Asura_List< AVP_ClientProjectile_Flamethrower > xList;
	if( FindAllProjectilesFlametrowerFiredOut( xList, m_uOwnerGuid, xCurrentMuzPos )==false )	// First parameter is [OUT], second is [IN]
	{
		//		return;
	}



#ifdef USE_SEGMENTED_LINE

	SegmentedLine xSegLine( xList.GetNumEntries()+10 );


	xSegLine.AddPoint( xAnimInverseOri.RotateVector( xCurrentMuzPos - xAnimPosition) );

	// Fill Segmented line.
	for( Asura_List_It< AVP_ClientProjectile_Flamethrower > xListIter( &xList ); !xListIter.Done(); xListIter.Next() )
	{
		AVP_ClientProjectile_Flamethrower* pxProjectile = xListIter.GetCurrent();
	
		xSegLine.AddPoint( xAnimInverseOri.RotateVector( pxProjectile->GetCurrentPosition() - xAnimPosition) );
	}

#ifdef DEBUG_ENABLE_LINE_INFO
	xSegLine.RenderLines( m_pxAnimation->GetOrientQuat(), m_pxAnimation->GetPosition() );
#endif //DEBUG_ENABLE_LINE_INFO

#else // USE_SEGMENTED_LINE

	// This is hard-coded and it should be removed. It is actually only for speed increasing of the code. However, value 200 is quite big, because
	// this value will store number of projectiles that flamethrower will shoot. And for now only about 10 projectiles are in the air in the same moment. (from same flamethrower)
	Asura_Vector_3 xPoints[200];

	int counter = 2;	// SKIP FIRST 2 POINTS. these points will be calculated differently. point zero is CLOSING POINT, point 1 is muzzle point.

	xPoints[1] = xAnimInverseOri.RotateVector(xCurrentMuzPos - xAnimPosition);	// Fill starting point. This point is actually a muzzle point.
	
	if( xList.IsEmpty() )
	{
		xPoints[2] = xPoints[1];
		++counter;
	}
	else
	{
		for( Asura_List_It< AVP_ClientProjectile_Flamethrower > xListIter( &xList ); !xListIter.Done(); xListIter.Next() )
		{
			AVP_ClientProjectile_Flamethrower* pxProjectile = xListIter.GetCurrent();

			const Asura_Vector_3& xProjectilePos = pxProjectile->GetCurrentPosition();

			xPoints[counter] = xAnimInverseOri.RotateVector(  xProjectilePos - xAnimPosition);
			++counter;
			if( counter>=200-1 )
			{
				Asura_Assert( false, "Fatal error. We run out of points for flame." );
				break;	// Avoid writing to memory that is not ours.
			}
		}
	}

	// Calculate starting and ending points
	xPoints[0      ] = xPoints[1        ] * 2.0f - xPoints[2        ];	// calculate "zero point"
	xPoints[counter] = xPoints[counter-1] * 2.0f - xPoints[counter-2];	// calculate "end point"

	// And now create the spline line from them. So according to this spline, we will calculate points for the flame.
	Asura_Spline<Asura_Vector_3> xSpline( /*xList.GetNumEntries()+3*/ counter+1, &xPoints[0] );


	// Draw the spline. It will also destroy xPoints so xPoints can't be used anymore.
	#ifdef DEBUG_ENABLE_LINE_INFO
		{
			// turn points that remains in xPoints data back to real world positions.
			for( int i = 0; i</*xList.GetNumEntries()+3*/ counter+1; i++ )
			{
				xPoints[i] = m_pxAnimation->GetOrientQuat().RotateVector( xPoints[i] );
				xPoints[i] += xAnimPosition;
			}

			Asura_Spline<Asura_Vector_3> xSpline2( /*xList.GetNumEntries()+3*/ counter+1, &xPoints[0] );
			xSpline2.RenderLines( Asura_RGBA(0xff808080), Asura_RGBA(0xff0000ff) );
		}
	#endif	// DEBUG_ENABLE_LINE_INFO


#endif	// USE_SEGMENTED_LINE



	Asura_Hierarchy_Tween* pxTween = m_pxAnimation->GetTweenStore();

	// CALCULATE RATIO
#ifdef USE_SEGMENTED_LINE
	float fSplineLength = xSegLine.GetTotalLength();
#else
	float fSplineLength = xSpline.GetTotalLength();
#endif
	float fAllBonesLength = 0.0f;

	// Extend the poor flame to be 3 times longer.
	for ( int iBoneItr = 1; iBoneItr <= iLastBone; ++iBoneItr )
	{
		Asura_Vector_3 xP;
		pxTween->GetBoneData( iBoneItr ).GetPosition( xP );
		fAllBonesLength+=xP.Magnitude();
	}

	float fRatio = 1.0f;

	if( fAllBonesLength )
	{
		fRatio = fSplineLength / fAllBonesLength;
	}
	// END OF CALCULATE RATIO.


	// Extend or shorten the flame, so it will match the length of the spline.
	for ( int iBoneItr = 1; iBoneItr <= iLastBone; ++iBoneItr )
	{
		Asura_Vector_3 xP;
		pxTween->GetBoneData( iBoneItr ).GetPosition( xP );
		pxTween->GetBoneData( iBoneItr ).SetPosition( xP * fRatio );
	}


	float fDistanceTravelled = 0.0f;
	for ( int iBoneItr = 1; iBoneItr <= iLastBone; ++iBoneItr )	// WARNING! Ignore FIRST BONE because it is ZERO. And it should stay ZERO. I don't know why, but in the bone system, that is currently used, the bone zero is somehow special. And it is alwyas zero lenghth. So it is wise to ignore it.
	{
		Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= pxTween->GetBoneData(iBoneItr);

		Asura_Vector_3 xCurPos;
		xBoneData.GetPosition(xCurPos);

		fDistanceTravelled += xCurPos.Magnitude();

		if (iBoneItr >= iFirstBone)
		{
			Asura_Vector_3 xOSSplinePoint;

#ifdef USE_SEGMENTED_LINE
			xSegLine.GetPositionFromDistance( fDistanceTravelled, xOSSplinePoint );
#else
			xSpline.GetPositionFromDistance( fDistanceTravelled, xOSSplinePoint );
#endif 

			Asura_Vector_3 xNewOSParentBonePos;
			Asura_Matrix_3x3 xNewOSParentBoneOri;
			m_pxAnimation->GetBoneLocation( iBoneItr-1, &xNewOSParentBonePos, &xNewOSParentBoneOri, false );

			Asura_Vector_3 xNewPos = xNewOSParentBoneOri.ApplyTranspose( xOSSplinePoint - xNewOSParentBonePos );

			xCurPos.Normalise();
			xNewPos.Normalise();

			Asura_Quat xChange;
			xChange.RotationFromAToB(xCurPos, xNewPos);

			Asura_Quat xOldQuat;
			pxTween->GetBoneData( iBoneItr-1 ).GetOrientation( xOldQuat );
			pxTween->GetBoneData( iBoneItr-1 ).SetOrientation( xChange * xOldQuat );
		}
	}
}


#ifdef DEBUG_ENABLE_LINE_INFO

// Debug function. It will show me rotation and position of all bones in the flame.
void AVP_BoneDataModifier_AnimationFlame::Debug_PrintBoneInfo()
{
	// Convert bone from world space to object space.
	Asura_Hierarchy_Tween* pxTween = m_pxAnimation->GetTweenStore();

	for( u_int u = 0; u < pxTween->GetNumberOfBones(); ++u )
	{
		Asura_Hierarchy_TweenStore_BoneData&	xBoneData	= pxTween->GetBoneData(u);

		Asura_Vector_3 xV;
		xBoneData.GetPosition( xV );
		Asura_Quat xO;
		xBoneData.GetOrientation( xO );
		Asura_Euler_3 xE;
		xO.ToEuler( xE );
		Asura_OutputToDebugger(( "Bone: %i, Pos: %f, %f, %f. Rot: %f,%f,%f\n", u, xV.x, xV.y, xV.z, Asura_Maths::RadToDeg(xE.x), Asura_Maths::RadToDeg(xE.y), Asura_Maths::RadToDeg(xE.z) ));
	}
}

#endif //DEBUG_ENABLE_LINE_INFO



AVP_BoneDataModifier_AnimationFlame::AVP_BoneDataModifier_AnimationFlame(Asura_Animation* pxAnimation, Asura_Animation *pxAttachmentAnimation, Asura_Hash_ID xAttachmentHash)
: Asura_Animation_BoneDataModifier( pxAnimation, 1, 1 )
, m_uFrameNo( Asura_Timers::GetFrameNumber() - 1 )
, m_pxAttachmentAnimation(pxAttachmentAnimation)
, m_xAttachmentHash(xAttachmentHash)
, m_uOwnerGuid( ASURA_GUID_UNREGISTERED )
{
}


AVP_BoneDataModifier_AnimationFlame::~AVP_BoneDataModifier_AnimationFlame()
{
}


//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------------------------------------



AVP_InventoryWeapon_Flamethrower::AVP_InventoryWeapon_Flamethrower( AVP_Inventory_Group& xParentGroup, const Asura_Hash_ID uHash, const Asura_Hash_ID uBlueprintHash )
: PARENT( xParentGroup, uHash, uBlueprintHash )
, m_pxContainerAttachment(NULL)
, m_pxAnimation(NULL)
, m_pxFlameModifier(NULL)
, m_fAmmoParametric(1.0f)
, m_uShotCount(0)
{
	m_eItemType = AVP_INVENTORYITEMTYPE_WEAPON_FLAMETHROWER;


	m_xInputStateOn3P.SetNumInputs(1);
	m_xInputStateOn3P.SetStateHash(0, 0x514BD7DE );	// Asura_GetHashID("Flamethrower ON 3P")

	m_xInputStateOn1P.SetNumInputs(1);
	m_xInputStateOn1P.SetStateHash(0, 0x514BD7A0 );	// Asura_GetHashID("Flamethrower ON 1P")

	GetStaticDataFromBlueprint();
}

void AVP_InventoryWeapon_Flamethrower::GetStaticDataFromBlueprint()
{
	const Asura_Blueprint* pxBlueprint = Asura_Blueprint_System::GetBlueprintByHashID( uAVP_BLUEPRINT_TYPE_INVENTORY_ITEMS, GetBlueprintHash() );
	if ( pxBlueprint )
	{
		Asura_Value xValue;

		// Initialise mesh for flame.
		if ( pxBlueprint->GetParameterValueByHashID( 0x48346B86, xValue ) )	// "Flame.Container"
		{
			m_uFlameMeshContainerHash = xValue.GetHashIDValue();
		}
	}
	else
	{
		Asura_Assertf( false, ( "AVP_InventoryWeapon_Flamethrower::GetStaticDataFromBlueprint - cannot find blueprint %u to initialise from.", GetBlueprintHash() ) );
	}
}

AVP_InventoryWeapon_Flamethrower::~AVP_InventoryWeapon_Flamethrower()
{
	if (m_pxFlameModifier)
	{
		delete m_pxFlameModifier;
		m_pxFlameModifier = NULL;
	}

	if (m_pxContainerAttachment)
	{
		if (m_pxContainerAttachment->IsLinked())
		{
			m_pxContainerAttachment->UnlinkAttachment(true);
		}
		else
		{
			delete m_pxContainerAttachment;
		}
		m_pxContainerAttachment = NULL;
	}
}

void AVP_InventoryWeapon_Flamethrower::DestroyAnimation()
{
	PARENT::DestroyAnimation();

	if (m_pxFlameModifier)
	{
		delete m_pxFlameModifier;
		m_pxFlameModifier = NULL;
	}

	if(m_pxClientEffectsHandler)
	{
		m_pxClientEffectsHandler->StopPermenantWeaponAnim();
	}

	if (m_pxContainerAttachment)
	{
		if (m_pxContainerAttachment->IsLinked())
		{
			m_pxContainerAttachment->UnlinkAttachment(true);
		}
		else
		{
			delete m_pxContainerAttachment;
		}
		m_pxContainerAttachment = NULL;
	}
}


int AVP_InventoryWeapon_Flamethrower::GetSubTypeIndexFromUsageType(const u_int uUsageType) const
{
	switch(uUsageType)
	{
	case UT_NONE:
	case UT_RELOAD_ALL:
	case UT_MELEE:
	case UT_COOL_DOWN:
		{
			return(-1);
		}

	case UT_FULL_AUTO_PRIMARY:
	case UT_CHARGED_SHOT_PRIMARY:
	case UT_N_BULLET_BURST_PRIMARY:
		{
			return(ST_PRIMARY);
		}

	case UT_FULL_AUTO_SECONDARY:
	case UT_CHARGED_SHOT_SECONDARY:
	case UT_N_BULLET_BURST_SECONDARY:
		{
			return(ST_SECONDARY);
		}

	default:
		{
			Asura_Assert(false, "Unexpected usage-type for this inventory-item.");
			return(-1);
		}
	}
}


void AVP_InventoryWeapon_Flamethrower::StartUsing( const u_int uUsageType )
{
	PARENT::StartUsing( uUsageType );

	int							iSubType	= GetSubTypeIndexFromUsageType( uUsageType );
	AVP_RangedWeapon_SubType*	pxSubType	= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );

	if( pxSubType )
	{
		m_uAmmoToConsumeBeforeStopFiring = Asura_Random::GetIntU( pxSubType->GetBurstFireMinShots(), pxSubType->GetBurstFireMaxShots() );
	}


	//	const AVP_GenericProjectileData* pxProjectileData = pxSubTypePrimary->GetProjectile() == ASURA_HASH_ID_UNSET ? NULL : AVP_GenericProjectileData::GetDataByHash( pxSubTypePrimary ? pxSubTypePrimary->GetProjectile() : ASURA_HASH_ID_UNSET );
	FlameAttachmentOn( uUsageType );
}

void AVP_InventoryWeapon_Flamethrower::StopUsing( const u_int uUsageType )
{
	PARENT::StopUsing( uUsageType );

	// detach all projectiles that are in the air from the flame.
	const Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
	for (Asura_Hashed_List_It<AVP_ClientProjectile_Flamethrower> xActorIter(AVP_ClientProjectile_Flamethrower::GetList()); !xActorIter.Done(); xActorIter.Next())
	{
		AVP_ClientProjectile_Flamethrower* pxProjectile = xActorIter.GetCurrent();

		const Asura_Guid uProjectileOwnerGuid = pxProjectile->GetOwnerGuid();

		if( uProjectileOwnerGuid==uOwnerGuid )
		{
			pxProjectile->SetDetachedFromFlame( true );
		}
	}

	FlameAttachmentOff();
}

// attach flame at the end of the barrel.
void AVP_InventoryWeapon_Flamethrower::FlameAttachmentOn( const u_int uUsageType )
{
	int							iSubType	= GetSubTypeIndexFromUsageType( uUsageType );
	AVP_RangedWeapon_SubType*	pxSubType	= iSubType < 0 ? NULL : static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ iSubType ] );

	if ( pxSubType )
	{
		const Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
		const AVP_ClientEntity_Actor* pxClient = AVP_ClientEntity_Actor::FindEntity( uOwnerGuid );
		if ( pxClient )
		{
			const AVP_Actor_AnimationHandler* pxAnimHandler = pxClient ? pxClient->GetAnimationHandler() : NULL;
			const bool bIsFirstPerson = pxAnimHandler ? pxAnimHandler->IsFirstPerson() : false;
			const Asura_Hash_ID	uMuzzleMarkerHash = bIsFirstPerson ? pxSubType->GetMuzzleMarkerPointFirstPerson() : pxSubType->GetMuzzleMarkerPointThirdPerson();

			Asura_Animation* pxOwnerAnim = GetAnimation();
			if( pxOwnerAnim )
			{
				if (m_pxContainerAttachment)
				{
					m_pxContainerAttachment->ChangeState( bIsFirstPerson ? m_xInputStateOn1P : m_xInputStateOn3P );
				}
				else
				{
					m_pxContainerAttachment = new Asura_AnimationAttachment_ClientContainerInstance( m_uFlameMeshContainerHash, bIsFirstPerson ? m_xInputStateOn1P : m_xInputStateOn3P );
					if (m_pxContainerAttachment)
					{
						m_pxContainerAttachment->LinkAttachment(pxOwnerAnim, uMuzzleMarkerHash, false);	
					}
				} 

				if (!m_pxFlameModifier && m_pxContainerAttachment)
				{
					Asura_ClientContainerInstance *pxClientContainer = m_pxContainerAttachment->GetClientContainerInstance();
					if (pxClientContainer && pxClientContainer->GetClassification() == AsuraContainerClass_Collection)
					{
						Asura_ClientContainerInstance_Collection * pxCollection = static_cast<Asura_ClientContainerInstance_Collection*>(pxClientContainer);
						AVP_FindFlameMeshFunctor xFunctor;

						pxCollection->Traverse(xFunctor);

						if (xFunctor.GetMesh())
						{
							m_pxAnimation = const_cast<Asura_Animation*>(xFunctor.GetMesh()->GetAnimation());

							m_pxFlameModifier = new AVP_BoneDataModifier_AnimationFlame( m_pxAnimation, pxOwnerAnim, uMuzzleMarkerHash );
						}
					}
				}

				if (m_pxFlameModifier)
				{
					const Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
					m_pxFlameModifier->SetOwnerGuid( uOwnerGuid );
				}
			}
		}
	}
}


void AVP_InventoryWeapon_Flamethrower::FlameAttachmentOff()
{
	// changing the state may destroy the animation, so clear everything up just in case.
	if (m_pxFlameModifier)
	{
		delete m_pxFlameModifier;
		m_pxFlameModifier = NULL;
	}

	if (m_pxContainerAttachment)
	{
		m_pxAnimation = NULL;
		// this may destroy the animation;
		m_pxContainerAttachment->ChangeState(Asura_Container_InputState::Off());
	}
}


void AVP_InventoryWeapon_Flamethrower::HandleReload( void )
{
	// Get our ammo store
	AVP_AmmoStore* pxAmmoStore = GetParentSystem().GetAmmoStore();

	AVP_AmmoStore::ReloadSubTypeFromAmmoStore(m_ppxSubTypes[ST_PRIMARY], pxAmmoStore);
}


void AVP_InventoryWeapon_Flamethrower::ProcessPlayerInput( const AVP_ClientEntity_Actor& xActor )
{
	AVP_InventoryItem_Weapon::ProcessPlayerInput( xActor ); // Replaces parent call - but call to layer below that...

	const static float fTimeToInitiateFire = 0.0f;

	const AVP_Actor_InputHandler* pxInputHandler = xActor.GetInputHandler();
	if ( GetCurrentState() == IDLING && pxInputHandler != NULL && ( GetTimeInState() > fTimeToInitiateFire ) )
	{
		// Work out if we should reload...
//		const bool bNoPrimaryAmmo		= m_ppxSubTypes[ ST_PRIMARY ]->IsEmpty();
//		const bool bNoSecondaryAmmo		= this->GetNumSubTypes() == 2 ? m_ppxSubTypes[ ST_SECONDARY ]->IsEmpty() : true;
//		const bool bNoAmmo				= bNoPrimaryAmmo && bNoSecondaryAmmo;

		const bool bNoAmmo = m_ppxSubTypes[ ST_PRIMARY ]->IsEmpty();
		
		bool bAllowReload = ShouldAllowReload();

		bool bFiredPrimary = false;
		// Check for the appropriate input...
		if ( !xActor.IsMovingFast() )
		{
			if ( ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::RELOAD ) > 0.0f ) && bAllowReload && CanReload() )
			{
				RequestUsing( UT_RELOAD_ALL );
			}
			else
			{
				if ( m_ppxSubTypes[ ST_PRIMARY ] != NULL )
				{
					if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::PRIMARY_ATTACK_HELD ) > 0.0f && ( !bNoAmmo ) )
					{
						if ( m_fPrimaryShotTimer <= 0.0f )
						{
							m_fPrimaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_PRIMARY ] ) )->GetRateOfFire();
							RequestUsing( UT_FULL_AUTO_PRIMARY );	// UT_N_BULLET_BURST_PRIMARY
							bFiredPrimary = true;
						}
					}
				}

				if ( !bFiredPrimary && GetNumSubTypes() == 2 )
				{
					if ( pxInputHandler->GetControlMapValue( AVP_Vertebrate_InputHandler::SECONDARY_ATTACK_HELD ) > 0.0f && ( !bNoAmmo ) )
					{
						if ( m_fSecondaryShotTimer <= 0.0f )
						{
							m_fSecondaryShotTimer = 1.0f / ( static_cast< AVP_RangedWeapon_SubType* >( m_ppxSubTypes[ ST_SECONDARY ] ) )->GetRateOfFire();
							RequestUsing( UT_FULL_AUTO_SECONDARY );	// UT_N_BULLET_BURST_SECONDARY 
						}
					}
				}
			}
		}
	}
}


bool AVP_InventoryWeapon_Flamethrower::IsPlayerIsStillFiring( const AVP_ClientEntity_Actor& xActor, int iSubType ) const
{
	if(xActor.GetMovementSystem() && xActor.GetMovementSystem()->IsMoveInProgress())
	{
		return false; // Don't fire in a move
	}

	if( (GetAmmoFiredSoFar() < (float)m_uAmmoToConsumeBeforeStopFiring) && (m_ppxSubTypes[ST_PRIMARY]->GetCurrentQuantity()>=1.0f) )
	{
		return true;
	}
	else
	{
//		return PARENT::IsPlayerIsStillFiring( xActor, iSubType );	// This allow continuous fire.
		return false;												// this stop after certain number of projectiles has been shot.
	}
}


void AVP_InventoryWeapon_Flamethrower::UpdateServerState( AVP_ServerEntity_Actor& xServer )
{
	PARENT::UpdateServerState( xServer );
}

void AVP_InventoryWeapon_Flamethrower::UpdateClientState(AVP_ClientEntity_Actor& xClient)
{
	// Call the base-function...

	UpdateAmmoAnim();

	PARENT::UpdateClientState( xClient );

#ifdef DEBUG_ENABLE_LINE_INFO

/*	const AVP_Actor_AnimationHandler* pxAnimHandler = xClient.GetAnimationHandler();
	const bool bIsFirstPerson = ( pxAnimHandler && pxAnimHandler->IsFirstPerson() );
	Asura_DebugText::AddTextBlock(1,30,0xffFFFFFF,0, bIsFirstPerson ? "First Person" : "Third Person" );
*/

	// Force client to update it's positions.
	//	xClient.GetAnimation()->Update( xClient.GetShapeInstance().GetPosition(), xClient.GetShapeInstance().GetOrientation() );
	// Or maybe this is correct update? I don't know. But right, I don't need to use any of these calls.
	//	xClient.GetAnimation()->UpdateAttachmentLocations();


//	Asura_Vector_3 xMuzPos( Asura_Maths::ZeroVector3 );
//	Asura_Matrix_3x3 xMuzOri( Asura_Maths::IdentityMatrix3x3 );
//	xClient.GetWorldSpaceMarkerPoint( 0x0001A7D2, &xMuzPos, &xMuzOri );	// Asura_GetHashID( "muz" )
//	Asura_DebugText::AddTextBlock(1,44,0xffFFFFFF,0, "x %.3f,   y %.3f,  z %.3f", xMuzPos.x, xMuzPos.y, xMuzPos.z );
//	Asura_DebugLineRender::AddCross( xMuzPos );

#endif

}

void AVP_InventoryWeapon_Flamethrower::UpdateAmmoAnim(void)
{
	Asura_Assert(m_bSubtypesUseSameAmmo || GetNumSubTypes() <= 1, "Flamethrower using more than one ammo type, ammo anim will not work correctly.");

	float fParametric =  m_ppxSubTypes[ST_PRIMARY]->GetCurrentQuantity() / m_ppxSubTypes[ST_PRIMARY]->GetMaxQuantity();

	m_fAmmoParametric = Asura_Maths::ClampToRange(fParametric, 0.0f, 1.0f);

	if(m_pxClientEffectsHandler)
	{
		if(!m_pxClientEffectsHandler->IsPermenantWeaponAnimPlaying())
		{
			m_pxClientEffectsHandler->PlayPermenantWeaponAnim();
		}

		m_pxClientEffectsHandler->SetWeaponPermenantAnimParametricTimer(m_fAmmoParametric);
	}
}

void AVP_InventoryWeapon_Flamethrower::SendCreateProjectileMessage( const AVP_GenericProjectileData* pxProjectileData, const AVP_RangedWeapon_SubType* pxSubType, u_int uUsageType, Asura_Hash_ID uMuzMP, float fRange, float fMaxRange, const Asura_Vector_3& xActorVel, const Asura_Vector_3& xFirePosition, const Asura_Vector_3& xShotDirection, const Asura_Matrix_3x3& xShotOrientation, Asura_Guid* puTargetGuid /*=NULL*/ )
{
	++m_uShotCount;

	if( m_uShotCount >= 5 )
	{
		m_uShotCount = 0;

		// BEGIN OF COPY .... These lines ARE COPIED from AVP_InventoryWeapon_RangedWeapon::SendCreateProjectileMessage!! And they are identical, except the AVP_MSGID_CREATE_GENERICPROJECTILE is changed to AVP_MSGID_CREATE_PROJECTILE_FLAMETHROWER and ballistic adjustment's has been taken out. (flamethrower is not affected by gravity yet. )
		Asura_Guid uOwnerGuid = GetParentSystem().GetOwnerGuid();
		Asura_Message* pxMessage = Asura_Network::CreateMessage( AVP_MSGID_CREATE_PROJECTILE_FLAMETHROWER, sizeof( AVP_Message_CreateGenericProjectile ) );
		Asura_Assert( pxMessage != NULL, "Unable to create message" );
		if(pxMessage != NULL)
		{
			AVP_Message_CreateGenericProjectile*	pxData		= static_cast< AVP_Message_CreateGenericProjectile* >( pxMessage->GetData() );

			Asura_Matrix_3x3 xAdjustedShotOri = xShotOrientation;

			// Fill-in the message...
			pxData->SetProjectileBlueprintHash( pxProjectileData->GetProjectileHash() );
			pxData->SetPosition( xFirePosition );
			pxData->SetOrientation( Asura_Quat(xAdjustedShotOri) );
			pxData->SetActorVelocity( xActorVel );

			float fDamageScale = 1.0f;
			if ( uUsageType == UT_CHARGED_SHOT_PRIMARY || uUsageType == UT_CHARGED_SHOT_SECONDARY )
			{
				fDamageScale = pxSubType->GetDamageMultiplierPerCharge() * m_fChargedPower;
			}
			pxData->SetDamageScale( fDamageScale );
			pxData->SetLaunch( true );

			if(puTargetGuid)
			{
				pxData->SetTargetGuid(*puTargetGuid);
			}
			else
			{
				pxData->SetTargetGuid(ASURA_GUID_UNREGISTERED);
			}

			// TODO we might need to do the local handling hack to make this more responsive on remote clients

			pxMessage->SetToGuid( uOwnerGuid );
			pxMessage->SetFromGuid( uOwnerGuid );

			Asura_Network::Send( pxMessage );
		} // if ( pxMessage != NULL )


		// END OF COPY .... These lines ARE COPIED from AVP_InventoryWeapon_RangedWeapon::SendCreateProjectileMessage!! And they are identical, except the AVP_MSGID_CREATE_GENERICPROJECTILE is changed to AVP_MSGID_CREATE_PROJECTILE_FLAMETHROWER
	}
}
