Editor/MovieMaker/Signals/Operations/Transform.cs
using Sandbox.MovieMaker;
using System.Diagnostics.CodeAnalysis;

namespace Editor.MovieMaker;

#nullable enable

partial record PropertySignal<T>
{
	public static PropertySignal<T> operator *( MovieTransform transform, PropertySignal<T> signal ) =>
		transform == MovieTransform.Identity ? signal : signal.OnTransform( transform );

	public static PropertySignal<T> operator +( PropertySignal<T> signal, MovieTime translation ) =>
		new MovieTransform( Translation: translation ) * signal;

	public static PropertySignal<T> operator *( MovieTimeScale scale, PropertySignal<T> signal ) =>
		new MovieTransform( Scale: scale ) * signal;

	public static PropertySignal<T> operator *( PropertySignal<T> signal, MovieTimeScale scale ) =>
		new MovieTransform( Scale: scale ) * signal;

	protected virtual PropertySignal<T> OnTransform( MovieTransform value ) =>
		new TransformOperation<T>( this, value );
}

partial record PropertyBlock<T>
{
	[return: NotNullIfNotNull( nameof(block) )]
	public static PropertyBlock<T>? operator *( MovieTransform transform, PropertyBlock<T>? block ) =>
		transform != MovieTransform.Identity && block is not null
			? new PropertyBlock<T>( transform * block.Signal, transform * block.TimeRange )
			: block;

	[return: NotNullIfNotNull( nameof( block ) )]
	public static PropertyBlock<T>? operator +( PropertyBlock<T>? block, MovieTime translation ) =>
		new MovieTransform( Translation: translation ) * block;

	[return: NotNullIfNotNull( nameof( block ) )]
	public static PropertyBlock<T>? operator *( MovieTimeScale scale, PropertyBlock<T>? block ) =>
		new MovieTransform( Scale: scale ) * block;

	[return: NotNullIfNotNull( nameof( block ) )]
	public static PropertyBlock<T>? operator *( PropertyBlock<T>? block, MovieTimeScale scale ) =>
		new MovieTransform( Scale: scale ) * block;
}

[JsonDiscriminator( "Shift" )]
file sealed record TransformOperation<T>( PropertySignal<T> Signal, MovieTransform Value )
	: UnaryOperation<T>( Signal )
{
	public override T GetValue( MovieTime time ) => Signal.GetValue( Value.Inverse * time );

	protected override PropertySignal<T> OnTransform( MovieTransform transform ) =>
		this with { Value = transform * Value };

	protected override PropertySignal<T> OnReduce( MovieTime? start, MovieTime? end ) =>
		(Value * Signal).Reduce( start, end );
}