using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Dynamic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace JiaZhiQuan.Common.Utils {
    public class ExpandoUtils {
        /// <summary>
        /// 转成可以扩充的对象
        /// </summary> www.it165.net
        /// <param name="obj"></param>
        /// <returns></returns>
        public static dynamic ConvertToDynamic(object obj) {
            IDictionary<string, object> result = new ExpandoObject();
            if(obj is IDictionary<string, object> rst) {
                foreach (var item in rst) {
                    result.Add(item.Key, item.Value);
                }
                return result;
            }
            
            foreach (PropertyDescriptor pro in TypeDescriptor.GetProperties(obj.GetType())) {
                result.Add(pro.Name, pro.GetValue(obj));
            }

            return result;
        }

        public static List<dynamic> ConvertToListDynamic<S>(List<S> list) {
            if (list == null || list.Count == 0) return new List<dynamic>();

            return (from obj in list
                    where obj != null
                    select ConvertToDynamic(obj)).ToList();            
        }

        public static T DictToObject<T>(IDictionary<string, object> dict,string dateFormat=null) {
            var type = typeof(T);
            var obj = Activator.CreateInstance(type);
            if (dict == null) return (T)obj;
            var properties = type.GetProperties();
            foreach (var item in properties) {
                if (!dict.ContainsKey(item.Name)) continue;
                var value = dict[item.Name]?.ToString();
                if(value==null) continue;
                var propertyType = item.PropertyType;
                if (propertyType.IsEnum) {
                    item.SetValue(obj, Enum.Parse(propertyType, value));
                } else if (propertyType.IsGenericType) {
                    propertyType = propertyType.GenericTypeArguments[0];
                    if (propertyType.IsEnum) {
                        item.SetValue(obj, Enum.Parse(propertyType, value));
                    }
                } else if (propertyType ==typeof( DateTime)) {
                    if(dateFormat==null) {
                        dateFormat = "yyyy-MM-dd HH:mm:ss";
                    }                    
                    item.SetValue(obj, DateTime.ParseExact(value, dateFormat, CultureInfo.CurrentCulture));
                }
                else {
                    item.SetValue(obj, Convert.ChangeType(value, propertyType));
                }
            }
            return (T)obj;
        }

        public class MyDynamic : DynamicObject {
            // The inner dictionary.
            Dictionary<string, object> dictionary = new Dictionary<string, object>();

            // This property returns the number of elements
            // in the inner dictionary.
            public int Count {
                get {
                    return dictionary.Count;
                }
            }

            // If you try to get a value of a property
            // not defined in the class, this method is called.
            public override bool TryGetMember(
                GetMemberBinder binder, out object? result) {
                // Converting the property name to lowercase
                // so that property names become case-insensitive.
                string name = binder.Name.ToLower();

                // If the property name is found in a dictionary,
                // set the result parameter to the property value and return true.
                // Otherwise, return false.
                return dictionary.TryGetValue(name, out result);
            }

            // If you try to set a value of a property that is
            // not defined in the class, this method is called.
            public override bool TrySetMember(
                SetMemberBinder binder, object? value) {
                // Converting the property name to lowercase
                // so that property names become case-insensitive.
                dictionary[binder.Name.ToLower()] = value!;

                // You can always add a value to a dictionary,
                // so this method always returns true.
                return true;
            }
            public override bool TryInvokeMember(InvokeMemberBinder binder, object?[]? args, out object? result) {
                 
                Type dictType = typeof(Dictionary<string, object>);
                try {

                    result = dictType.InvokeMember(binder.Name,BindingFlags.InvokeMethod,
                                 null, dictionary, args);
                    return true;
                } catch {
                    result = null;
                    return false;
                }
            }

        }
    }
}
