using DeviceRepair.Models.Attr;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Reflection;

namespace DeviceRepair.Utils
{
    /// <summary>
    /// 实体对象<->数据集转换帮助类
    /// </summary>
    public static class DTOHelper<TData> where TData : class, new()
    {
        #region Methods

        #region 实体对象->DataSet数据集
        private static void AddColumnsByDTOProperty(PropertyInfo pi, ref DataTable dtResult)
        {
            if (dtResult == null)
            {
                dtResult = new DataTable();
            }
            if (dtResult.Columns.Contains(pi.Name))
            {
                return;
            }
            Type colType = pi.PropertyType;

            #region 查看类型是否需要转换 Myl20190801

            object[] objBoolConverter = pi.GetCustomAttributes(typeof(BoolConverterAttribute), true);
            if (objBoolConverter != null && objBoolConverter.Length > 0)
            {
                colType = ((BoolConverterAttribute)objBoolConverter[0]).ConvertType;
            }

            #endregion

            if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                if (colType.GetGenericArguments()[0].IsEnum)
                {
                    //V1.0.1
                    colType = typeof(string);
                    //V1.0.1
                }
                else
                {
                    colType = colType.GetGenericArguments()[0];
                }
            }
            else if (colType.IsEnum)
            {
                colType = typeof(string);
            }

            dtResult.Columns.Add(new DataColumn(pi.Name, colType));
        }

