using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Reflection;

namespace YBEE.EQM.Core
{
    public class DataCompareUtil<T1, T2>
    {
        public DataCompareUtil(
            Expression<Func<T1, object>> SelectorKey_1,
            Expression<Func<T2, object>> SelectorKey_2
            )
        {
            Propertys = new List<View_Property>();
            T_1 = typeof(T1);
            T_2 = typeof(T2);

            KeyProperty = new View_Property()
            {
                Property_1 = T_1.GetProperty(GetPropertyName(SelectorKey_1)),
                Property_2 = T_2.GetProperty(GetPropertyName(SelectorKey_2))
            };
        }

        private List<View_Property> Propertys { get; set; }

        private Type T_1 { get; set; }
        private Type T_2 { get; set; }

        private View_Property KeyProperty { get; set; }

        public void PushCompare(
            Expression<Func<T1, object>> Selector_1,
            Expression<Func<T2, object>> Selector_2
            )
        {
            Propertys.Add(new View_Property()
            {
                Property_1 = T_1.GetProperty(GetPropertyName(Selector_1)),
                Property_2 = T_2.GetProperty(GetPropertyName(Selector_2))
            });
        }

        public View_DataCompare<T1, T2> Compare(
            List<T1> Data_1,
            List<T2> Data_2
            )
        {
            View_DataCompare<T1, T2> data = new View_DataCompare<T1, T2>();

            Dictionary<string, T1> dic_Contain_2 = new Dictionary<string, T1>();
            Dictionary<string, T2> dic_Contain_1 = new Dictionary<string, T2>();

            foreach (var item_1 in Data_1)
            {
                foreach (var item_2 in Data_2)
                {
                    bool IsDifferent = false;

                    foreach (var Property_item in Propertys)
                    {
                        if (!IsDifferent)
                        {
                            if (Property_item.Property_1 == null || Property_item.Property_2 == null)
                            {
                                if (Property_item.Property_1 == null && Property_item.Property_2 == null)
                                {
                                    break;
                                }
                                else
                                {
                                    IsDifferent = true;
                                }
                            }
                            else
                            {
                                var obj_value1 = Property_item.Property_1.GetValue(item_1);
                                var obj_value2 = Property_item.Property_2.GetValue(item_2);

                                if (obj_value1 != null && obj_value2 != null)
                                {
                                    if (obj_value1 is decimal @decimal && obj_value2 is decimal @decimal2 && @decimal != @decimal2)
                                    {
                                        IsDifferent = true;
                                    }
                                    else if (obj_value1 is int @intl && obj_value2 is int @int2 && @intl != @int2)
                                    {
                                        IsDifferent = true;
                                    }
                                    else if (obj_value1 is Guid @Guidl && obj_value2 is Guid @Guid2 && @Guidl != @Guid2)
                                    {
                                        IsDifferent = true;
                                    }
                                    else if (obj_value1 is DateTime @DateTimel && obj_value2 is DateTime @DateTime2 && @DateTimel != @DateTime2)
                                    {
                                        IsDifferent = true;
                                    }
                                    else
                                    {
                                        if (obj_value1.ToString() != obj_value2.ToString())
                                            IsDifferent = true;
                                    }
                                }
                                else if (obj_value1 != null || obj_value2 != null)
                                {
                                    IsDifferent = true;
                                }
                            }
                        }
                        else
                        {
                            break;
                        }
                    }

                    if (!IsDifferent)
                    {
                        //item_1与item_2相同
                        string key_1 = KeyProperty.Property_1.GetValue(item_1).ToString();

                        if (!dic_Contain_2.ContainsKey(key_1))
                        {
                            dic_Contain_2.Add(key_1, item_1);
                            data.Contain_2.Add(item_1);
                        }

                        string key_2 = KeyProperty.Property_2.GetValue(item_2).ToString();

                        if (!dic_Contain_1.ContainsKey(key_2))
                        {
                            dic_Contain_1.Add(key_2, item_2);
                            data.Contain_1.Add(item_2);
                        }
                    }
                }
            }

            foreach (var item_1 in Data_1)
            {
                string key_1 = KeyProperty.Property_1.GetValue(item_1).ToString();

                if (!dic_Contain_2.ContainsKey(key_1))
                {
                    data.NoContain_2.Add(item_1);
                }
            }

            foreach (var item_2 in Data_2)
            {
                string key_2 = KeyProperty.Property_2.GetValue(item_2).ToString();

                if (!dic_Contain_1.ContainsKey(key_2))
                {
                    data.NoContain_1.Add(item_2);
                }
            }

            return data;
        }

        public static string GetPropertyName<T>(Expression<Func<T, object>> expr)
        {
            var rtn = "";
            if (expr.Body is UnaryExpression expression)
            {
                rtn = ((MemberExpression)expression.Operand).Member.Name;
            }
            else if (expr.Body is MemberExpression expression2)
            {
                rtn = expression2.Member.Name;
            }
            else if (expr.Body is ParameterExpression expression1)
            {
                rtn = expression1.Type.Name;
            }
            return rtn;
        }
    }

    public class View_DataCompare<T1, T2>
    {
        public View_DataCompare()
        {
            Contain_2 = new List<T1>();
            Contain_1 = new List<T2>();
            NoContain_2 = new List<T1>();
            NoContain_1 = new List<T2>();
        }

        /// <summary>
        /// 2号数据源中存在
        /// </summary>
        public List<T1> Contain_2 { get; set; }

        /// <summary>
        /// 1号数据源中存在
        /// </summary>
        public List<T2> Contain_1 { get; set; }

        /// <summary>
        /// 2号数据源中不存在
        /// </summary>
        public List<T1> NoContain_2 { get; set; }

        /// <summary>
        /// 1号数据源中不存在
        /// </summary>
        public List<T2> NoContain_1 { get; set; }
    }

    public class View_Property
    {
        public PropertyInfo Property_1 { get; set; }
        public PropertyInfo Property_2 { get; set; }
    }
}