Code/Compiled/Block.cs
using System;
using System.Collections.Immutable;

namespace Sandbox.MovieMaker.Compiled;

#nullable enable
/// <summary>
/// A block of time where something happens in an <see cref="ICompiledTrack"/>.
/// </summary>
public interface ICompiledBlock : ITrackBlock;

/// <summary>
/// Unused, will describe starting / stopping an action in the scene.
/// </summary>
/// <param name="TimeRange">Start and end time of this block.</param>
public sealed record CompiledActionBlock( MovieTimeRange TimeRange ) : ICompiledBlock;

/// <summary>
/// Interface for blocks describing a property changing value over time.
/// </summary>
public interface ICompiledPropertyBlock : ICompiledBlock, IPropertyBlock;

/// <summary>
/// Interface for blocks describing a property changing value over time.
/// Typed version of <see cref="ICompiledPropertyBlock"/>.
/// </summary>
// ReSharper disable once TypeParameterCanBeVariant
public interface ICompiledPropertyBlock<T> : ICompiledPropertyBlock, IPropertyBlock<T>;

/// <summary>
/// This block has a single constant value for the whole duration.
/// Useful for value types that can't be interpolated, and change infrequently.
/// </summary>
/// <typeparam name="T">Property value type.</typeparam>
/// <param name="TimeRange">Start and end time of this block.</param>
/// <param name="Value">Constant value.</param>
public sealed record CompiledConstantBlock<T>( MovieTimeRange TimeRange, T Value )
	: ICompiledPropertyBlock<T>
{
	public T GetValue( MovieTime time ) => Value;
}

/// <summary>
/// This block contains an array of values sampled at uniform intervals.
/// </summary>
/// <typeparam name="T">Property value type.</typeparam>
/// <param name="TimeRange">Start and end time of this block.</param>
/// <param name="Offset">Time offset of the first sample.</param>
/// <param name="SampleRate">How many samples per second.</param>
/// <param name="Samples">Raw sample values.</param>
public sealed partial record CompiledSampleBlock<T>( MovieTimeRange TimeRange, MovieTime Offset, int SampleRate, ImmutableArray<T> Samples )
	: ICompiledPropertyBlock<T>
{
	private readonly ImmutableArray<T> _samples = Validate( Samples );

	public ImmutableArray<T> Samples
	{
		get => _samples;
		init => _samples = Validate( value );
	}

	public T GetValue( MovieTime time ) =>
		Samples.Sample( time.Clamp( TimeRange ) - TimeRange.Start - Offset, SampleRate, _interpolator );

	private static ImmutableArray<T> Validate( ImmutableArray<T> samples )
	{
		if ( samples.IsDefaultOrEmpty )
		{
			throw new ArgumentException( "Expected at least one sample.", nameof(Samples) );
		}

		return samples;
	}

#pragma warning disable SB3000
	private static readonly IInterpolator<T>? _interpolator = Interpolator.GetDefault<T>();
#pragma warning restore SB3000
}