        public static DataSet ConvertDTOToDataSet<T>(IList<T> sourceList, params string[] propertyNameArray) where T : class
        {
            if (sourceList == null || sourceList.Count == 0)
            {
                return null;
            }

            List<string> propertyNameList = new List<string>();
            if (propertyNameArray != null)
            {
                propertyNameList.AddRange(propertyNameArray);
            }
            DataSet dsResult = new DataSet("Datas");
            DataTable dtResult = new DataTable();

            Type[] typeArr = sourceList.GetType().GetGenericArguments();
            if (typeArr.Length == 0)
            {
                return dsResult;
            }

            #region 过滤DomIgnore特性的属性 Myl20180206

            PropertyInfo[] domProperties = typeArr[0].GetProperties();
            IEnumerable<PropertyInfo> domDomNotIngoredPropertyList =
            domProperties.Where<System.Reflection.PropertyInfo>((A) =>
            {
                return A.GetCustomAttributes(typeof(DomFieldIgnoreAttribute), true).Length == 0;
            });

            #endregion

            dtResult.TableName = typeArr[0].Name;
            foreach (PropertyInfo pi in domDomNotIngoredPropertyList)
            {
                if (propertyNameList.Count == 0)
                {
                    //dtResult.Columns.Add(pi.Name, pi.PropertyType);

                    AddColumnsByDTOProperty(pi, ref dtResult);
                }
                else
                {
                    if (propertyNameList.Contains(pi.Name))
                    {
                        AddColumnsByDTOProperty(pi, ref dtResult);
                    }
                }
            }
            for (int i = 0; i < sourceList.Count; i++)
            {
                ArrayList tempList = new ArrayList();
                foreach (PropertyInfo pi in domDomNotIngoredPropertyList)
                {
                    #region Bool类型转换 Myl190801

                    object[] objBoolConverter = pi.GetCustomAttributes(typeof(BoolConverterAttribute), true);
                    Type boolConvertType = null;
                    if (objBoolConverter != null && objBoolConverter.Length > 0)
                    {
                        boolConvertType = ((BoolConverterAttribute)objBoolConverter[0]).ConvertType;
                    }

                    #endregion

                    if (propertyNameList.Count == 0)
                    {
                        object obj = pi.GetValue(sourceList[i], null);
                        //V1.0.1
                        if (pi.PropertyType.IsEnum)
                        {
                            if (obj != null)
                            {
                                obj = ((int)Enum.Parse(pi.PropertyType, obj.ToString())).ToString();
                            }
                        }
                        //V1.0.1
                        #region Bool类型转换 Myl190801

                        if (pi.PropertyType == typeof(bool))
                        {
                            if (boolConvertType != null)
                            {
                                if (obj != null)
                                {
                                    obj = Convert.ChangeType(bool.Parse(obj.ToString()) ? ((BoolConverterAttribute)objBoolConverter[0]).BoolString[0] : ((BoolConverterAttribute)objBoolConverter[0]).BoolString[1], boolConvertType);
                                }
                            }
                        }
                        #endregion

                        tempList.Add(obj);
                    }
                    else
                    {
                        if (propertyNameList.Contains(pi.Name))
                        {
                            object obj = pi.GetValue(sourceList[i], null);

                            //V1.0.1
                            if (pi.PropertyType.IsEnum)
                            {
                                if (obj != null)
                                {
                                    obj = ((int)Enum.Parse(pi.PropertyType, obj.ToString())).ToString();
                                }
                            }
                            else
                            {
                                //V1.0.3

                                if ((pi.PropertyType.IsGenericType) && (pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                                {
                                    if (pi.PropertyType.GetGenericArguments()[0].IsEnum)
                                    {
                                        if (obj != null)
                                        {
                                            obj = ((int)Enum.Parse(pi.PropertyType.GetGenericArguments()[0], obj.ToString())).ToString();
                                        }
                                    }
                                }

                                //V1.0.3
                            }
                            //V1.0.1
                            #region Bool类型转换 Myl190801

                            if (pi.PropertyType == typeof(bool))
                            {
                                if (boolConvertType != null)
                                {
                                    if (obj != null)
                                    {
                                        obj = Convert.ChangeType(bool.Parse(obj.ToString()) ? ((BoolConverterAttribute)objBoolConverter[0]).BoolString[0] : ((BoolConverterAttribute)objBoolConverter[0]).BoolString[1], boolConvertType);
                                    }
                                }
                            }

                            #endregion

                            tempList.Add(obj);
                        }
                    }
                }
                object[] array = tempList.ToArray();
                dtResult.LoadDataRow(array, true);
            }
            dsResult.Tables.Add(dtResult);
            return dsResult;
        }

        public static DataSet ConvertDTOToDataSet(IList<TData> sourceList, params string[] propertyNameArray)
        {
            return ConvertDTOToDataSet<TData>(sourceList, propertyNameArray);
        }

        public static DataSet ConvertSingleDTOToDataSet(TData sourceData, params string[] propertyNameArray)
        {
            return ConvertDTOToDataSet(new List<TData>() { sourceData }, propertyNameArray);
        }
        #endregion

        #region DataTable数据集->实体对象集合

        #region DataTable利用泛型填充实体类
        /// <summary>  
        /// DataTable利用泛型填充实体类
        /// </summary>  
        /// <typeparam name="U">实体类类型</typeparam>  
        /// <param name="dtData">待填充的DataTable</param>  
        /// <returns>
        /// 返回填充完的实体类对象集合
        /// </returns>  
        public static IList<U> DataTableToList<U>(DataTable dtData) where U : class
        {
            IList<U> listObject = new List<U>();
            U t = default(U);
            PropertyInfo[] propertypes = null;
            string tempColumnName = string.Empty;
            foreach (DataRow eachRow in dtData.Rows)
            {
                t = Activator.CreateInstance<U>();
                propertypes = t.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                foreach (PropertyInfo eachProperty in propertypes)
                {
                    if (!eachProperty.CanWrite)
                    {
                        //Myl - 20190122
                        continue;
                    }

                    #region 优化类型转换问题 Myl - 20190122
                    tempColumnName = eachProperty.Name;
                    if (dtData.Columns.Contains(tempColumnName))
                    {
                        #region 添加数值字段的小数位数自动截取 Myl - 20190128

                        int? iDigits = null;
                        object[] objNumberDigits = eachProperty.GetCustomAttributes(typeof(NumberDigitsAttribute), true);
                        if (objNumberDigits != null && objNumberDigits.Length > 0)
                        {
                            iDigits = ((NumberDigitsAttribute)objNumberDigits[0]).Digits;
                        }

                        #endregion

                        object value = Convert.IsDBNull(eachRow[tempColumnName]) ? null : iDigits == null ? eachRow[tempColumnName] : Math.Round(decimal.Parse(eachRow[tempColumnName].ToString()), iDigits.Value);
                        if (value != null)
                        {
                            if (!eachProperty.PropertyType.IsGenericType)
                            {
                                //非泛型

                                if (eachProperty.PropertyType.IsEnum)
                                {
                                    //赋值枚举 20210304 Myl Add
                                    eachProperty.SetValue(t, string.IsNullOrEmpty(value.ToString().Trim()) ? null : Enum.Parse(eachProperty.PropertyType, value.ToString().Trim()), null);
                                }
                                else if (eachProperty.PropertyType == typeof(string))
                                {
                                    eachProperty.SetValue(t, string.IsNullOrEmpty(value.ToString().Trim()) ? null : Convert.ChangeType(value.ToString().Trim(), eachProperty.PropertyType, null), null);
                                }
                                else
                                {
                                    eachProperty.SetValue(t, string.IsNullOrEmpty(value.ToString().Trim()) ? null : Convert.ChangeType(value, eachProperty.PropertyType, null), null);
                                }
                            }
                            else
                            {
                                //泛型Nullable<>
                                Type genericTypeDefinition = eachProperty.PropertyType.GetGenericTypeDefinition();
                                if (genericTypeDefinition == typeof(Nullable<>))
                                {
                                    if (eachProperty.PropertyType.GetGenericArguments().Count() > 0 && eachProperty.PropertyType.GetGenericArguments()[0].IsEnum)
                                    {
                                        //赋值枚举 20210304 Myl Add
                                        eachProperty.SetValue(t, string.IsNullOrEmpty(value.ToString().Trim()) ? null : Enum.Parse(eachProperty.PropertyType.GetGenericArguments()[0], value.ToString().Trim()), null);
                                    }
                                    else
                                    {
                                        eachProperty.SetValue(t, string.IsNullOrEmpty(value.ToString().Trim()) ? null : Convert.ChangeType(value, Nullable.GetUnderlyingType(eachProperty.PropertyType), null), null);
                                    }
                                }
                            }
                        }
                    }
                    #endregion
                }
                listObject.Add(t);
            }
            return listObject;
        }
        #region DataTable利用泛型填充实体类
        /// <summary>
        /// DataTable利用泛型填充实体类
        /// </summary>
        /// <param name="dtData">待填充的DataTable</param>
        /// <returns></returns>
        public static IList<TData> DataTableToList(DataTable dtData)
        {
            return DataTableToList<TData>(dtData);
        }

        /// <summary>
        /// DataTable利用泛型填充实体类，并制定特定的筛选条件
        /// Myl 20210310
        /// </summary>
        /// <param name="dtData">待填充的DataTable</param>
        /// <param name="filterString">筛选条件</param>
        /// <returns></returns>
        public static IList<TData> DataTableToList(DataTable dtData, string filterString)
        {
            if (!string.IsNullOrEmpty(filterString))
            {
                DataTable dtFilter = dtData.Clone();
                var vFilterRows = dtData.Select(filterExpression: filterString);
                foreach (var eachFilterRow in vFilterRows)
                {
                    DataRow drFilterNewRow = dtFilter.NewRow();
                    drFilterNewRow.ItemArray = eachFilterRow.ItemArray;
                    dtFilter.Rows.Add(drFilterNewRow);
                }
                return DataTableToList(dtFilter);
            }
            else
            {
                return DataTableToList(dtData);
            }
        }

        #endregion

        #endregion

        public static TData ToEntity(DataRow adaptedRow)
        {
            if (adaptedRow == null)
            {
                return default(TData);
            }
            TData entity = Activator.CreateInstance<TData>();
            CopyToEntity(entity, adaptedRow);
            return entity;
        }

        public static object ToEntity(DataRow adaptedRow, Type entityType)
        {
            if (entityType == null || adaptedRow == null)
            {
                return null;
            }
            object entity = Activator.CreateInstance(entityType);
            CopyToEntity(entity, adaptedRow);
            return entity;
        }

        public static T ToEntity<T>(DataRow adaptedRow, T value) where T : new()
        {
            T item = new T();
            if (value == null || adaptedRow == null)
            {
                return item;
            }
            item = Activator.CreateInstance<T>();
            CopyToEntity(item, adaptedRow);

            return item;
        }

        public static TData ToEntity(DataRow adaptedRow, TData value)
        {
            return ToEntity<TData>(adaptedRow, value);
        }

        public static void CopyToEntity(object entity, DataRow adaptedRow)
        {
            if (entity == null || adaptedRow == null)
            {
                return;
            }
            PropertyInfo[] propertyInfos = entity.GetType().GetProperties();

            foreach (PropertyInfo propertyInfo in propertyInfos)
            {
                if (!CanSetPropertyValue(propertyInfo, adaptedRow))
                {
                    continue;
                }

                try
                {
                    if (adaptedRow[propertyInfo.Name] is DBNull)
                    {
                        propertyInfo.SetValue(entity, null, null);
                        continue;
                    }
                    SetPropertyValue(entity, adaptedRow, propertyInfo);
                }
                finally
                {

                }
            }
        }

        private static bool CanSetPropertyValue(PropertyInfo propertyInfo, DataRow adaptedRow)
        {
            if (!propertyInfo.CanWrite)
            {
                return false;
            }

            if (!adaptedRow.Table.Columns.Contains(propertyInfo.Name))
            {
                return false;
            }

            return true;
        }

        private static void SetPropertyValue(object entity, DataRow adaptedRow, PropertyInfo propertyInfo)
        {
            if (propertyInfo.PropertyType == typeof(DateTime?) ||
                  propertyInfo.PropertyType == typeof(DateTime))
            {
                DateTime date = DateTime.MaxValue;
                DateTime.TryParse(adaptedRow[propertyInfo.Name].ToString(),
                    CultureInfo.CurrentCulture, DateTimeStyles.None, out date);

                propertyInfo.SetValue(entity, date, null);
            }
            else
            {
                if (!Convert.IsDBNull(adaptedRow[propertyInfo.Name]))
                {
                    #region Myl20210514

                    if (!propertyInfo.PropertyType.IsGenericType)
                    {
                        //非泛型

                        if (propertyInfo.PropertyType.IsEnum)
                        {
                            //赋值枚举 20210304 Myl Add
                            propertyInfo.SetValue(entity, string.IsNullOrEmpty(adaptedRow[propertyInfo.Name].ToString().Trim()) ? null : Enum.Parse(propertyInfo.PropertyType, adaptedRow[propertyInfo.Name].ToString().Trim()), null);
                        }
                        else if (propertyInfo.PropertyType == typeof(string))
                        {
                            propertyInfo.SetValue(entity, string.IsNullOrEmpty(adaptedRow[propertyInfo.Name].ToString().Trim()) ? null : Convert.ChangeType(adaptedRow[propertyInfo.Name].ToString().Trim(), propertyInfo.PropertyType, null), null);
                        }
                        else
                        {
                            propertyInfo.SetValue(entity, string.IsNullOrEmpty(adaptedRow[propertyInfo.Name].ToString().Trim()) ? null : Convert.ChangeType(adaptedRow[propertyInfo.Name], propertyInfo.PropertyType, null), null);
                        }
                    }
                    else
                    {
                        //泛型Nullable<>
                        Type genericTypeDefinition = propertyInfo.PropertyType.GetGenericTypeDefinition();
                        if (genericTypeDefinition == typeof(Nullable<>))
                        {
                            if (propertyInfo.PropertyType.GetGenericArguments().Count() > 0 && propertyInfo.PropertyType.GetGenericArguments()[0].IsEnum)
                            {
                                //赋值枚举 20210304 Myl Add
                                propertyInfo.SetValue(entity, string.IsNullOrEmpty(adaptedRow[propertyInfo.Name].ToString().Trim()) ? null : Enum.Parse(propertyInfo.PropertyType.GetGenericArguments()[0], adaptedRow[propertyInfo.Name].ToString().Trim()), null);
                            }
                            else
                            {
                                propertyInfo.SetValue(entity, string.IsNullOrEmpty(adaptedRow[propertyInfo.Name].ToString().Trim()) ? null : Convert.ChangeType(adaptedRow[propertyInfo.Name], Nullable.GetUnderlyingType(propertyInfo.PropertyType), null), null);
                            }
                        }
                    }

                    #endregion
                }
            }
        }

        #endregion

        #region V4.0 实体转换增加字段自动映射及绑定顺序 20230327

        /// <summary>
        /// DTO按照映射配置特性自动转换为DataSet数据集
        /// </summary>
        /// <typeparam name="T">泛型类型</typeparam>
        /// <param name="sourceList">数据源集合</param>
        /// <returns></returns>
        public static DataSet DTOToDataSetAutoConvertByAttribute<T>(IList<T> sourceList) where T : class
        {
            if (sourceList == null || sourceList.Count == 0)
            {
                return null;
            }


            DataSet dsResult = new DataSet("Datas");
            DataTable dtResult = new DataTable();

            Type[] typeArr = sourceList.GetType().GetGenericArguments();

            if (typeArr.Length == 0)
            {
                return dsResult;
            }

            #region 过滤DomIgnore特性的属性 Myl20180206

            #region 获取需要转换DOM字段的属性 20230327

            PropertyInfo[] domProperties = typeArr[0].GetProperties();
            IEnumerable<PropertyInfo> domDomNotIngoredPropertyList =
            domProperties.Where<System.Reflection.PropertyInfo>((A) =>
            {
                return A.GetCustomAttributes(typeof(DomFieldMapAttribute), true).Length > 0;
            });
            if (domDomNotIngoredPropertyList == null || domDomNotIngoredPropertyList.Count() == 0)
            {
                throw new Exception("对象申明中未配置需要字段映射的特性!");
            }
            #endregion

            #endregion

            dtResult.TableName = typeArr[0].Name;

            foreach (PropertyInfo pi in domDomNotIngoredPropertyList)
            {
                object[] objFieldMapConverter = pi.GetCustomAttributes(typeof(DomFieldMapAttribute), true);
                if (objFieldMapConverter != null && objFieldMapConverter.Length > 0)
                {
                    var vMapAttr = (DomFieldMapAttribute)objFieldMapConverter[0];
                    AddColumnsWithColumnNameByDTOProperty(pi, vMapAttr.MappedFieldName, vMapAttr.MappedFieldSequence, ref dtResult);
                }
            }

            for (int i = 0; i < sourceList.Count; i++)
            {
                ArrayList tempList = new ArrayList();
                foreach (PropertyInfo pi in domDomNotIngoredPropertyList)
                {
                    #region Bool类型转换 Myl190801

                    object[] objBoolConverter = pi.GetCustomAttributes(typeof(BoolConverterAttribute), true);
                    Type boolConvertType = null;
                    if (objBoolConverter != null && objBoolConverter.Length > 0)
                    {
                        boolConvertType = ((BoolConverterAttribute)objBoolConverter[0]).ConvertType;
                    }

                    #endregion

                    object obj = pi.GetValue(sourceList[i], null);

                    //V1.0.1
                    if (pi.PropertyType.IsEnum)
                    {
                        if (obj != null)
                        {
                            obj = ((int)Enum.Parse(pi.PropertyType, obj.ToString())).ToString();
                        }
                    }
                    else
                    {
                        //V1.0.3

                        if ((pi.PropertyType.IsGenericType) && (pi.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                        {
                            if (pi.PropertyType.GetGenericArguments()[0].IsEnum)
                            {
                                if (obj != null)
                                {
                                    obj = ((int)Enum.Parse(pi.PropertyType.GetGenericArguments()[0], obj.ToString())).ToString();
                                }
                            }
                        }

                        //V1.0.3
                    }
                    //V1.0.1
                    #region Bool类型转换 Myl190801

                    if (pi.PropertyType == typeof(bool))
                    {
                        if (boolConvertType != null)
                        {
                            if (obj != null)
                            {
                                obj = Convert.ChangeType(bool.Parse(obj.ToString()) ? ((BoolConverterAttribute)objBoolConverter[0]).BoolString[0] : ((BoolConverterAttribute)objBoolConverter[0]).BoolString[1], boolConvertType);
                            }
                        }
                    }

                    #endregion

                    tempList.Add(obj);
                }
                object[] array = tempList.ToArray();
                dtResult.LoadDataRow(array, true);
            }

            #region 字段排序
            foreach (PropertyInfo pi in domDomNotIngoredPropertyList)
            {
                object[] objFieldMapConverter = pi.GetCustomAttributes(typeof(DomFieldMapAttribute), true);
                if (objFieldMapConverter != null && objFieldMapConverter.Length > 0)
                {
                    var vMapAttr = (DomFieldMapAttribute)objFieldMapConverter[0];
                    dtResult.Columns[vMapAttr.MappedFieldName].SetOrdinal(vMapAttr.MappedFieldSequence);
                }
            }
            #endregion

            dsResult.Tables.Add(dtResult);
            return dsResult;
        }
        /// <summary>
        /// DTO按照映射配置特性自动转换为DataSet数据集
        /// </summary>
        /// <param name="sourceList">数据源集合</param>
        /// <returns></returns>
        public static DataSet DTOToDataSetAutoConvertByAttribute(IList<TData> sourceList)
        {
            return DTOToDataSetAutoConvertByAttribute<TData>(sourceList);
        }
        public static DataSet AutoConvertSingleDTOToDataSetByAttribute(TData sourceData)
        {
            return DTOToDataSetAutoConvertByAttribute(new List<TData>() { sourceData });
        }
        private static void AddColumnsWithColumnNameByDTOProperty(PropertyInfo pi, string cColumnName, int? iMappedSequence, ref DataTable dtResult)
        {
            if (dtResult == null)
            {
                dtResult = new DataTable();
            }
            if (string.IsNullOrEmpty(cColumnName))
            {
                cColumnName = pi.Name;
            }

            if (dtResult.Columns.Contains(cColumnName))
            {
                return;
            }
            Type colType = pi.PropertyType;

            #region 查看类型是否需要转换 Myl20190801

            object[] objBoolConverter = pi.GetCustomAttributes(typeof(BoolConverterAttribute), true);
            if (objBoolConverter != null && objBoolConverter.Length > 0)
            {
                colType = ((BoolConverterAttribute)objBoolConverter[0]).ConvertType;
            }

            #endregion

            if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
            {
                if (colType.GetGenericArguments()[0].IsEnum)
                {
                    colType = typeof(string);
                }
                else
                {
                    colType = colType.GetGenericArguments()[0];
                }
            }
            else if (colType.IsEnum)
            {
                colType = typeof(string);
            }

            dtResult.Columns.Add(new DataColumn(cColumnName, colType));


        }

        #endregion

        #endregion
    }
}
