using ErsatzTV.Core.Domain; using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Scheduling; namespace ErsatzTV.Core.Scheduling.BlockScheduling; public class BlockPlayoutShuffledMediaCollectionEnumerator : IMediaCollectionEnumerator { private readonly Lazy> _lazyMinimumDuration; private readonly int _mediaItemCount; private readonly IList _mediaItems; private IList _shuffled; public BlockPlayoutShuffledMediaCollectionEnumerator( IList mediaItems, CollectionEnumeratorState state) { CurrentIncludeInProgramGuide = Option.None; _mediaItems = mediaItems; _mediaItemCount = _mediaItems.Sum(i => 1 + Optional(i.Additional).Flatten().Count()); State = state; _shuffled = Shuffle(_mediaItems); _lazyMinimumDuration = new Lazy>( () => _shuffled.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); } public void ResetState(CollectionEnumeratorState state) { // only re-shuffle if needed if (State.Seed != state.Seed || State.Index != state.Index) { State.Seed = state.Seed; State.Index = state.Index; _shuffled = Shuffle(_mediaItems); } } public CollectionEnumeratorState State { get; } public Option Current => _shuffled.Any() ? _shuffled[State.Index % _mediaItemCount] : None; public Option CurrentIncludeInProgramGuide { get; } public void MoveNext() { State.Index++; if (State.Index % _mediaItemCount == 0) { _shuffled = Shuffle(_mediaItems); } } public Option MinimumDuration => _lazyMinimumDuration.Value; public int Count => _shuffled.Count; private IList Shuffle(IList list) { var copy = new GroupedMediaItem[list.Count]; var superShuffle = new SuperShuffle(); for (var i = 0; i < list.Count; i++) { int toSelect = superShuffle.Shuffle(i, State.Seed + State.Index / list.Count, list.Count); copy[i] = list[toSelect]; } return GroupedMediaItem.FlattenGroups(copy, _mediaItemCount); } }