//\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
// UserRTX.NET satellite library | Author: Sergei Y.Kitáev
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

#if !WASM
namespace user_rtx { using u = user_rtx;
#else
namespace user_rtx.wasm { using u = user_rtx.wasm;
#endif

/// <summary>
/// Enumerable helpers
/// </summary>
static partial class enumerable {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

public static bool HasValues<T>( IEnumerable<T> collection )
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext();
}

public static bool IsEmpty<T>( IEnumerable<T> collection ) =>
   !HasValues<T>( collection );

public static int Count<T>( IEnumerable<T> collection )
{
   using ( var iterator = collection.GetEnumerator() ) {
      int count = 0;
      while ( iterator.MoveNext() )
         count++;
      return count;
   }
}

//--------------------------------------------------------------------------------------------------

public static T? FirstOrNull<T>( IEnumerable<T> collection )
where T: class
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() ? iterator.Current : null;
}
public static T? SecondOrNull<T>( IEnumerable<T> collection )
where T: class
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() && iterator.MoveNext() ? iterator.Current : null;
}

public static T? FirstOrNullV<T>( IEnumerable<T> collection )
where T: struct
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() ? iterator.Current : null;
}
public static T? SecondOrNullV<T>( IEnumerable<T> collection )
where T: struct
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() && iterator.MoveNext() ? iterator.Current : null;
}

public static T? FirstOrDefault<T>( IEnumerable<T> collection )
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() ? iterator.Current : default;
}
public static T? SecondOrDefault<T>( IEnumerable<T> collection )
{
   using ( var iterator = collection.GetEnumerator() )
      return iterator.MoveNext() && iterator.MoveNext() ? iterator.Current : default;
}

//--------------------------------------------------------------------------------------------------

public static bool Exists<T>( IEnumerable<T> collection, T value )
where T: IEquatable<T> =>
   Exists<T>( collection, val => val.Equals( value ) );

public static bool ExistsB<T>( IEnumerable<T> collection, T value )
where T: struct =>
   Exists<T>( collection, val => val.Equals( value ) );

public static bool Exists<T>( IEnumerable<T> collection, Func<T,bool> predicate )
{
   using ( var iterator = collection.GetEnumerator() ) {
      while ( iterator.MoveNext() )
         if ( predicate( iterator.Current ) )
            return true;
      return false;
   }
}

//--------------------------------------------------------------------------------------------------

public static T? Find<T>( IEnumerable<T> collection, Func<T,bool> predicate )
where T: class
{
   foreach ( T elem in collection )
      if ( predicate( elem ) )
         return elem;
   return null;
}

public static T? FindV<T>( IEnumerable<T> collection, Func<T,bool> predicate )
where T: struct
{
   foreach ( T elem in collection )
      if ( predicate( elem ) )
         return elem;
   return null;
}

public static T? FindOrDefault<T>( IEnumerable<T> collection, Func<T,bool> predicate )
{
   foreach ( T elem in collection )
      if ( predicate( elem ) )
         return elem;
   return default;
}

/// <exception cref="EElementNotFound" />
[u.strict_result]
public static T StrictFind<T>( IEnumerable<T> collection, Func<T,bool> predicate )
{
   foreach ( T elem in collection )
      if ( predicate( elem ) )
         return elem;
   throw new EElementNotFound();
}

//--------------------------------------------------------------------------------------------------

public static void ForEach<T>( IEnumerable<T> collection, Action<T> processElementProc )
{
   foreach ( T elem in collection )
      processElementProc( elem );
}

public static async Task ForEach_A<T>( IEnumerable<T> collection, Func<T,Task> processElementAP )
{
   foreach ( T elem in collection )
      await processElementAP( elem ).ConfigureAwait(false);
}

//==================================================================================================

[u.opt_through_minus_1]
public static int FindIndex<T>( IEnumerable<T> collection, Predicate<T> predicate )
{
   using ( var iterator = collection.GetEnumerator() ) {
      int idx = -1;
      while ( iterator.MoveNext() )
      {
         idx++;
         if ( predicate( iterator.Current ) )
            return idx;
      }
      return -1;
   }
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
} // enumerable

} // user_rtx (.wasm)
