using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Xml.Serialization;
using Exam.Infrastructure.Attributes;

namespace Exam.Infrastructure.Extensions
{
    public static class ObjectExtension
    {
        #region public method
        public static bool TryConvert<T>(this object obj, out T result)
        {
            try
            {
                var converted = Convert.ChangeType(obj, typeof(T));
                if (converted is T)
                {
                    result = (T)converted;
                    return true;
                }
            }
            catch
            {

            }

            result = default(T);
            return false;
        }

        public static T ConvertTo<T>(this object obj)
        {
            try
            {
                var converted = Convert.ChangeType(obj, typeof(T));
                if (converted is T)
                {
                    return (T)converted;
                }
            }
            catch
            {

            }

            return default(T);
        }

        /// <summary>
        /// 对象序列化为字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static string ToJson(this object obj)
        {
            var jsonSerializerSettings = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            };

            var result = JsonConvert.SerializeObject(obj, jsonSerializerSettings);

            return result;
        }

        /// <summary>
        /// 对象序列化为字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this string value)
        {
            var jsonSerializerSettings = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            };

            var result = JsonConvert.DeserializeObject<T>(value, jsonSerializerSettings);

            return result;
        }

        /// <summary>
        /// 对象序列化为字符串
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T ToEntity<T>(this JObject value)
        {
            var jsonSerializerSettings = new JsonSerializerSettings
            {
                NullValueHandling = NullValueHandling.Ignore,
                ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            };

            var result = JsonConvert.DeserializeObject<T>(value.ToString(), jsonSerializerSettings);

            return result;
        }

        /// <summary>
        /// 序列化为XML字符串
        /// </summary>
        public static string ToXml(this object obj)
        {
            using (var stream = new MemoryStream())
            {
                var xml = new XmlSerializer(obj.GetType());
                try
                {
                    //序列化对象
                    xml.Serialize(stream, obj);
                }
                catch (InvalidOperationException)
                {
                    throw;
                }
                stream.Position = 0;
                using (var streamReader = new StreamReader(stream))
                {
                    string xmlString = streamReader.ReadToEnd();
                    return xmlString;
                }
            }
        }

        /// <summary>
        /// XML字符串反虚拟化对象
        /// </summary>
        public static T XmlToObj<T>(this string xmlString)
        {
            try
            {
                using (var stringReader = new StringReader(xmlString))
                {
                    XmlSerializer xml = new XmlSerializer(typeof(T));
                    return (T)xml.Deserialize(stringReader);
                }
            }
            catch (Exception)
            {
                throw;
            }

        }

        /// <summary>
        /// 时间戳转换为时间
        /// </summary>
        /// <param name="timeStamp"></param>
        /// <returns></returns>
        public static DateTime StampToTime(this string timeStamp)
        {
            DateTime dateTimeStart = TimeZone.CurrentTimeZone.ToLocalTime(DateTime.Now);
            long lTime = long.Parse(timeStamp);
            TimeSpan toNow = new TimeSpan(lTime);
            return dateTimeStart.Add(toNow);
        }

        /// <summary>
        /// DateTime时间格式转换为Unix时间戳格式
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string TimeToStamp(this System.DateTime time)
        {
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));
            var result = (int)(time - startTime).TotalSeconds;
            return result.ToString();
        }

        public static bool IsNull<T>(this T value)
        {
            if (value != null)
                return false;

            Type type = typeof(T);

            if (!type.IsValueType)
                return true; // ref-type

            if (Nullable.GetUnderlyingType(type) != null)
                return true; // Nullable<T>

            return false; // value-type
        }

        public static T GetAttribute<T>(this MemberInfo field) where T : Attribute
        {
            return Attribute.GetCustomAttribute(field, typeof(T)) as T;
        }

        public static string GetDescription(this Type type)
        {

            var attrs = type.GetCustomAttributes();

            foreach (var attr in attrs)
            {
                if (attr is DescriptionAttribute)
                {
                    return ((DescriptionAttribute)attr).Description;
                }
            }
            return string.Empty;
        }

        public static string GetDescription(this Type type, string memberName)
        {
            MemberInfo memberInfo = type.GetMembers().FirstOrDefault(m => m.Name == memberName);

            if (memberInfo == null) return string.Empty;

            var attribute = GetAttribute<DescriptionAttribute>(memberInfo);

            return attribute != null ? attribute.Description : string.Empty;
        }

        public static string GetAttrDescription(this object value, string memberName)
        {
            MemberInfo memberInfo = value.GetType().GetMembers().FirstOrDefault(m => m.Name == memberName);

            if (memberInfo == null) return string.Empty;

            var attribute = GetAttribute<DescriptionAttribute>(memberInfo);

            return attribute != null ? attribute.Description : string.Empty;
        }

        public static string GetObjDescription(this object value, string name)
        {
            if (value != null)
            {
                var properties = value.GetType().GetProperties();

                var property = properties.FirstOrDefault(m => m.Name == name);

                var attrs = property.GetCustomAttributes();

                foreach (var attr in attrs)
                {
                    if (attr is DescriptionAttribute)
                    {
                        return ((DescriptionAttribute)attr).Description;
                    }
                }
            }
            return string.Empty;
        }

        public static string GetObjDescription(this object value)
        {
            if (value != null)
            {
                var attrs = value.GetType().GetCustomAttributes();

                foreach (var attr in attrs)
                {
                    if (attr is DescriptionAttribute)
                    {
                        return ((DescriptionAttribute)attr).Description;
                    }
                }
            }
            return string.Empty;
        }

        public static string GetTableName(this object value)
        {
            if (value != null)
            {
                var attrs = value.GetType().GetCustomAttributes();

                foreach (var attr in attrs)
                {
                    if (attr is TableAttribute)
                    {
                        return ((TableAttribute)attr).Name;
                    }
                }
            }
            return string.Empty;
        }

        //public static string GetObjDescription(this object value)
        //{
        //    var attrs = value.GetType().GetCustomAttributes();

        //    foreach (var attr in attrs)
        //    {
        //        if (attr is DescriptionAttribute)
        //        {
        //            return ((DescriptionAttribute)attr).Description;
        //        }
        //    }
        //    return string.Empty;
        //}

        /// <summary>
        /// 把简单一个对象转换成属性名-属性值的键值对字典
        /// 注意：此扩展方法不支持嵌套的复杂对象
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static IDictionary<string, object> ToDictionary(this object obj)
        {
            if (obj == null)
                return new Dictionary<string, object>();

            var dict = new Dictionary<string, object>();

            var props = obj.GetType().GetProperties();

            foreach (var prop in props)
            {
                var attributes = prop.GetCustomAttributes<ReflectIgnoreAttribute>();

                if (attributes != null && attributes.Any()) continue;

                if (prop.GetValue(obj) != null)
                {
                    dict[prop.Name] = prop.GetValue(obj);
                }
            }
            return dict;
        }

        /// <summary>
        /// 把简单一个对象转换成属性名-属性值的键值对字典
        /// 注意：此扩展方法不支持嵌套的复杂对象
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static IDictionary<TKey, TValue> ToDictionary<TKey, TValue>(this object obj)
        {
            if (obj == null)
                return new Dictionary<TKey, TValue>();

            var dict = new Dictionary<TKey, TValue>();

            var props = obj.GetType().GetProperties();

            foreach (var prop in props)
            {
                var attributes = prop.GetCustomAttributes<ReflectIgnoreAttribute>();

                if (attributes != null && attributes.Any()) continue;

                if (prop.GetValue(obj) != null)
                {
                    var key = (TKey)Convert.ChangeType(prop.Name, typeof(TKey));

                    if (key != null)
                    {
                        dict[key] = (TValue)Convert.ChangeType(prop.GetValue(obj), typeof(TValue));
                    }
                }
            }
            return dict;
        }
        /// <summary>
        /// 把简单一个对象转换成属性名-属性值的键值对字典
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static IDictionary<string, object> ToDeepDictionary(this object obj)
        {
            if (obj == null)
                return new Dictionary<string, object>();

            var dict = new Dictionary<string, object>();

            Type objType = obj.GetType();

            if (objType.IsGenericType || objType.Equals(typeof(string)))
            {
                return dict;
            }

            var props = obj.GetType().GetProperties();

            foreach (var prop in props)
            {
                var attributes = prop.GetCustomAttributes<ReflectIgnoreAttribute>();

                if (attributes != null && attributes.Any()) continue;

                var childObj = prop.GetValue(obj);

                if (childObj != null)
                {
                    dict[prop.Name] = childObj.ToDeepDictionary();
                }
            }
            return dict;
        }

        public static SortedDictionary<string, TValue> ToSortedDictionary<TValue>(this object obj, int sorted = 0, bool isEncode = false)
        {
            if (obj == null)
                return new SortedDictionary<string, TValue>();

            var dict = new SortedDictionary<string, TValue>();

            var props = obj.GetType().GetProperties();

            foreach (var prop in props)
            {
                var attributes = prop.GetCustomAttributes<ReflectIgnoreAttribute>();

                if (attributes != null && attributes.Any()) continue;

                if (prop.GetValue(obj) != null)
                {
                    dict[Encode(prop.Name, isEncode).ToString()] = (TValue)Convert.ChangeType(Encode(prop.GetValue(obj), isEncode), typeof(TValue));
                }
            }
            return (SortedDictionary<string, TValue>)(sorted == 0 ? dict.OrderBy(x => x.Key) : dict.OrderByDescending(x => x.Key));
        }

        /// <summary>
        /// JSON序列化和反序列化实现的深拷贝
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="obj"></param>
        /// <returns></returns>
        public static T DeepClone<T>(this T source)
        {
            T newInfo = System.Text.Json.JsonSerializer.Deserialize<T>(System.Text.Json.JsonSerializer.Serialize(source));

            return newInfo;
        }

        public static List<T> DeepCloneClear<T>(this List<T> source)
        {
            var result = source.DeepClone();
            source.Clear();
            return result;
        }

        public static byte[] GetBytes<T>(this T source)
        {
            byte[] bytes = null;

            if (source != null)
            {
                bytes = Encoding.UTF8.GetBytes(source.ToJson());
            }

            return bytes;
        }

        public static T GetEntity<T>(this byte[] bytes)
        {

            T entity = default(T);

            if (bytes != null)
            {

                var source = Encoding.UTF8.GetString(bytes);

                entity = source.ToEntity<T>();
            }

            return entity;
        }

        public static IDictionary<string, object> ToDictionary(this JObject @object)
        {
            var result = @object.ToObject<Dictionary<string, object>>();

            var JObjectKeys = (from r in result
                               let key = r.Key
                               let value = r.Value
                               where value.GetType() == typeof(JObject)
                               select key).ToList();

            var JArrayKeys = (from r in result
                              let key = r.Key
                              let value = r.Value
                              where value.GetType() == typeof(JArray)
                              select key).ToList();

            JArrayKeys.ForEach(key => result[key] = ((JArray)result[key]).Values().Select(x => ((JValue)x).Value).ToArray());
            JObjectKeys.ForEach(key => result[key] = ToDictionary(result[key] as JObject));

            return result;
        }
        #endregion

        #region private method
        private static object Encode(object value, bool isEncode)
        {
            if (isEncode)
            {
                return HttpUtility.HtmlEncode(value);
            }
            return value;
        }
        #endregion
    }
}
