#nullable enable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace Masuit.Tools
{
    public static partial class IEnumerableExtensions
    {
        /// 
        /// 遍历IEnumerable
        /// 
        /// 
        /// 回调方法
        /// 
        public static void ForEach(this IEnumerable objs, Action action)
        {
            foreach (var o in objs)
            {
                action(o);
            }
        }
        /// 
        /// 异步foreach
        /// 
        /// 
        /// 
        /// 并行线程数
        /// 
        /// 
        public static async Task ForeachAsync(this IEnumerable source, int maxParallelCount, Func action)
        {
            if (source.Any())
            {
                using SemaphoreSlim completeSemphoreSlim = new(1);
                using SemaphoreSlim taskCountLimitsemaphoreSlim = new(maxParallelCount);
                await completeSemphoreSlim.WaitAsync();
                int runningtaskCount = source.Count();
                foreach (var item in source)
                {
                    await taskCountLimitsemaphoreSlim.WaitAsync();
                    Task.Run(async () =>
                    {
                        try
                        {
                            await action(item).ContinueWith(_ =>
                            {
                                Interlocked.Decrement(ref runningtaskCount);
                                if (runningtaskCount == 0)
                                {
                                    completeSemphoreSlim.Release();
                                }
                            });
                        }
                        finally
                        {
                            taskCountLimitsemaphoreSlim.Release();
                        }
                    });
                }
                await completeSemphoreSlim.WaitAsync();
            }
        }
        /// 
        /// 异步foreach
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Task ForeachAsync(this IEnumerable source, Func action)
        {
            return ForAsync(source, (t, _) => action(t));
        }
        /// 
        /// 按字段去重
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static IEnumerable DistinctBy(this IEnumerable source, Func keySelector)
        {
            var hash = new HashSet();
            return source.Where(p => hash.Add(keySelector(p)));
        }
        /// 
        /// 添加多个元素
        /// 
        /// 
        /// 
        /// 
        public static void AddRange(this ICollection @this, params T[] values)
        {
            foreach (var obj in values)
            {
                @this.Add(obj);
            }
        }
        /// 
        /// 添加符合条件的多个元素
        /// 
        /// 
        /// 
        /// 
        /// 
        public static void AddRangeIf(this ICollection @this, Func predicate, params T[] values)
        {
            foreach (var obj in values)
            {
                if (predicate(obj))
                {
                    @this.Add(obj);
                }
            }
        }
        /// 
        /// 添加不重复的元素
        /// 
        /// 
        /// 
        /// 
        public static void AddRangeIfNotContains(this ICollection @this, params T[] values)
        {
            foreach (T obj in values)
            {
                if (!@this.Contains(obj))
                {
                    @this.Add(obj);
                }
            }
        }
        /// 
        /// 移除符合条件的元素
        /// 
        /// 
        /// 
        /// 
        public static void RemoveWhere(this ICollection @this, Func @where)
        {
            foreach (var obj in @this.Where(where).ToList())
            {
                @this.Remove(obj);
            }
        }
        /// 
        /// 在元素之后添加元素
        /// 
        /// 
        /// 
        /// 条件
        /// 值
        public static void InsertAfter(this IList list, Func condition, T value)
        {
            foreach (var item in list.Select((item, index) => new { item, index }).Where(p => condition(p.item)).OrderByDescending(p => p.index))
            {
                if (item.index + 1 == list.Count)
                {
                    list.Add(value);
                }
                else
                {
                    list.Insert(item.index + 1, value);
                }
            }
        }
        /// 
        /// 在元素之后添加元素
        /// 
        /// 
        /// 
        /// 索引位置
        /// 值
        public static void InsertAfter(this IList list, int index, T value)
        {
            foreach (var item in list.Select((v, i) => new { Value = v, Index = i }).Where(p => p.Index == index).OrderByDescending(p => p.Index))
            {
                if (item.Index + 1 == list.Count)
                {
                    list.Add(value);
                }
                else
                {
                    list.Insert(item.Index + 1, value);
                }
            }
        }
        /// 
        /// 转HashSet
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static HashSet ToHashSet(this IEnumerable source, Func selector)
        {
            var set = new HashSet();
            set.UnionWith(source.Select(selector));
            return set;
        }
        /// 
        /// 异步Select
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Task SelectAsync(this IEnumerable source, Func> selector)
        {
            return Task.WhenAll(source.Select(selector));
        }
        /// 
        /// 异步Select
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Task SelectAsync(this IEnumerable source, Func> selector)
        {
            return Task.WhenAll(source.Select(selector));
        }
        /// 
        /// 异步For
        /// 
        /// 
        /// 
        /// 
        /// 
        public static Task ForAsync(this IEnumerable source, Func selector)
        {
            return Task.WhenAll(source.Select(selector));
        }
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MaxOrDefault(this IQueryable source, Expression> selector) => source.Select(selector).DefaultIfEmpty().Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MaxOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        public static TSource MaxOrDefault(this IQueryable source) => source.DefaultIfEmpty().Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TSource MaxOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MaxOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MaxOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        public static TSource MaxOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Max();
        /// 
        /// 取最大值
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TSource MaxOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MinOrDefault(this IQueryable source, Expression> selector) => source.Select(selector).DefaultIfEmpty().Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MinOrDefault(this IQueryable source, Expression> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        public static TSource MinOrDefault(this IQueryable source) => source.DefaultIfEmpty().Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TSource MinOrDefault(this IQueryable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MinOrDefault(this IEnumerable source, Func selector) => source.Select(selector).DefaultIfEmpty().Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult MinOrDefault(this IEnumerable source, Func selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        public static TSource MinOrDefault(this IEnumerable source) => source.DefaultIfEmpty().Min();
        /// 
        /// 取最小值
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TSource MinOrDefault(this IEnumerable source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min();
        /// 
        /// 标准差
        /// 
        /// 
        /// 
        /// 
        /// 
        public static TResult StandardDeviation(this IEnumerable source, Func selector) where TResult : IConvertible
        {
            return StandardDeviation(source.Select(t => selector(t).ConvertTo())).ConvertTo();
        }
        /// 
        /// 标准差
        /// 
        /// 
        /// 
        /// 
        public static T StandardDeviation(this IEnumerable source) where T : IConvertible
        {
            return StandardDeviation(source.Select(t => t.ConvertTo())).ConvertTo();
        }
        /// 
        /// 标准差
        /// 
        /// 
        /// 
        public static double StandardDeviation(this IEnumerable source)
        {
            double result = 0;
            int count = source.Count();
            if (count > 1)
            {
                double avg = source.Average();
                double sum = source.Sum(d => (d - avg) * (d - avg));
                result = Math.Sqrt(sum / count);
            }
            return result;
        }
    }
}