using ErsatzTV.Core.Domain; using ErsatzTV.Core.Extensions; using ErsatzTV.Core.Interfaces.Scheduling; namespace ErsatzTV.Core.Scheduling; public sealed class ChronologicalMediaCollectionEnumerator : IMediaCollectionEnumerator { private readonly Lazy> _lazyMinimumDuration; private readonly List _sortedMediaItems; private readonly Lazy> _lazyMediaItemGroupSize; public ChronologicalMediaCollectionEnumerator( IEnumerable mediaItems, CollectionEnumeratorState state) { CurrentIncludeInProgramGuide = Option.None; _sortedMediaItems = mediaItems.OrderBy(identity, new ChronologicalMediaComparer()).ToList(); _lazyMinimumDuration = new Lazy>(() => _sortedMediaItems.Bind(i => i.GetNonZeroDuration()).OrderBy(identity).HeadOrNone()); _lazyMediaItemGroupSize = new Lazy>(CalculateMediaItemGroupSizes); State = new CollectionEnumeratorState { Seed = state.Seed }; if (state.Index >= _sortedMediaItems.Count) { state.Index = 0; state.Seed = 0; } while (State.Index < state.Index) { MoveNext(); } } private Dictionary CalculateMediaItemGroupSizes() { var result = new Dictionary(); List groupedItems = MultiPartEpisodeGrouper.GroupMediaItems(_sortedMediaItems, false); foreach (GroupedMediaItem group in groupedItems) { int size = group.Additional.Count + 1; result[group.First.Id] = size; foreach (MediaItem additional in group.Additional) { result[additional.Id] = size; } } return result; } public void ResetState(CollectionEnumeratorState state) => // seed doesn't matter in chronological State.Index = state.Index; public CollectionEnumeratorState State { get; } public Option Current => _sortedMediaItems.Count != 0 ? _sortedMediaItems[State.Index] : None; public Option CurrentIncludeInProgramGuide { get; } public void MoveNext() => State.Index = (State.Index + 1) % _sortedMediaItems.Count; public Option MinimumDuration => _lazyMinimumDuration.Value; public int Count => _sortedMediaItems.Count; public int GroupSizeForMediaItem(MediaItem mediaItem) => _lazyMediaItemGroupSize.Value.GetValueOrDefault(mediaItem.Id, 1); }