#include "StdAfx.h"
#include "JointBreak.h"
#include "BreakReplicator.h"
#include "NetworkCVars.h"

void SJointBreak::AddSendables( INetSendableSink * pSink, int32 brkId )
{
	CBreakReplicator::SendJointBreakWith( SJointBreakParams(brkId), pSink );
	for (size_t i=0; i<states.size(); i++)
	{
		if (states[i].isEnt)
			pSink->NextRequiresEntityEnabled( states[i].entId );
		CBreakReplicator::SendDeclareExplosiveObjectStateWith( SDeclareExplosiveObjectState(brkId, states[i]), pSink );
	}
	SProceduralBreak::AddProceduralSendables(brkId, pSink);
}

#if BREAK_HIERARCHICAL_TRACKING
static const uint32 JointBreakPBTFlags = ePBTF_ChainBreaking;
#else
static const uint32 JointBreakPBTFlags = 0;
#endif

CJointBreak::CJointBreak() : IProceduralBreakType(JointBreakPBTFlags, 'j')
{
}

CJointBreak::CJointBreak( IPhysicalEntity * pEnt ) : IProceduralBreakType(JointBreakPBTFlags, 'j')
{
	SExplosiveObjectState state;
	ExplosiveObjectStateFromPhysicalEntity(state, pEnt);
	m_states.push_back(state);

	pe_status_pos sts;
	pEnt->GetStatus(&sts);
	// TODO: use BBox
	m_rgn = AABB(sts.pos - Vec3(10,10,10), sts.pos + Vec3(10,10,10));
}

int CJointBreak::GetVirtualId( IPhysicalEntity * pEnt )
{
	int iForeignData = pEnt->GetiForeignData();
	void * pForeignData = pEnt->GetForeignData(iForeignData);
	if (iForeignData == PHYS_FOREIGN_ID_ENTITY && pForeignData)
	{
		EntityId entId = ((IEntity*)pForeignData)->GetId();
		for (size_t i=0; i<m_states.size(); i++)
		{
			if (m_states[i].isEnt && m_states[i].entId == entId)
				return i+1;
		}
	}
	else if (iForeignData == PHYS_FOREIGN_ID_STATIC && pForeignData)
	{
		IRenderNode * rn = (IRenderNode*) pForeignData;
		Vec3 objPos = rn->GetPos();
		Vec3 objCenter = rn->GetBBox().GetCenter();
		float objVolume = 0;
		if (IStatObj * pStatObj = rn->GetEntityStatObj(0))
			if (phys_geometry * pPhysGeom = pStatObj->GetPhysGeom())
				objVolume = pPhysGeom->V;

		for (size_t i=0; i<m_states.size(); i++)
			if (!m_states[i].isEnt)
				if ((objPos - m_states[i].objPos).GetLengthSquared() < sqr(0.05f))
					if ((objCenter - m_states[i].objCenter).GetLengthSquared() < sqr(0.05f))
						if (fabsf(objVolume - m_states[i].objVolume) < 0.05f)
							return i+1;
	}
	return 0;
}

CObjectSelector CJointBreak::GetObjectSelector( int idx )
{
	if (idx >= 1 && idx <= m_states.size())
	{
		idx--;
		if (m_states[idx].isEnt)
			return CObjectSelector(m_states[idx].entId);
		else
			return CObjectSelector(m_states[idx].objPos, m_states[idx].objCenter, m_states[idx].objVolume);
	}
	return CObjectSelector();
}

bool CJointBreak::GotExplosiveObjectState( const SExplosiveObjectState * pState )
{
	m_states.push_back(*pState);
	return true;
}

void CJointBreak::PreparePlayback()
{
	for (size_t i=0; i<m_states.size(); i++)
	{
		if (m_states[i].isEnt)
		{
			if (IEntity * pEnt = gEnv->pEntitySystem->GetEntity(m_states[i].entId))
			{
				pEnt->SetPosRotScale(m_states[i].entPos, m_states[i].entRot, m_states[i].entScale);
			}
			else
			{
				GameWarning("[brk] CJointBreak::PreparePlayback unable to find entity %.8x to prepare for playback", m_states[i].entId);
			}
		}
	}
}

bool CJointBreak::AttemptAbsorb( const IProceduralBreakTypePtr& pBT )
{
	if (pBT->type == type)
	{
		// can always absorb another joint break
		CJointBreak * pJB = (CJointBreak *)(IProceduralBreakType*)pBT;
		switch (pJB->m_states.size())
		{
		case 1:
			m_states.push_back( pJB->m_states[0] );
			return true;
		case 0:
			GameWarning("CJointBreak::AttemptAbsorb: nothing to absorb from new break");
			return false;
		default:
			GameWarning("CJointBreak::AttemptAbsorb: too many things to absorb");
			return false;
		}
	}
	return false;
}

void CJointBreak::BeginPlayback( bool hasJointBreaks )
{
/*
	if (!hasJointBreaks)
	{
		// need to add some threshold to force a joint break of an object that has no joints
		CObjectSelector sel;
		if (m_state.isEnt)
			sel = CObjectSelector(m_state.entId);
		else
			sel = CObjectSelector(m_state.objPos, m_state.objCenter, m_state.objVolume);
		if (CNetworkCVars::Get().BreakageLog)
			CryLogAlways("[brk] CJointBreak::BeginPlayback: apply impulse to simulate joint break on object %s", sel.GetDescription().c_str());
		if (IPhysicalEntity * pEnt = sel.Find())
		{
			pe_params_structural_joint ppsj;
			ppsj.idx = -1;
			ppsj.partidEpicenter = 0;
			pEnt->SetParams(&ppsj);
		}
		else
		{
			GameWarning("[brk] CJointBreak::BeginPlayback: couldn't apply fake-impulse since we could not find the object %s", sel.GetDescription().c_str());
		}
	}
*/
}

_smart_ptr<SProceduralBreak> CJointBreak::CompleteSend()
{
	SJointBreak * pjb = new SJointBreak;
	pjb->region = m_rgn;
	pjb->states = m_states;
	return pjb;
}

bool CJointBreak::AllowComplete( const SProceduralBreakRecordingState& state )
{
	return state.gotRemove && state.numEmptySteps > 0;
}

void CJointBreak::PatchRecording( DynArray<SProceduralSpawnRec>& spawnRecs, DynArray<SJointBreakRec>& jointBreaks )
{
	std::map<int,int> rootEntitiesAndBreaks;
	for (int i=0; i<spawnRecs.size(); i++)
		if (BIT(spawnRecs[i].op) & OPS_REFERENCING_ENTS)
			if (spawnRecs[i].idxRef < 0)
				rootEntitiesAndBreaks[spawnRecs[i].idxRef] = 0;
	for (int i=0; i<jointBreaks.size(); i++)
		if (jointBreaks[i].idxRef < 0)
			rootEntitiesAndBreaks[jointBreaks[i].idxRef] ++;
	for (std::map<int,int>::iterator it = rootEntitiesAndBreaks.begin(); it != rootEntitiesAndBreaks.end(); ++it)
	{
		if (!it->second)
		{
			SJointBreakRec jb;
			jb.idxRef = it->first;
			jb.id = -1;
#if BREAK_HIERARCHICAL_TRACKING
			jb.frame = 0;
#endif
			jb.epicenter = 0;
			jointBreaks.push_back(jb);
		}
	}
}
