namespace Quadarax.Foundation.Core.Value.Extensions
{
    public static class EnumerableExt
    {
        private static Random _rand = new Random();
        private const string CS_MESSAGE_MUST_HAVE_ONE = "Collection must have at least one element.";
        private const string CS_MESSAGE_MUST_POSITIVE = "Must be positive number.";
        private const string CS_MESSAGE_MUST_SMALLER = "fromIndex must be smaller or equal to toIndex.";


        public static TElement GetRandom<TElement>(this IEnumerable<TElement> owner)
        {
            if (owner == null)
                throw new ArgumentNullException(nameof(owner));

            if (!owner.Any())
                throw new ArgumentOutOfRangeException(nameof(owner), CS_MESSAGE_MUST_HAVE_ONE);

            return GetRandom(owner, 0, owner.Count());
        }

        public static TElement GetRandom<TElement>(this IEnumerable<TElement> owner, int fromIndex, int toIndex = -1)
        {
            if (owner == null)
                throw new ArgumentNullException(nameof(owner));

            if (!owner.Any())
                throw new ArgumentOutOfRangeException(nameof(owner), CS_MESSAGE_MUST_HAVE_ONE);

            if (toIndex == -1)
                toIndex = owner.Count() - 1;
            if (fromIndex<0)
                throw new ArgumentOutOfRangeException(nameof(fromIndex), CS_MESSAGE_MUST_POSITIVE);
            if (toIndex<0)
                throw new ArgumentOutOfRangeException(nameof(fromIndex), CS_MESSAGE_MUST_POSITIVE);
            if (fromIndex>toIndex)
                throw new ArgumentOutOfRangeException(nameof(fromIndex), CS_MESSAGE_MUST_SMALLER);

            var index = _rand.Next(fromIndex, toIndex);
            return owner.Skip(index).Take(1).First();


        }


        public static IEnumerable<TElement> CompareDiff<TElement>(this IEnumerable<TElement> owner,
            IEnumerable<TElement> other)
        {
            if (owner== null)
                throw new ArgumentNullException(nameof(owner));
            if (other == null)
                throw new ArgumentNullException(nameof(other));

            var ownerArr = (owner as TElement[] ?? owner.ToArray()).ToArray();
            var otherArr = (other as TElement[] ?? other.ToArray()).ToArray();
            
            var result = new List<TElement>();

            foreach (var elem in ownerArr)
            {
                if (!otherArr.Contains(elem)) result.Add(elem);
            }
            foreach (var elem in otherArr)
            {
                if (!ownerArr.Contains(elem)) result.Add(elem);
            }
            return result.Distinct();
        }
        
        public static IEnumerable<TElement> CompareCommon<TElement>(this IEnumerable<TElement> owner,
            IEnumerable<TElement> other)
        {
            if (owner== null)
                throw new ArgumentNullException(nameof(owner));
            if (other == null)
                throw new ArgumentNullException(nameof(other));

            var ownerArr = (owner as TElement[] ?? owner.ToArray()).ToArray();
            var otherArr = (other as TElement[] ?? other.ToArray()).ToArray();
            
            var result = new List<TElement>();

            foreach (var elem in ownerArr)
            {
                if (otherArr.Contains(elem)) result.Add(elem);
            }
            foreach (var elem in otherArr)
            {
                if (ownerArr.Contains(elem)) result.Add(elem);
            }
            return result.Distinct();
        }
    }
}
