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

using Xunit;
using FluentAssertions;


namespace LinqStudy.Test.LinqToObject
{
    /// <summary>
    ///  量词操作符
    /// </summary>
    public class QuantifierTest
    {
        #region Any

        /// <summary>
        /// Andy:序列中是否存在任一满足条件的项;为加快判断速度,立即执行
        /// </summary>
        [Fact]
        public void Any_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "西站热",Age = 66 },
                new Person(){ Id = 3, Name = "西门吹雪",Age = 45 },
            };

            var anyQuery = peoples.Any(q => q.Name.StartsWith("西"));

            Assert.True(anyQuery);
        }

        /// <summary>
        /// 立即执行
        /// </summary>
        [Fact]
        public void Any_Immediately_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "东方不败",Age = 66 },
                new Person(){ Id = 3, Name = "西门吹雪",Age = 45 },
            };

            //查询
            var anyQuery = peoples.Any(q => q.Id >= 4);

            //查询后添加满足条件的项
            peoples.Add(new Person { Id = 5, Name = "小东", Age = 44 });

            //结果
            var delayResult = anyQuery;
            //断言
            Assert.False(delayResult);
        }

        /// <summary>
        /// 重载方法:查询表达式
        /// </summary>
        [Fact]
        public void Any_Overload_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "东方不败",Age = 66 },
                new Person(){ Id = 3, Name = "西门吹雪",Age = 45 },
            };

            var anyQuery = peoples.Any(q => q.Id >= 2);

            Assert.True(anyQuery);
        }

        /// <summary>
        ///  ArgumentNullException异常
        /// </summary>
        [Fact]
        public void Any_ArgumentNullException_Test()
        {
            List<Person> peoples =null;

            Action anyQueryAction =() => peoples.Any(q => q.Id >= 2);

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

        #region All

        /// <summary>
        /// All:序列中是否所有项都满足条件;
        /// 立即执行
        /// </summary>
        [Fact]
        public void All_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "王小庆",Age = 66 },
                new Person(){ Id = 3, Name = "西门之王",Age = 45 },
            };

            var anyQuery = peoples.All(q => q.Name.Contains("王"));

            Assert.True(anyQuery);
        }

        /// <summary>
        /// 立即执行
        /// </summary>
        [Fact]
        public void All_Immediately_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "王小庆",Age = 66 },
                new Person(){ Id = 3, Name = "西门",Age = 45 },
            };

            //查询
            var anyQuery = peoples.All(q => q.Id >= 4);

            //查询后设置满足条件的项
            peoples[2].Name += "之王";

            //结果
            var delayResult = anyQuery;

            //断言
            Assert.False(delayResult);
        }

        /// <summary>
        /// 重载方法:查询表达式
        /// </summary>
        [Fact]
        public void All_Overload_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "东方不败",Age = 66 },
                new Person(){ Id = 3, Name = "西门吹雪",Age = 45 },
            };

            var anyQuery = peoples.All(q => q.Name.Length >= 2);

            Assert.True(anyQuery);
        }

        /// <summary>
        ///  ArgumentNullException异常
        /// </summary>
        [Fact]
        public void All_ArgumentNullException_Test()
        {
            List<Person> peoples = null;

            Action anyQueryAction = () => peoples.All(q => q.Id >= 2);

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

        #region Contains

        /// <summary>
        /// Contains:序列中是否包含指定的项;
        /// 立即执行
        /// </summary>
        [Fact]
        public void Contains_Test()
        {
            List<int> arr = new List<int>() {1,2,3,4,5 };

            var anyQuery = arr.Contains(2);

            Assert.True(anyQuery);
        }

        /// <summary>
        /// 不包含时,返回false
        /// </summary>
        [Fact]
        public void Contains_No_Test()
        {
            List<int> arr = new List<int>() { 1, 2, 3, 4, 5 };

            var anyQuery = arr.Contains(100);

            Assert.False(anyQuery);
        }

        /// <summary>
        /// 引用类型的包含,指引用的对象是同一个对象。
        /// 属性值全部相等,但引用地址不同,则不包含。
        /// </summary>
        [Fact]
        public void Contains_Reference_No_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "王小庆",Age = 66 },
                new Person(){ Id = 3, Name = "西门之王",Age = 45 },
            };

            var current = new Person() { Id = 1, Name = "王小明", Age = 10 };
            var anyQuery = peoples.Contains(current);

            Assert.False(anyQuery);
        }

        /// <summary>
        /// 引用类型,包含:引用地址相同。
        /// </summary>
        [Fact]
        public void Contains_Reference_Yes_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "王小庆",Age = 66 },
                new Person(){ Id = 3, Name = "西门之王",Age = 45 },
            };

            var queryItem = peoples.First();
            var isContain = peoples.Contains(queryItem);

            Assert.True(isContain);
        }

        /// <summary>
        /// 立即执行
        /// </summary>
        [Fact]
        public void Contains_Immediately_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "王小庆",Age = 66 },
                new Person(){ Id = 3, Name = "西门",Age = 45 },
            };

            var addItem = new Person() { Id = 4, Name = "张三", Age = 33 };

            //不包含指定项
            var containQuery = peoples.Contains(addItem);

            //操作后,添加到集合,此时应该包含
            peoples.Add(addItem);

            //断言:不包含
            //因为添加前,已经立即执行,保留结果;后面再添加项,不影响以前的执行结果。
            Assert.False(containQuery);
        }

        /// <summary>
        /// 重载方法:使用自定义比较器
        /// 虽然大部分属性都不一样,但是比较器认为Id属性相同即相等,Contains就认为包含。
        /// </summary>
        [Fact]
        public void Contains_Overload_Test()
        {
            List<Person> peoples = new List<Person>()
            {
                new Person(){ Id = 1, Name = "王小明",Age = 10 },
                new Person(){ Id = 2, Name = "东方不败",Age = 66 },
                new Person(){ Id = 3, Name = "西门吹雪",Age = 45 },
            };

            var people = new Person() { Id = 3, Name = "小清闲", Age = 88 };

            //自定义比较器:Id相同即为同一个对象
            PersonEqualityComparerById equalId = new PersonEqualityComparerById();
            var anyQuery = peoples.Contains(people, new PersonEqualityComparerById());

            //比较器相等,即包含
            Assert.True(anyQuery);
        }

        /// <summary>
        ///  NullReferenceException 异常
        /// </summary>
        [Fact]
        public void Contains_NullReferenceException_Test()
        {
            List<Person> peoples = null;

            var people = new Person() { Id = 3, Name = "小清闲", Age = 88 };

            Action anyQueryAction = () => peoples.Contains(people);

            Assert.Throws<NullReferenceException>(anyQueryAction);
        }
        #endregion
    }
}