/** Look for dead particles and move from the end of the list to the dead location, compacting in the process
  */
void FNiagaraSimulation::KillParticles()
{
	SCOPE_CYCLE_COUNTER(STAT_NiagaraKill);
	int32 OrigNumParticles = Data.GetNumInstances();
	int32 CurNumParticles = OrigNumParticles;
	int32 ParticleIndex = OrigNumParticles - 1;

	if (bGenerateDeathEvents)
	{
		DeathEventGenerator.BeginTrackingDeaths();
	}

	const FVector4* ParticleRelativeTimes = Data.GetVariableData(FNiagaraVariableInfo(BUILTIN_VAR_PARTICLEAGE, ENiagaraDataType::Vector));
	if (ParticleRelativeTimes)
	{
		while (ParticleIndex >= 0)
		{
			if (ParticleRelativeTimes[ParticleIndex].X > 1.0f)
			{
				if (bGenerateDeathEvents)
				{
					DeathEventGenerator.OnDeath(ParticleIndex);
				}

				check(CurNumParticles > ParticleIndex);

				// Particle is dead, move one from the end here. 
				MoveParticleToIndex(--CurNumParticles, ParticleIndex);
			}
			--ParticleIndex;
		}
	}

	Data.SetNumInstances(CurNumParticles);

	if (bGenerateDeathEvents)
	{
		DeathEventGenerator.EndTrackingDeaths();
	}

	// check if the emitter has officially died
	if (GetTickState() == NTS_Dieing && CurNumParticles == 0)
	{
		SetTickState(NTS_Dead);
	}
}
void FNiagaraSimulation::PreTick()
{
	UNiagaraEmitterProperties* PinnedProps = Props.Get();
	if (!PinnedProps || !bIsEnabled || TickState == NTS_Suspended || TickState == NTS_Dead)
		return;

	check(Data.GetNumVariables() > 0);
	check(PinnedProps->SpawnScriptProps.Script);
	check(PinnedProps->UpdateScriptProps.Script);

	// Iterate over looking for dead particles and move from the end of the list to the dead location, compacting in the process
	{
		SCOPE_CYCLE_COUNTER(STAT_NiagaraKill);
		int32 OrigNumParticles = Data.GetNumInstances();
		int32 CurNumParticles = OrigNumParticles;
		int32 ParticleIndex = OrigNumParticles-1;

		if (bGenerateDeathEvents)
		{
			DeathEventGenerator.BeginTrackingDeaths();
		}
		const FVector4* ParticleRelativeTimes = Data.GetVariableData(FNiagaraVariableInfo(FName(TEXT("Age")), ENiagaraDataType::Vector));
		if (ParticleRelativeTimes)
		{
			while (ParticleIndex >= 0)
			{
				if (ParticleRelativeTimes[ParticleIndex].X > 1.0f)
				{
					if (bGenerateDeathEvents)
					{
						DeathEventGenerator.OnDeath(ParticleIndex);
					}

					check(CurNumParticles > ParticleIndex);
					// Particle is dead, move one from the end here. 
					MoveParticleToIndex(--CurNumParticles, ParticleIndex);

					DebuggerHook_OnDeath(this, ParticleIndex, CurNumParticles);
				}
				--ParticleIndex;
			}
		}
		Data.SetNumInstances(CurNumParticles);

		if (bGenerateDeathEvents)
		{
			DeathEventGenerator.EndTrackingDeaths();
		}

		// check if the emitter has officially died
		if (GetTickState() == NTS_Dieing && CurNumParticles == 0)
		{
			SetTickState(NTS_Dead);
		}
	}

	//Swap all data set buffers before doing the main tick on any simulation.
	for (TPair<FNiagaraDataSetID, FNiagaraDataSet*> SetPair : DataSetMap)
	{
		SetPair.Value->Tick();
	}
}
void FNiagaraSimulation::Tick(float DeltaSeconds)
{
	SCOPE_CYCLE_COUNTER(STAT_NiagaraTick);

	if (!bIsEnabled || TickState==NTS_Suspended || TickState==NTS_Dead)
		return;

	SimpleTimer TickTime;

	check(Data.GetNumAttributes() > 0);
	check(Props->SpawnScript);
	check(Props->UpdateScript);

	// Cache the ComponentToWorld transform.
//	CachedComponentToWorld = Component.GetComponentToWorld();

	Data.SwapBuffers();
	Data.SetNumParticles(Data.GetPrevNumParticles());

	int32 OrigNumParticles = Data.GetNumParticles();
	int32 NumToSpawn = 0;

	// Figure out how many we will spawn.
	NumToSpawn = CalcNumToSpawn(DeltaSeconds);
	int32 MaxNewParticles = OrigNumParticles + NumToSpawn;
	Data.Allocate(MaxNewParticles);

	Age += DeltaSeconds;
	Constants.SetOrAdd(TEXT("Emitter Age"), FVector4(Age, Age, Age, Age));
	Constants.SetOrAdd(TEXT("Delta Time"), FVector4(DeltaSeconds, DeltaSeconds, DeltaSeconds, DeltaSeconds));

	// Simulate particles forward by DeltaSeconds.
	if (TickState==NTS_Running || TickState==NTS_Dieing)
	{
		SCOPE_CYCLE_COUNTER(STAT_NiagaraSimulate);
		RunVMScript(Props->UpdateScript, EUnusedAttributeBehaviour::Copy);
	}
	
	//Init new particles with the spawn script.
	if (TickState==NTS_Running)
	{
		SCOPE_CYCLE_COUNTER(STAT_NiagaraSpawn);
		Data.SetNumParticles(MaxNewParticles);
		//For now, zero any unused attributes here. But as this is really uninitialized data we should maybe make this a more serious error.
		RunVMScript(Props->SpawnScript, EUnusedAttributeBehaviour::Zero, OrigNumParticles, NumToSpawn);
	}

	// Iterate over looking for dead particles and move from the end of the list to the dead location, compacting in the process
	{
		SCOPE_CYCLE_COUNTER(STAT_NiagaraKill);
		int32 CurNumParticles = OrigNumParticles = Data.GetNumParticles();
		int32 ParticleIndex = 0;
		const FVector4* ParticleRelativeTimes = Data.GetAttributeData(FNiagaraVariableInfo(FName(TEXT("Age")), ENiagaraDataType::Vector));
		if (ParticleRelativeTimes)
		{
			while (ParticleIndex < OrigNumParticles)
			{
				if (ParticleRelativeTimes[ParticleIndex].X > 1.0f)
				{
					// Particle is dead, move one from the end here. 
					MoveParticleToIndex(--CurNumParticles, ParticleIndex);
				}
				ParticleIndex++;
			}
		}
		Data.SetNumParticles(CurNumParticles);

		// check if the emitter has officially died
		if (GetTickState() == NTS_Dieing && CurNumParticles == 0)
		{
			SetTickState(NTS_Dead);
		}
	}



	CPUTimeMS = TickTime.GetElapsedMilliseconds();

	DECLARE_DWORD_COUNTER_STAT(TEXT("NumParticles"), STAT_NiagaraNumParticles, STATGROUP_Niagara);
	INC_DWORD_STAT_BY(STAT_NiagaraNumParticles, Data.GetNumParticles());

}