/******************************************************************************
 * SunnyUI 开源控件库、工具类库、扩展类库、多页面开发框架。
 * CopyRight (C) 2012-2022 ShenYongHua(沈永华).
 * QQ群：56829229 QQ：17612584 EMail：SunnyUI@QQ.Com
 *
 * Blog:   https://www.cnblogs.com/yhuse
 * Gitee:  https://gitee.com/yhuse/SunnyUI
 * GitHub: https://github.com/yhuse/SunnyUI
 *
 * SunnyUI.dll can be used for free under the GPL-3.0 license.
 * If you use this code, please keep this note.
 * 如果您使用此代码，请保留此说明。
 ******************************************************************************
 * 文件名称: UMapper.cs
 * 文件说明: 轻量级的对象映射框架，可以映射值类型（包括Struct），和以值类型构成的List和数组。
 * 当前版本: V3.1
 * 创建日期: 2021-09-30
 *
 * 2021-09-30: V3.0.7 增加文件说明
******************************************************************************/

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

namespace Sunny.UI
{
    public static class Mapper
    {
        private static void Execute<T1, T2>(T1 source, T2 dest)
            where T1 : class
            where T2 : class
        {
            if (source == null || dest == null)
            {
                return;
            }

            var listSource = source.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);
            var listDest = dest.GetType().GetNeedProperties().ToDictionary(prop => prop.Name);

            foreach (var item in listDest)
            {
                if (listSource.NotContainsKey(item.Key)) continue;

                var sourceInfo = listSource[item.Key];
                Type sourceType = sourceInfo.PropertyType;
                object sourceValue = sourceInfo.GetValue(source, null);

                var destInfo = item.Value;
                Type destType = item.Value.PropertyType;

                if (!sourceType.Equals(destType)) continue;
                if (sourceType.IsValueType)
                {
                    //Console.WriteLine("ValueType: " + item.Key + ", " + sourceType.FullName);
                    destInfo.SetValue(dest, sourceValue, null);
                }
                else
                {
                    if (sourceType == typeof(string))
                    {
                        //Console.WriteLine("String: " + item.Key + ", " + sourceType.FullName);
                        destInfo.SetValue(dest, sourceValue, null);
                        continue;
                    }

                    if (sourceType.IsList())
                    {
                        //Console.WriteLine("List: " + item.Key + ", " + sourceType.FullName);
                        Type[] sourceTypes = sourceType.GetGenericArguments();
                        Type[] destTypes = destType.GetGenericArguments();
                        if (sourceTypes.Length != 1) continue;
                        if (destTypes.Length != 1) continue;
                        if (!sourceTypes[0].Equals(destTypes[0])) continue;

                        if (sourceValue == null)
                        {
                            destInfo.SetValue(dest, null, null);
                        }
                        else
                        {
                            Type typeDataList = typeof(List<>).MakeGenericType(destTypes[0]);
                            MethodInfo AddInfo = typeDataList.GetMethod("Add", BindingFlags.Public | BindingFlags.Instance);
                            if (AddInfo == null) continue;

                            if (sourceTypes[0].IsValueType || sourceTypes[0] == typeof(string))
                            {
                                object listvalue = typeDataList.InvokeMember(null, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, null, new object[] { });
                                IEnumerable sourceList = sourceValue as IEnumerable;
                                foreach (var listItem in sourceList)
                                {
                                    AddInfo.Invoke(listvalue, new[] { listItem });
                                }

                                destInfo.SetValue(dest, listvalue, null);
                            }
                            else
                            {
                                //暂时不考虑
                            }
                        }

                        continue;
                    }

                    if (sourceType.IsArray)
                    {
                        //Console.WriteLine("Array: " + item.Key + ", " + sourceType.FullName);
                        if (sourceValue == null)
                        {
                            destInfo.SetValue(dest, null, null);
                        }
                        else
                        {
                            ICollection sourceList = sourceValue as ICollection;
                            Type type = sourceType.GetElementType();
                            var array = Array.CreateInstance(type, sourceList.Count);

                            int index = 0;
                            foreach (var listItem in sourceList)
                            {
                                array.SetValue(listItem, index++);
                            }

                            destInfo.SetValue(dest, array, null);
                        }

                        continue;
                    }

                    if (sourceType.IsDictionary())
                    {

                        continue;
                    }

                    //类没有无参构造函数的话，创建有问题
                    //if (sourceType.IsClass)
                    //{
                    //    if (sourceValue == null)
                    //    {
                    //        destInfo.SetValue(dest, null, null);
                    //    }
                    //    else
                    //    {
                    //        object obj = Activator.CreateInstance(sourceType, null);
                    //        obj.MapperFrom(sourceValue);
                    //        destInfo.SetValue(dest, obj, null);
                    //    }
                    //
                    //    continue;
                    //}
                }
            }
        }

        public static void MapperTo<T1, T2>(this T1 source, T2 dest)
            where T1 : class
            where T2 : class
        {
            Execute(source, dest);
        }

        public static void MapperFrom<T1, T2>(this T1 dest, T2 source)
            where T1 : class
            where T2 : class
        {
            Execute(source, dest);
        }

        public static Type GetArrayElementType(this Type t)
        {
            if (!t.IsArray) return null;
            string name = t.FullName.Replace("[]", string.Empty);
            return t.Assembly.GetType(name);
        }
    }
}
