using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Data;
using System.Data.SqlTypes;
using System.Data.Common;

using Xunit;
using LinqStudy.IEnumerableDemo;

namespace LinqStudy.Test.LinqToObject
{
    /// <summary>
    /// 转换运算符
    /// </summary>
    public class TypeConvertTest 
    {
        #region AsEnumeralbe 
        /// <summary>
        ///  AsEnumeralbe操作符:将IEnumerable<T>输入序列转换成IEnumerable<T>的输出序列
        ///  主要用于将一个实现了IEnumerable<T>接口的对象转换成一个标准的IEnumerable<T>接口对象。
        ///  在Linq中、不同领域的Linq实现都有自己专属的操作符。
        /// </summary>
        [Fact]
        public void AsEnumeralbe_Test()
        {
            var dt = GetDataTable();

            //DataTable 使用方法,须有添加 System.Data.DataSetExtensions 引用
            var students = dt.AsEnumerable().FirstOrDefault();

            Assert.NotNull(students);
        }
        #endregion

        #region AsQueryable
        /// <summary>
        /// 主要应用与Linq to sql
        /// </summary>
        [Fact]
        public void AsQueryable_Test()
        {
            //
        }
        #endregion

        #region OfType
        /// <summary>
        /// OfType:从序列中筛选指定类型的元素,并构建一个序列。
        /// </summary>
        [Fact]
        public void OfType_Test()
        {
            //动态数组做为数据源
            ArrayList ints = new ArrayList() { 1, "2", 3, "4", 5 };

            var values = from i in ints.OfType<int>()
                         select i;

            Assert.Equal(new List<int>() { 1, 3, 5 }, values.ToList());
        }
        #endregion

        #region Cast
        /// <summary>
        /// Cast:将数据源中元素,强制转换成指定的类型。
        /// 数据源null或存在不能转换的元素,则异常。
        /// </summary>
        [Fact]
        public void Cast_Test()
        {
            //动态数组做为数据源
            ArrayList ints = new ArrayList() { "1", "2", "3", "4", "5" };

            var values = ints.Cast<string>();

            Assert.Equal(new List<string>() { "1", "2", "3", "4", "5" }, values.ToList());
        }

        [Fact]
        public void Cast_ArgumentNullException_Test()
        {
            //动态数组做为数据源
            ArrayList ints = null;

            Assert.Throws<ArgumentNullException>(() => {
                ints.Cast<string>();
            });
        }

        [Fact]
        public void Cast_InvalidCastException_Test()
        {
            //动态数组做为数据源
            ArrayList ints = new ArrayList() { 1, "2", 3, "4" };

            Assert.Throws<InvalidCastException>(() => {
                ints.Cast<string>().ToList();
            });
        }
        #endregion

        #region ToList
        /// <summary>
        ///  ToList:将IEnumerable<T>转换为List<T>, 立即执行。
        ///  特别注意:此操作破坏"延迟执行",变为立即执行;
        ///  结果为数据源的一个快照,独立保存在内存中,不 受数据源变化的影响。
        /// </summary>
        [Fact]
        public void ToList_Test()
        {
            List<string> email = new List<string>() { "abc@163.com", "bcd@126.com", "111@163.com" };

            var query = email.Where(q => q.Contains("@163.")).ToList();

            Assert.Equal(new List<string>() { "abc@163.com", "111@163.com" }, query);
        }

        [Fact]
        public void ToList_DataSource_Change_Test()
        {
            List<string> email = new List<string>() { "abc@163.com", "bcd@126.com", "111@163.com" };

            //延迟执行机制
            var query1 = email.Where(q => q.Contains("@163."));

            //ToList 变成立即执行,保存数据源的一个“快照”
            var query2 = email.Where(q => q.Contains("@163.")).ToList();

            //数据源变化
            email.Add("test@163.com");

            //延迟执行,反映数据源变化 
            Assert.Equal(new List<string>() { "abc@163.com", "111@163.com", "test@163.com" }, query1.ToList());

            //ToList,不受数据源变化影响
            Assert.Equal(new List<string>() { "abc@163.com", "111@163.com" }, query2);
        }

