using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Chernobyl.Collections.Generic
{
///
/// An that continuously loops through an .
///
public class LoopingEnumerator : IEnumerator
{
/// The values to infinitely loop.
/// True if this instance should start at the end when it is finished
/// with one full iteration. False if it should start at the beginning.
public LoopingEnumerator(IEnumerable enumerable, bool reverseWhenFinished = false)
{
_enumerable = enumerable;
_reverseWhenFinished = reverseWhenFinished;
_forward = _enumerable.GetEnumerator();
_backward = _enumerable.Reverse().GetEnumerator();
_enumerator = _forward;
}
///
public T Current
{
get
{
if (_currentAvailable == null)
throw new InvalidOperationException("Enumeration has not started. Call MoveNext.");
var current = default(T);
if (_currentAvailable == true) current = _enumerator.Current;
return current;
}
}
///
object IEnumerator.Current => Current;
///
public bool MoveNext()
{
bool result = _enumerator.MoveNext();
_currentAvailable = result;
if (result == false)
{
if(_reverseWhenFinished)
{
if (_enumerator == _forward)
{
_enumerator.Reset();
_enumerator = _backward;
}
else
{
// Reverse enumerators don't support Reset so we have to grab a fresh
// reverse enumerator.
_enumerator = _forward;
_backward.Dispose();
_backward = _enumerable.Reverse().GetEnumerator();
}
}
else _enumerator.Reset();
_currentAvailable = result = _enumerator.MoveNext();
}
return result;
}
///
public void Reset()
{
_enumerator.Reset();
_enumerator = _forward;
}
///
public void Dispose()
{
_backward.Dispose();
_forward.Dispose();
}
readonly IEnumerable _enumerable;
readonly bool _reverseWhenFinished;
readonly IEnumerator _forward;
IEnumerator _backward;
IEnumerator _enumerator;
// Set to the last return value of MoveNext or null if MoveNext has not been called.
bool? _currentAvailable;
}
///
/// Utility and extension methods for .
///
public static class LoopingEnumerator
{
///
/// Turns an into an infinitely looping .
///
/// The values to infinitely loop.
/// True if this instance should start at the end when it
/// is finished with one full iteration. False if it should start at the beginning.
public static IEnumerable AsLooping(this IEnumerable enumerable, bool reverseWhenFinished = false) =>
new EnumeratorEnumerable(() => new LoopingEnumerator(enumerable, reverseWhenFinished));
}
}