void UMovieSceneParticleTrack::AddNewParticleSystem(float KeyTime, bool bTrigger)
{
	EParticleKey::Type KeyType = bTrigger ? EParticleKey::Trigger : EParticleKey::Toggle;
	// @todo Instead of a 0.1 second event, this should be 0 seconds, requires handling 0 size sections
	float Duration = KeyType == EParticleKey::Trigger ? 0.1f : 1.f;

	UMovieSceneParticleSection* NewSection = NewObject<UMovieSceneParticleSection>(this);
	NewSection->InitialPlacement(ParticleSections, KeyTime, KeyTime + Duration, SupportsMultipleRows());
	NewSection->SetKeyType(KeyType);

	ParticleSections.Add(NewSection);
}
FVector2D FParticleSection::GetKeyBrushOrigin( FKeyHandle KeyHandle ) const
{
	UMovieSceneParticleSection* ParticleSection = Cast<UMovieSceneParticleSection>( &Section );
	if ( ParticleSection != nullptr )
	{
		FIntegralKey ParticleKey = ParticleSection->GetParticleCurve().GetKey(KeyHandle);
		if ( (EParticleKey::Type)ParticleKey.Value == EParticleKey::Activate )
		{
			return FVector2D(-1.0f, 1.0f);
		}
		else if ( (EParticleKey::Type)ParticleKey.Value == EParticleKey::Deactivate )
		{
			return FVector2D(1.0f, 1.0f);
		}
	}
	return FVector2D(0.0f, 0.0f);
}
const FSlateBrush* FParticleSection::GetKeyBrush( FKeyHandle KeyHandle ) const
{
	UMovieSceneParticleSection* ParticleSection = Cast<UMovieSceneParticleSection>( &Section );
	if ( ParticleSection != nullptr )
	{
		FIntegralKey ParticleKey = ParticleSection->GetParticleCurve().GetKey(KeyHandle);
		if ( (EParticleKey::Type)ParticleKey.Value == EParticleKey::Activate )
		{
			return LeftKeyBrush;
		}
		else if ( (EParticleKey::Type)ParticleKey.Value == EParticleKey::Deactivate )
		{
			return RightKeyBrush;
		}
	}
	return nullptr;
}
void FMatineeImportTools::CopyInterpParticleTrack( TSharedRef<ISequencer> Sequencer, UInterpTrackToggle* MatineeToggleTrack, UMovieSceneParticleTrack* ParticleTrack )
{
	const FScopedTransaction Transaction( NSLOCTEXT( "Sequencer", "PasteMatineeParticleTrack", "Paste Matinee particle track" ) );
	bool bSectionCreated = false;

	ParticleTrack->Modify();

	float KeyTime = MatineeToggleTrack->GetKeyframeTime( 0 );
	UMovieSceneParticleSection* Section = Cast<UMovieSceneParticleSection>( MovieSceneHelpers::FindSectionAtTime( ParticleTrack->GetAllSections(), KeyTime ) );
	if ( Section == nullptr )
	{
		Section = Cast<UMovieSceneParticleSection>( ParticleTrack->CreateNewSection() );
		ParticleTrack->AddSection( *Section );
		bSectionCreated = true;
	}

	if (Section->TryModify())
	{
		float SectionMin = Section->GetStartTime();
		float SectionMax = Section->GetEndTime();

		FIntegralCurve& ParticleCurve = Section->GetParticleCurve();
		for ( const auto& Key : MatineeToggleTrack->ToggleTrack )
		{
			EParticleKey::Type ParticleKey;
			if ( TryConvertMatineeToggleToOutParticleKey( Key.ToggleAction, ParticleKey ) )
			{
				ParticleCurve.AddKey( Key.Time, (int32)ParticleKey, ParticleCurve.FindKey( Key.Time ) );
			}
			SectionMin = FMath::Min( SectionMin, Key.Time );
			SectionMax = FMath::Max( SectionMax, Key.Time );
		}

		Section->SetStartTime( SectionMin );
		Section->SetEndTime( SectionMax );

		if ( bSectionCreated )
		{
			Sequencer->NotifyMovieSceneDataChanged();
		}
	}
}
void FMovieSceneParticleTrackInstance::Update( float Position, float LastPosition, const TArray<UObject*>& RuntimeObjects, class IMovieScenePlayer& Player )
{
    // @todo Sequencer We need something analagous to Matinee 1's particle replay tracks
    // What we have here is simple toggling/triggering

    if (Position > LastPosition && Player.GetPlaybackStatus() == EMovieScenePlayerStatus::Playing)
    {
        bool bTrigger = false, bOn = false, bOff = false;

        const TArray<UMovieSceneSection*> Sections = ParticleTrack->GetAllParticleSections();
        for (int32 i = 0; i < Sections.Num(); ++i)
        {
            UMovieSceneParticleSection* Section = Cast<UMovieSceneParticleSection>(Sections[i]);
            if (Section->GetKeyType() == EParticleKey::Trigger)
            {
                if (Position > Section->GetStartTime() && LastPosition < Section->GetStartTime())
                {
                    bTrigger = true;
                }
            }
            else if (Section->GetKeyType() == EParticleKey::Toggle)
            {
                if (Position >= Section->GetStartTime() && Position <= Section->GetEndTime())
                {
                    bOn = true;
                }
                else if (Position >= Section->GetEndTime() && LastPosition < Section->GetEndTime())
                {
                    bOff = true;
                }
            }
        }

        if (bTrigger || bOn || bOff)
        {
            for (int32 i = 0; i < RuntimeObjects.Num(); ++i)
            {
                AEmitter* Emitter = Cast<AEmitter>(RuntimeObjects[i]);
                if (Emitter)
                {
                    if (bTrigger)
                    {
                        Emitter->ToggleActive();
                    }
                    else if (bOn)
                    {
                        Emitter->Activate();
                    }
                    else if (bOff)
                    {
                        Emitter->Deactivate();
                    }
                }
            }
        }
    }
    else
    {
        for (int32 i = 0; i < RuntimeObjects.Num(); ++i)
        {
            AEmitter* Emitter = Cast<AEmitter>(RuntimeObjects[i]);
            if (Emitter)
            {
                Emitter->Deactivate();
            }
        }
    }
}
int32 FParticleSection::OnPaintSection( FSequencerSectionPainter& InPainter ) const
{
	TSharedPtr<ISequencer> OwningSequencer = OwningSequencerPtr.Pin();

	if (!OwningSequencer.IsValid())
	{
		return InPainter.LayerId + 1;
	}

	const ESlateDrawEffect::Type DrawEffects = InPainter.bParentEnabled ? ESlateDrawEffect::None : ESlateDrawEffect::DisabledEffect;
	UMovieSceneParticleSection* AnimSection = Cast<UMovieSceneParticleSection>( &Section );
	const FTimeToPixel& TimeToPixelConverter = InPainter.GetTimeConverter();

	FLinearColor TrackColor;

	// @todo Sequencer - These values should be cached and then refreshed only when the particle system changes.
	bool bIsLooping = false;
	float LastEmitterEndTime = 0;
	UMovieSceneParticleSection* ParticleSection = Cast<UMovieSceneParticleSection>( &Section );
	if ( ParticleSection != nullptr )
	{
		UMovieSceneParticleTrack* ParentTrack = Cast<UMovieSceneParticleTrack>( ParticleSection->GetOuter() );
		if ( ParentTrack != nullptr )
		{
			TrackColor = ParentTrack->GetColorTint();

			FGuid ObjectHandle;
			for ( const FMovieSceneBinding& Binding : OwningSequencer->GetFocusedMovieSceneSequence()->GetMovieScene()->GetBindings() )
			{
				if ( Binding.GetTracks().Contains( ParentTrack ) )
				{
					ObjectHandle = Binding.GetObjectGuid();
					break;
				}
			}

			if ( ObjectHandle.IsValid() )
			{
				UObject* BoundObject = OwningSequencer->GetFocusedMovieSceneSequenceInstance()->FindObject( ObjectHandle, *OwningSequencer );
				UParticleSystemComponent* ParticleSystemComponent = Cast<UParticleSystemComponent>(BoundObject);
				if(AEmitter* ParticleSystemActor = Cast<AEmitter>(BoundObject))
				{
					ParticleSystemComponent = ParticleSystemActor->GetParticleSystemComponent();
				}

				if ( ParticleSystemComponent != nullptr && ParticleSystemComponent->Template != nullptr )
				{
					for ( UParticleEmitter* Emitter : ParticleSystemComponent->Template->Emitters )
					{
						UParticleModuleRequired* RequiredModule = Emitter->GetLODLevel( 0 )->RequiredModule;
						bIsLooping |= RequiredModule->EmitterLoops == 0;
						LastEmitterEndTime = FMath::Max( LastEmitterEndTime, RequiredModule->EmitterDelay + RequiredModule->EmitterDuration );
					}
				}
			}
		}
	}

	// @todo Sequencer - This should only draw the visible ranges.
	TArray<TRange<float>> DrawRanges;
	TOptional<float> CurrentRangeStart;
	for ( auto KeyIterator = ParticleSection->GetParticleCurve().GetKeyIterator(); KeyIterator; ++KeyIterator )
	{
		FIntegralKey Key = *KeyIterator;
		if ( (EParticleKey::Type)Key.Value == EParticleKey::Activate )
		{
			if ( CurrentRangeStart.IsSet() == false )
			{
				CurrentRangeStart = Key.Time;
			}
			else
			{
				if ( bIsLooping == false )
				{
					if ( Key.Time > CurrentRangeStart.GetValue() + LastEmitterEndTime )
					{
						DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), CurrentRangeStart.GetValue() + LastEmitterEndTime ) );
					}
					else
					{
						DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), Key.Time ) );
					}
					CurrentRangeStart = Key.Time;
				}
			}
		}
		if ( (EParticleKey::Type)Key.Value == EParticleKey::Deactivate )
		{
			if ( CurrentRangeStart.IsSet() )
			{
				if (bIsLooping)
				{
					DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), Key.Time ) );
				}
				else
				{
					if ( Key.Time > CurrentRangeStart.GetValue() + LastEmitterEndTime )
					{
						DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), CurrentRangeStart.GetValue() + LastEmitterEndTime ) );
					}
					else
					{
						DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), Key.Time ) );
					}
				}
				CurrentRangeStart.Reset();
			}
		}
	}
	if ( CurrentRangeStart.IsSet() )
	{
		if ( bIsLooping )
		{
			DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), OwningSequencer->GetViewRange().GetUpperBoundValue() ) );
		}
		else
		{
			DrawRanges.Add( TRange<float>( CurrentRangeStart.GetValue(), CurrentRangeStart.GetValue() + LastEmitterEndTime ) );
		}
	}

	for ( const TRange<float>& DrawRange : DrawRanges )
	{
		float XOffset = TimeToPixelConverter.TimeToPixel(DrawRange.GetLowerBoundValue());
		float XSize = TimeToPixelConverter.TimeToPixel(DrawRange.GetUpperBoundValue()) - XOffset;
		FSlateDrawElement::MakeBox(
			InPainter.DrawElements,
			InPainter.LayerId,
			InPainter.SectionGeometry.ToPaintGeometry( FVector2D( XOffset, (InPainter.SectionGeometry.GetLocalSize().Y - SequencerSectionConstants::KeySize.Y) / 2 ), FVector2D( XSize, SequencerSectionConstants::KeySize.Y ) ),
			FEditorStyle::GetBrush( "Sequencer.Section.Background" ),
			InPainter.SectionClippingRect,
			DrawEffects
			);
		FSlateDrawElement::MakeBox(
			InPainter.DrawElements,
			InPainter.LayerId,
			InPainter.SectionGeometry.ToPaintGeometry( FVector2D( XOffset, (InPainter.SectionGeometry.GetLocalSize().Y - SequencerSectionConstants::KeySize.Y) / 2 ), FVector2D( XSize, SequencerSectionConstants::KeySize.Y ) ),
			FEditorStyle::GetBrush( "Sequencer.Section.BackgroundTint" ),
			InPainter.SectionClippingRect,
			DrawEffects,
			TrackColor
			);
	}

	return InPainter.LayerId+1;
}
void FParticleSection::GenerateSectionLayout( class ISectionLayoutBuilder& LayoutBuilder ) const
{
	UMovieSceneParticleSection* ParticleSection = Cast<UMovieSceneParticleSection>( &Section );
	LayoutBuilder.SetSectionAsKeyArea( MakeShareable( new FEnumKeyArea( ParticleSection->GetParticleCurve(), ParticleSection, ParticleKeyEnum ) ) );
}