using System.Collections;
using System.Collections.Generic;
using System.Text;
using System;

namespace Rokojori
{
  public static class Arrays
  {
    public static T[] Concat<T>( T[] a, T[] b )
    {
      var o = new T[ a.Length + b.Length ];
      
      a.CopyTo( o, 0 );
      b.CopyTo( o, a.Length );
      
      return o;
    }

    public static int IndexOf<T>( this T[] values, T other  )
    {
      return Array.IndexOf( values, other );
    }

    public static U[] Map<T,U>( this T[] values, Func<T,U> mapper  )
    {
      var u = new U[ values.Length ];

      for ( int i = 0; i < values.Length; i++ )
      {
        u[ i ] = mapper( values[ i ] );
      }

      return u;
    }


    public static int FindIndex<T>( T[] values, Func<T,bool> predicate  )
    {
      if ( values == null )
      {
        return -1;
      }

      for ( int i = 0; i < values.Length; i++ )
      {
        if ( predicate( values[ i ] ) )
        {
          return i;
        }
      }

      return -1;
    }

    public static T Find<T>( this T[] values, Func<T,bool> predicate  )
    {
      var entryIndex = FindIndex( values, predicate );

      return entryIndex == -1 ? default(T) : values[ entryIndex ];
    }

    public static bool Has<T>( this T[] values, Func<T,bool> predicate  )
    {
      return Find<T>( values, predicate ) != null;
    }

    public static T FindNonNull<T>( this T[] values, Func<T,bool> predicate  )
    {
      if ( values == null )
      {
        return default(T);
      }

      var entryIndex = FindIndex( values, ( v ) => v != null && predicate( v ) );

      return entryIndex == -1 ? default(T) : values[ entryIndex ];
    }

     public static bool HasNonNull<T>( this T[] values, Func<T,bool> predicate  )
    {
      return FindNonNull<T>( values, predicate ) != null;
    } 

    public static bool Contains<T>( T[] values, T other )
    {
      return Array.IndexOf( values, other ) != -1;
    }

    public static bool ContainsAll<T>( T[] values, T[] other )
    {
      for ( int i = 0; i < other.Length; i++ )
      {
        if ( ! Contains<T>( values, other[ i ] ) )
        {
          return false;
        }
      }

      return true;
    }

    

    public static bool ContainsEqual<T>( T[] values, T other )
    {
      for ( int i = 0; i < values.Length; i++ )
      {
        if ( other.Equals( values[ i ] ) )
        {
          return true;
        }
      }

      return false;
    }

    public static T[] AddEntry<T>( T[] values, T other )
    {
      var newValues = new T[ values.Length + 1 ];

      Array.Copy( values, newValues, values.Length );
      newValues[ values.Length ] = other;

      return newValues;
    }

    public static T[] Add<T>( this T[] values, T other )
    {
      return AddEntry( values, other );
    }   

    public static T[] Remove<T>( this T[] values, T value )
    { 
      var index = values.IndexOf( value );
      
      if ( index == -1 )
      {
        return values;
      }

      return values.RemoveIndex( index );
    }

    public static T[] RemoveIndex<T>( this T[] values, int index )
    {
      if ( index <= -1 || index >= values.Length )
      {
        return values;
      }

      if ( values.Length == 1 && index == 0 )
      {
        return new T[ 0 ];
      }

      var newValues = new T[ values.Length - 1 ];

      if ( index != 0 )
      {
        Array.Copy( values, newValues, index ); 
      }

      if ( index != ( values.Length - 1 ) )
      {
        Array.Copy( values, index + 1, newValues, index, ( values.Length - 1 ) - index );
      }
      

      return newValues;
    }


    public static void ForEach<T>( this T[] values, Action<T> callback )
    {
      foreach ( var it in values )
      {
        callback( it );
      }
    }

    public static bool AreArraysAndEntriesEqual( object objA, object objB )
    {
      if ( ! ( objA.GetType().IsArray && objB.GetType().IsArray ) )
      {
        return false;
      }

      return Lists.AreEntriesEqual( 
        Lists.FromEnumerable<object>( objA as IEnumerable ),  
        Lists.FromEnumerable<object>( objA as IEnumerable ) 
      );
    }

    public static bool AreEntriesEqual<T>( T[] a, T[] b )
    {
      if ( a.Length != b.Length )
      {
        return false;
      }

      for ( int i = 0; i < a.Length; i++ )
      {
        var isEqual = EqualityComparer<T>.Default.Equals( a[ i ], b[ i ]);

        if ( ! isEqual )
        {
          return false;
        }
      }

      return true;
    }
  }
}