using ErsatzTV.Core.Domain; using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Scheduling; namespace ErsatzTV.Core.Scheduling; public class RandomizedMediaCollectionEnumerator : IMediaCollectionEnumerator { private readonly Lazy> _lazyMinimumDuration; private readonly IList _mediaItems; private readonly Random _random; private int _index; public RandomizedMediaCollectionEnumerator(IList mediaItems, CollectionEnumeratorState state) { CurrentIncludeInProgramGuide = Option.None; _mediaItems = mediaItems; _lazyMinimumDuration = new Lazy>( () => _mediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); _random = new Random(state.Seed); State = new CollectionEnumeratorState { Seed = state.Seed }; // we want to move at least once so we start with a random item and not the first // because _index defaults to 0 while (State.Index <= state.Index) { MoveNext(); } } public void ResetState(CollectionEnumeratorState state) => // seed never changes here, no need to reset State.Index = state.Index; public CollectionEnumeratorState State { get; } public Option Current => _mediaItems.Any() ? _mediaItems[_index] : None; public Option CurrentIncludeInProgramGuide { get; } public void MoveNext() { _index = _random.Next() % _mediaItems.Count; State.Index++; } public Option MinimumDuration => _lazyMinimumDuration.Value; public int Count => _mediaItems.Count; }