// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

// =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
//
// MergeEnumerator.cs
//
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace System.Linq.Parallel
{
    /// <summary>
    /// Convenience class used by enumerators that merge many partitions into one.
    /// </summary>
    /// <typeparam name="TInputOutput"></typeparam>
    internal abstract class MergeEnumerator<TInputOutput> : IEnumerator<TInputOutput>
    {
        protected QueryTaskGroupState _taskGroupState;

        //-----------------------------------------------------------------------------------
        // Initializes a new enumerator with the specified group state.
        //

        protected MergeEnumerator(QueryTaskGroupState taskGroupState)
        {
            Debug.Assert(taskGroupState != null);
            _taskGroupState = taskGroupState;
        }

        //-----------------------------------------------------------------------------------
        // Abstract members of IEnumerator<T> that must be implemented by concrete subclasses.
        //

        public abstract TInputOutput Current { get; }

        public abstract bool MoveNext();

        //-----------------------------------------------------------------------------------
        // Straightforward IEnumerator<T> methods. So subclasses needn't bother.
        //

        object? IEnumerator.Current
        {
            get { return ((IEnumerator<TInputOutput>)this).Current; }
        }

        public virtual void Reset()
        {
            // (intentionally left blank)
        }

        //-----------------------------------------------------------------------------------
        // If the enumerator is disposed of before the query finishes, we need to ensure
        // we properly tear down the query such that exceptions are not lost.
        //

        public virtual void Dispose()
        {
            // If the enumerator is being disposed of before the query has finished,
            // we will wait for the query to finish.  Cancellation should have already
            // been initiated, so we just need to ensure exceptions are propagated.
            if (!_taskGroupState.IsAlreadyEnded)
            {
                Debug.Assert(_taskGroupState.CancellationState.TopLevelDisposedFlag.Value);
                _taskGroupState.QueryEnd(true);
            }
        }
    }
}
