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

using Xunit;

using LinqStudy;

namespace LinqStudy.Test.LinqToObject
{
    /// <summary>
    /// 投影操作符
    /// 投影:遍历序列中的元素,将元素由一种类型转换为另一种类型的操作,并返回由转换后元素组成的新序列。
    /// 即是:IEnumerable<TSource> => IEnumerable<TResult>
    /// </summary>
    public class ProjectiveTest
    {
        /// <summary>
        /// Select投影:简单投影,一对一
        /// </summary>
        [Fact]
        public void Select_Test()
        {
            // Arrange
            var persons = PersonManager.GetPersons();

            // Act
            var maps = persons.Select(p=>p.Age).ToList();
            
            // Assert
            Assert.IsType<List<int>>(maps);
        }
        /// <summary>
        /// 投影为匿名类
        /// </summary>
        [Fact]
        public void Select_Anonymous_Test()
        {
            // Arrange
            var persons = PersonManager.GetPersons();

            // Act
            var maps = persons.Select(p=>new {Id=p.Id, Node=$"姓名{p.Name},年龄{p.Age}."}).ToList();
            
            // Assert
            Assert.IsNotType<List<Person>>(maps);
        }

        /// <summary>
        /// 投影传入索引(序号)参数
        /// </summary>
        [Fact]
        public void Select_Index_Test()
        {
            var persons = PersonManager.GetPersons();

            var maps = persons.Select((query,index)=> new KeyValuePair<int, Person>(index,query)).ToList();
            var indexs = persons.Select((query, index) => index).ToList();

            Assert.IsType<List<KeyValuePair<int, Person>>>(maps);
            Assert.Equal(0,indexs[0]);
        }

        /// <summary>
        ///  将序列的每个元素投影到 IEnumerable<TResult> 并将结果序列合并为一个序列。
        /// </summary>
        /// <remarks>
        ///  SelectMany:复合投影,一对多,合并多到一个集合。
        ///  枚举源序列,将源序列每一项投影为新的集合,合并所有新集合为一个可枚举序列,做为返回值;
        ///  提供了将多个 from子句组合起来的功能,它将每个对象的结果合并成单个可枚举序列。
        /// </remarks>
        [Fact]
        public void SelectMany_Test()
        {
            var employees = new List<Employee>()
            {
                new Employee(){Id=1,Name="小明",Emails=new List<string>(){ "abc@163.com", "acd@163.com", "ade@163.com" } },
                new Employee(){Id=2,Name="大壮",Emails=new List<string>(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } },
                new Employee(){Id=3,Name="周羊",Emails=new List<string>(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } },
                new Employee(){Id=4,Name="承承",Emails=new List<string>(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } },
                new Employee(){Id=5,Name="东升",Emails=new List<string>(){ "ebc@163.com", "ecd@163.com", "ede@163.com" }},
            };

            var maps = employees.SelectMany(q=>q.Emails).ToList();

            Assert.IsType<List<string>>(maps);
            Assert.Equal(15, maps.Count);
        }

        /// <summary>
        ///  将序列的每个元素投影到 IEnumerable<TResult>,并将结果序列合并为一个序列。每个源元素的索引用于该元素的投影表。
        /// </summary>
        /// <remarks>
        ///  添加一个对源枚举的索引而已。索引从0开始,最大为TSource.Count-1
        /// </remarks>
        [Fact]
        public void SelectMany_Index_Test()
        {
            var employees = new List<Employee>()
            {
                new Employee(){Id=1,Name="小明",Emails=new List<string>(){ "abc@163.com", "acd@163.com", "ade@163.com" } },
                new Employee(){Id=2,Name="大壮",Emails=new List<string>(){ "bbc@163.com", "bcd@163.com", "bde@163.com" } },
                new Employee(){Id=3,Name="周羊",Emails=new List<string>(){ "cbc@163.com", "ccd@163.com", "cde@163.com" } },
                new Employee(){Id=4,Name="承承",Emails=new List<string>(){ "dbc@163.com", "dcd@163.com", "dde@163.com" } },
                new Employee(){Id=5,Name="东升",Emails=new List<string>(){ "ebc@163.com", "ecd@163.com", "ede@163.com" }},
            };

            var maps = employees.SelectMany((q, idx) => 
            {
                q.Emails.Add(idx.ToString());
                return q.Emails;
            }).ToList();

            Assert.IsType<List<string>>(maps);
            Assert.Equal(20, maps.Count);
        }

        /// <summary>
        /// 将序列的每个元素投影到 IEnumerable<TCollection>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数。
        /// </summary>
        /// <remarks>
        /// 自定义结果项,类似Cross JOIN
        /// </remarks>
        [Fact]
        public void SelectMany_TCollection_Test()
        {
            var employees = new List<Employee>()
            {
                new Employee(){ Id=1,Name="小明",Emails=new List<string>(){ "a1@163.com", "a2@163.com", "a3@163.com" } },
                new Employee(){ Id=2,Name="大壮",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } },
                new Employee(){ Id=3,Name="周羊",Emails=new List<string>(){ "c1@163.com", "c2@163.com", "c3@163.com" } },
                new Employee(){ Id=4,Name="承承",Emails=new List<string>(){ "d1@163.com", "d2@163.com", "d3@163.com" } },
                new Employee(){ Id=5,Name="东升",Emails=new List<string>(){ "e1@163.com", "e2@163.com", "e3@163.com" } },
            };

            var maps = employees.SelectMany((employee)=> employee.Emails,(person,email)=> new { Name=person.Name,Email=email}).ToList();

            Assert.Equal(15, maps.Count);
        }

        /// <summary>
        /// 将序列的每个元素投影到 IEnumerable<TCollection>,并将结果序列合并为一个序列,并对其中每个元素调用结果选择器函数。
        /// 每个源元素的索引用于该元素的中间投影表。
        /// </summary>
        /// <remark>
        ///  自定义结果项,类似Cross JOIN
        /// </remark>
        [Fact]
        public void SelectMany_TCollection_Index_Test()
        {
            var employees = new List<Employee>()
            {
                new Employee(){ Id=1,Name="小明",Emails=new List<string>(){ "a1@163.com", "a2@163.com", "a3@163.com" } },
                new Employee(){ Id=2,Name="大壮",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } },
                new Employee(){ Id=3,Name="周羊",Emails=new List<string>(){ "c1@163.com", "c2@163.com", "c3@163.com" } },
                new Employee(){ Id=4,Name="承承",Emails=new List<string>(){ "d1@163.com", "d2@163.com", "d3@163.com" } },
                new Employee(){ Id=5,Name="东升",Emails=new List<string>(){ "e1@163.com", "e2@163.com", "e3@163.com" } },
            };

            var maps = employees.SelectMany
            (
                (employee, idx) => 
                {
                    return new List<Employee>() { new Employee() { Id = employee.Id, Name = employee.Name + "_" + idx, Emails = employee.Emails } };
                }, 

                (person,email) => new
                {
                    Name = email.Name,
                    Email = email.Emails
                }
            )
            .ToList();

            Assert.Equal(5, maps.Count);
        }

        [Fact]
        public void Test()
        {
            var cc= DocDemo("");
        }

        /// <summary>
        ///  Doc示例
        /// </summary>
        /// <param name="aa">参数</param>
        /// <exception cref="ArgumentNullException">
        ///   ArgumentNullException
        /// </exception>
        /// <example>DecDemo("dsfsadf")</example>
        /// <seealso cref="SortedDictionary{TKey, TValue}"/>
        /// <returns>参数+“-”</returns>
        private string DocDemo(string aa)
        {
            return aa + "-";
        }
    }
}