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

namespace LinqStudy.Test.LinqToObject
{
    /// <summary>
    /// Sets 集合操作符
    /// 注意:集合操作为 “延迟执行”,数据源为null时均引发异常。
    /// </summary>
    public class SetsTest
    {
        #region Distinct

        /// <summary>
        /// Distinct:元素去重;延迟执行。
        /// 注意:值类型可以直接去重;引用类型比较的是引用而不是对象本身的属性等,可以提供自定义比较器。
        /// </summary>
        [Fact]
        public void Distinct_Test()
        {
            List<int> array = new List<int>() { 1, 2, 2, 3, 3, 3 };

            var distinctItem = array.Distinct();
            var expected = new List<int>() { 1, 2, 3 };

            Assert.Equal(expected, distinctItem);
        }

        /// <summary>
        /// 引用类型:剔除同一个引用的重复项。
        /// </summary>
        [Fact]
        public void Distinct_Reference_Yes_Test()
        {
            var p1 = new Person() { Id = 2, Name = "小龙女", Age = 28 };
            var p2 = new Person() { Id = 4, Name = "杨过", Age = 32 };

            List<Person> peoples = new List<Person>();
            peoples.Add(p1);
            peoples.Add(p2);

            //添加对同一对象的多次引用
            peoples.Add(p1);
            peoples.Add(p2);

            var totalCount = peoples.Count();
            var distinctCount = peoples.Distinct().Count();

            Assert.Equal(4, totalCount);
            Assert.Equal(2, distinctCount);
        }

        [Fact]
        public void Distinct_Reference_No_Test()
        {

            List<Person> peoples = new List<Person>()
            {
                new Person() { Id = 1, Name = "小龙女", Age = 28 },
                new Person() { Id = 1, Name = "小龙女", Age = 28 },
            };

            //虽然1、2两项对象属性完命相同,但是两个不同的对象(new两次),所以不是“重复项”
            //去重后仍然是2项
            var distinctCount = peoples.Distinct().Count();

            Assert.Equal(2, distinctCount);
        }

        /// <summary>
        /// 延迟执行
        /// </summary>
        [Fact]
        public void Distinct_Daley_Test()
        {
            List<int> array = new List<int>() { 1, 2, 2, 3, 3, 3 };

            var distinctItem = array.Distinct();

            array.Add(5);

            var expected = new List<int>() { 1, 2, 3, 5 };

            Assert.Equal(expected, distinctItem);
        }

        /// <summary>
        /// 重载:自定义比较器
        /// </summary>
        [Fact]
        public void Distinct_compare_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id=1, Name="小龙女", Age=28},
                new Person(){ Id=1, Name="我不是龙女", Age=68},
            };

            var distinctItem = peoples.Distinct(new PersonEqualityComparerById());

            //虽然两项只有Id属性相等,其它属性均不同;
            //但是因为自定义比较器内部,只比较Id即只要Id相同就认为同一对象。
            //所以去重后,只有一项(第一项,第二项被认为是重复项,被去除)。
            var distinctCount = distinctItem.Count();

            var expected = 1;
            Assert.Equal(expected, distinctCount);
        }
        #endregion

        #region Union

        ///<summary>
        /// Union:取两个序列的并集,并去除重复项。
        /// 与SQL比较相似;Concat不去除重复项。
        ///</summary>
        [Fact]
        public void Union_Test()
        {
            var p1 = new Person() { Id = 1, Name = "杨过", Age = 32 };
            var p2 = new Person() { Id = 2, Name = "小龙女", Age = 28 };
            var p3 = new Person() { Id = 3, Name = "银花公主", Age = 16 };

            List<Person> peoples1 = new List<Person>()
            {
                p1,
                p2,
            };
            List<Person> peoples2 = new List<Person>()
            {
                p1,
                p3,
            };

            var act = peoples1.Union(peoples2);

            //因为"杨过"是重复的,所有总共有3项
            var totalCount = act.Count();

            Assert.Equal(3, totalCount);
        }

        ///<summary>
        /// Union: 对同一数据源两次查询后,两次结果Union
        /// 注意:同一数据源多次查询形成的序列项,均是对同一对象的引用。
        ///      项里保存的是引用地址,非对象本身。
        ///</summary>
        [Fact]
        public void Union_UseQuery_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id=1, Name="张三丰", Age=230},
                new Person(){ Id=2, Name="小龙女", Age=28},
                new Person(){ Id=3, Name="银花公主", Age=16},
                new Person(){ Id=4, Name="杨过", Age=32},
            };

            var peoples1 = peoples.Where(q => q.Id <= 3);
            var peoples2 = peoples.Where(q => q.Age < 230);

            var act = peoples1.Union(peoples2);

            //前3项 + 后3项- 重复2项=4项
            var totalCount = act.Count();

            Assert.Equal(4, totalCount);
        }

        ///<summary>
        /// Union重载: camparer
        ///</summary>
        [Fact]
        public void Union_camparer_Test()
        {
            var p1 = new Person() { Id = 1, Name = "杨过", Age = 32 };
            var p2 = new Person() { Id = 2, Name = "小龙女", Age = 28 };
            var p3 = new Person() { Id = 2, Name = "我非龙女", Age = 77 };

            List<Person> peoples1 = new List<Person>()
            {
                p1,
                p2,
            };
            List<Person> peoples2 = new List<Person>()
            {
                p1,
                p3,
            };

            var act = peoples1.Union
            (
                peoples2,
                new PersonEqualityComparerById()
            )
            .ToList();

            //虽然P2和p3只有Id相同,其它不同;但自定义比较只看Id,所以P2和p3被认为是同一个对象。
            //结果集为:p1+p2
            var totalCount = act.Count();

            Assert.Equal(2, totalCount);
        }

        ///<summary>
        /// 异常:ArgumentNullException
        ///</summary>
        [Fact]
        public void Union_ArgumentNullException_Test()
        {
            List<Person> peoples1 = null;
            List<Person> peoples2 = null;

            Action act = () => peoples1.Union
             (
                 peoples2
             );

            Assert.Throws<ArgumentNullException>(act);
        }
        #endregion
    }
}