using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace PushPlatform.Common.Util
{
    /// <summary>
    /// 对象辅助类
    /// </summary>
    public static class ObjectHelper
    {
        #region 克隆对象，object扩展方法
        /// <summary>
        /// 克隆对象，object扩展方法
        /// </summary>
        /// <param name="obj">要克隆的对象</param>
        /// <returns>返回克隆的对象</returns>
        public static object Clone(this object obj)
        {
            try
            {
                MemoryStream stream = new MemoryStream();
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, obj);
                stream.Position = 0;
                return formatter.Deserialize(stream);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        #endregion

        #region 复制属性，支持基本类型
        /// <summary>
        /// 复制属性,支持基本类型
        /// </summary>
        /// <param name="sourceObj">复制源对象</param>
        /// <param name="targetObj">复制目标对象</param>
        /// <param name="excludeProperties">不需要复制的属性名称集合</param>
        public static void CopyProperties(object sourceObj, object targetObj, List<string> excludeProperties = null)
        {
            PropertyInfo[] properties = sourceObj.GetType().GetProperties(); //获得源对象的所有属性
            if (properties != null && properties.Length > 0)
            {
                foreach (PropertyInfo p in properties)
                {
                    string propertyName = p.Name;
                    if (excludeProperties != null && excludeProperties.Contains(propertyName)) //判断属性名称是否在不需要复制的属性名称集合中
                        continue;
                    else
                    {
                        PropertyInfo propertyInfo = targetObj.GetType().GetProperty(propertyName); //根据属性名称判断目标对象是否存在该属性，
                        if (propertyInfo != null && propertyInfo.CanWrite) //判断属性是否为空并且该属性是可写入的
                        {
                            propertyInfo.SetValue(targetObj, p.GetValue(sourceObj, null), null);
                        }
                    }
                }
            }
        }
        #endregion

        #region 设置对象属性值,支持基本类型
        /// <summary>
        /// 设置对象属性值
        /// </summary>
        /// <param name="obj">要设置值的对象</param>
        /// <param name="propertyName">要设置值得属性名称</param>
        /// <param name="value">值</param>
        public static void SetPropertyValue(object obj, string propertyName, object value)
        {
            PropertyInfo property = obj.GetType().GetProperty(propertyName);

            if (property != null && property.CanWrite && value != null)
            {
                object pvalue = value;

                Type valueType = property.PropertyType;

                if (valueType == typeof(decimal) || valueType == typeof(decimal?))
                {
                    pvalue = Convert.ToDecimal(pvalue);
                }
                else if (valueType == typeof(Int64) || valueType == typeof(Int64?))
                {
                    pvalue = Convert.ToInt64(pvalue);
                }
                else if (valueType == typeof(double) || valueType == typeof(double?))
                {
                    pvalue = Convert.ToDouble(pvalue);
                }
                else if (valueType == typeof(Int32) || valueType == typeof(Int32?))
                {
                    pvalue = Convert.ToInt32(pvalue);
                }
                else if (valueType == typeof(Int16) || valueType == typeof(Int16?))
                {
                    pvalue = Convert.ToInt16(pvalue);
                }
                else if (valueType == typeof(DateTime) || valueType == typeof(DateTime?))
                {
                    try
                    {
                        pvalue = Convert.ToDateTime(pvalue);
                    }
                    catch
                    {
                        pvalue = null;
                    }
                }
                else if (valueType == typeof(bool) || valueType == typeof(bool?))
                {
                    try
                    {
                        pvalue = Convert.ToBoolean(pvalue);
                    }
                    catch
                    {
                        pvalue = null;
                    }
                }
                property.SetValue(obj, pvalue, null);
            }
        }
        #endregion

        #region 对象属性URL编码,通常用于有中文值的对象，在页面传递参数时编码
        /// <summary>
        /// 对象属性URL编码,通常用于有中文值的对象，在页面传递参数时编码
        /// </summary>
        /// <param name="obj">要解码的对象</param>
        public static void PropertiesUrlEncode(object obj)
        {
            PropertyInfo[] properties = obj.GetType().GetProperties();
            foreach (var p in properties)
            {
                if (p.PropertyType == typeof(string))
                {
                    var value = (string)p.GetValue(obj, null);
                    p.SetValue(obj, HttpUtility.UrlEncode(value, Encoding.UTF8), null);
                }
            }
        }
        #endregion

        #region 对象属性URL解码,通常用于页面编码后传递的参数对象
        /// <summary>
        /// 对象属性URL解码,通常用于页面编码后传递的参数对象
        /// </summary>
        /// <param name="obj">要解码的对象</param>
        public static void PropertiesUrlDecode(object obj)
        {
            PropertyInfo[] properties = obj.GetType().GetProperties();
            foreach (var p in properties)
            {
                if (p.PropertyType == typeof(string))
                {
                    var value = (string)p.GetValue(obj, null);
                    p.SetValue(obj, HttpUtility.UrlDecode(value, Encoding.UTF8), null);
                }
            }
        }
        #endregion

        #region 是否可为空
        /// <summary>
        /// 是否可为空
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        private static bool IsNullable(Type t)
        {
            return !t.IsValueType || (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>));
        }
        #endregion

        #region 获取类型
        /// <summary>
        /// 获取类型
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        public static Type GetCoreType(Type t)
        {
            if (t != null && IsNullable(t))
            {
                if (!t.IsValueType)
                {
                    return t;
                }
                else
                {
                    return Nullable.GetUnderlyingType(t);
                }
            }
            else
            {
                return t;
            }
        }
        #endregion

        #region 获得属性值
        public static string GetPropertyValue(object obj, string propertyName)
        {
            var sProperty = obj.GetType().GetProperty(propertyName);
            if (sProperty != null && sProperty.GetValue(obj) != null)
            {
                return sProperty.GetValue(obj).ToString();
            }
            return "";
        }
        #endregion
    }
}