        /// <summary>
        ///  引用类型的数据源,ToList之后形成的新序列是数据源的“完全新副本”的新对象。
        ///  数据源与新序列元素的引用,不是同一个对象。
        /// </summary>
        [Fact]
        public void ToList_ReferenceType_DataSource_Test()
        {
            List<Person> persons = PersonManager.GetPersons();

            var person2 = persons.ToList();

            //元素对象不是同一个元素
            Assert.NotSame(persons, person2);
        }
        #endregion

        #region ToDictionary
        /// <summary>
        /// ToDictionar转换字典,立即执行并保存快照。
        /// </summary>
        [Fact]
        public void ToDictionary_keySelector_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name="gaofeng",Age = 32 },
                new Person() {Id=2, Name="zhangsan",Age = 25 },
            };

            var dic = persons.ToDictionary(p => p.Id);

            Assert.Equal(1, dic[1].Id);
            Assert.Equal(2, dic[2].Id);
        }

        [Fact]
        public void ToDictionary_keySelector_comparer_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name="gaofeng",Age = 32 },
                new Person() {Id=2, Name="zhangsan",Age = 25 },
            };

            var dic = persons.ToDictionary<Person, int>(p => p.Id, EqualityComparer<int>.Default);

            Assert.Equal("gaofeng", dic[1].Name);
            Assert.Equal("zhangsan", dic[2].Name);
        }

        [Fact]
        public void ToDictionary_keySelector_elementSelector_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name="gaofeng",Age = 32 },
                new Person() {Id=2, Name="zhangsan",Age = 25 },
            };

            var dic = persons.ToDictionary(p => p.Id, s => s.Name);

            Assert.Equal("gaofeng", dic[1]);
            Assert.Equal("zhangsan", dic[2]);
        }

        [Fact]
        public void ToDictionary_keySelector_elementSelector_comparer_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name="gaofeng",Age = 32 },
                new Person() {Id=2, Name="zhangsan",Age = 25 },
            };

            var dic = persons.ToDictionary(p => p.Id, s => s.Name, EqualityComparer<int>.Default);

            Assert.Equal("gaofeng", dic[1]);
            Assert.Equal("zhangsan", dic[2]);
        }

        [Fact]
        public void ToDictionary_ArgumentException_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name="gaofeng",Age = 32 },
                new Person() {Id=1, Name="zhangsan",Age = 25 },
            };

            Assert.Throws<ArgumentException>(() => {
                persons.ToDictionary(p => p.Id, s => s.Name);
            });
        }

        [Fact]
        public void ToDictionary_ArgumentNullException_Test()
        {
            List<Person> persons = new List<Person>()
            {
                new Person() {Id=1, Name=null,Age = 32 },
                new Person() {Id=1, Name="zhangsan",Age = 25 },
            };

            Assert.Throws<ArgumentNullException>(() => {
                persons.ToDictionary(p => p.Name, s => s.Name);
            });
        }

        [Fact]
        public void ToDictionary_DataSource_NullException_Test()
        {
            List<Person> persons = null;

            Assert.Throws<ArgumentNullException>(() => {
                persons.ToDictionary(p => p.Name, s => s.Name);
            });
        }
        #endregion

        #region ToLookup

        /// <summary>
        /// ToLookup:根据指定的键选择器函数从IEnumerable<T>创建Lookup<T>
        /// Lookup<T>对象表示一个一对多的字典,定义了一个由键和序列组成的元组,类似GroupJoin返回的结果。
        /// </summary>
        [Fact]
        public void ToLookup_Test()
        {
            var persons = new List<Employee>()
            {
                new Employee(){ Id=1, Name="gaofeng",Emails=new List<string>(){"a1@163.com","a2@163.com","a3@163.com"} },
                new Employee(){ Id=1, Name="zhangsan",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } },

                new Employee(){ Id=2, Name="gaofeng2",Emails=new List<string>(){"c1@163.com","c2@163.com","c3@163.com"} },
                new Employee(){ Id=2, Name="zhangsan2",Emails=new List<string>(){ "d1@163.com", "d2@163.com", "d3@163.com" } },
            };

            var lookup = persons.ToLookup(q => q.Id);
            var group2 = lookup[2];
            var group2_count = group2.Count();

            Assert.Equal(2,group2_count);
        }

        [Fact]
        public void ToLookup_keySelector_comparer_Test()
        {
            var persons = new List<Employee>()
            {
                new Employee(){ Id=1, Name="gaofeng",Emails=new List<string>(){"a1@163.com","a2@163.com","a3@163.com"} },
                new Employee(){ Id=1, Name="zhangsan",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } }
            };

            var lookup = persons.ToLookup(q => q.Id,EqualityComparer<int>.Default);
            var group1_firstEmail = lookup[1].FirstOrDefault().Emails.FirstOrDefault() ;

            Assert.Equal("a1@163.com", group1_firstEmail);
        }

        [Fact]
        public void ToLookup_keySelector_elementSelector_Test()
        {
            var persons = new List<Employee>()
            {
                new Employee(){ Id=1, Name="gaofeng",Emails=new List<string>(){"a1@163.com","a2@163.com","a3@163.com"} },
                new Employee(){ Id=1, Name="zhangsan",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } }
            };

            var lookup = persons.ToLookup(q => q.Id, e=>e.Emails);
            var group1_Emails = lookup[1].FirstOrDefault();
            var group1_Emails_first = group1_Emails.FirstOrDefault();

            Assert.Equal("a1@163.com", group1_Emails_first);
        }

        [Fact]
        public void ToLookup_keySelector_elementSelector_comparer_Test()
        {
            var persons = new List<Employee>()
            {
                new Employee(){ Id=1, Name="gaofeng",Emails=new List<string>(){"a1@163.com","a2@163.com","a3@163.com"} },
                new Employee(){ Id=1, Name="zhangsan",Emails=new List<string>(){ "b1@163.com", "b2@163.com", "b3@163.com" } }
            };

            var lookup = persons.ToLookup(q => q.Name, e => e.Emails,EqualityComparer<string>.Default);
            var group1_Emails = lookup["gaofeng"].FirstOrDefault();
            var group1_Emails_first = group1_Emails.FirstOrDefault();

            Assert.Equal("a1@163.com", group1_Emails_first);
        }
        #endregion

        #region private

        private DataTable GetDataTable()
        {

            DataTable dt = new DataTable();
            DataColumn col_id = new DataColumn("Id");
            col_id.AllowDBNull = false;
            col_id.AutoIncrementSeed = 1;
            col_id.AutoIncrement = true;
            col_id.AutoIncrementStep = 1;
            col_id.DataType = typeof(int);

            DataColumn col_name = new DataColumn("Name");
            col_name.AllowDBNull = true;
            col_name.AutoIncrement = false;
            col_name.DataType = typeof(string);
            col_name.DefaultValue = string.Empty;

            DataColumn col_age = new DataColumn("Age");
            col_age.AllowDBNull = false;
            col_age.AutoIncrement = false;
            col_age.DataType = typeof(int);
            col_age.DefaultValue = 20;

            dt.Columns.Add(col_id);
            dt.Columns.Add(col_name);
            dt.Columns.Add(col_age);

            var row_one = dt.NewRow();
            row_one[1] = "王高峰";
            row_one[2] = 21;

            var row_2 = dt.NewRow();
            row_2[1] = "王向前";
            row_2[2] = 32;

            dt.Rows.Add(row_one);
            dt.Rows.Add(row_2);

            return dt;
        }

        #endregion
    }
}