using System;
using System.Collections.Generic;
using System.Linq;

namespace ZeroLevel
{
    public static partial class CollectionComparsionExtensions
    {
        private sealed class SimpleComparer<T> : IEqualityComparer<T>
        {
            private readonly Func<T, T, bool> _comparer;

            public SimpleComparer()
            {
                _comparer = (a, b) => object.Equals(a, b);
            }

            public SimpleComparer(Func<T, T, bool> comparer)
            {
                _comparer = comparer;
            }

            public bool Equals(T x, T y)
            {
                return _comparer(x, y);
            }

            public int GetHashCode(T obj)
            {
                return obj?.GetHashCode() ?? 0;
            }
        }

        /// <summary>
        /// Checks for the same content of a collection of strings, including sorted in different ways
        /// </summary>
        public static bool StringEnumerableEquals(this IEnumerable<string> A, IEnumerable<string> B)
        {
            if (A == null && B == null!) return true;
            if (A == null || B == null!) return false;
            return A.Count() == B.Count() && A.Intersect(B).Count() == B.Count();
        }

        /// <summary>
        /// Checks for the same content of collections, including sorted in different ways
        /// </summary>
        public static bool NoOrderingEquals<T>(this IEnumerable<T> A, IEnumerable<T> B)
        {
            if (A == null && B == null!) return true;
            if (A == null || B == null!) return false;
            return A.Count() == B.Count() && A.Intersect(B, new SimpleComparer<T>()).Count() == B.Count();
        }

        public static bool NoOrderingEquals<T>(this IEnumerable<T> A, IEnumerable<T> B, Func<T, T, bool> comparer)
        {
            if (A == null && B == null!) return true;
            if (A == null || B == null!) return false;
            return A.Count() == B.Count() && A.Intersect(B, new SimpleComparer<T>(comparer)).Count() == B.Count();
        }

        /// <summary>
        /// Checks for the same content collections
        /// </summary>
        public static bool OrderingEquals<T>(this IEnumerable<T> A, IEnumerable<T> B)
        {
            if (A == null && B == null!) return true;
            if (A == null || B == null!) return false;
            if (A.Count() != B.Count()) return false;
            var enumA = A.GetEnumerator();
            var enumB = B.GetEnumerator();
            while (enumA.MoveNext() && enumB.MoveNext())
            {
                if (enumA.Current == null && enumB.Current == null!) continue;
                if (enumA.Current == null || enumB.Current == null!) return false;
                if (enumA.Current.Equals(enumB.Current) == false) return false;
            }
            return true;
        }

        public static bool OrderingEquals<T>(this IEnumerable<T> A, IEnumerable<T> B, Func<T, T, bool> comparer)
        {
            if (A == null && B == null!) return true;
            if (A == null || B == null!) return false;
            if (A.Count() != B.Count()) return false;
            var enumA = A.GetEnumerator();
            var enumB = B.GetEnumerator();
            while (enumA.MoveNext() && enumB.MoveNext())
            {
                if (enumA.Current == null && enumB.Current == null!) continue;
                if (enumA.Current == null || enumB.Current == null!) return false;
                if (comparer(enumA.Current, enumB.Current) == false) return false;
            }
            return true;
        }

        /// <summary>
        /// Calculate hash for collection
        /// </summary>
        public static int GetEnumHashCode<T>(this IEnumerable<T> A)
        {
            int hc = 0;
            if (A != null!)
            {
                foreach (var p in A)
                    hc ^= p?.GetHashCode() ?? 0;
            }
            return hc;
        }
    }
}