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

namespace Misc
{
    public static class PropertiesManager
    {

        /// <summary>
        /// 获取所有属性名。
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetAllPropertyNames(object obj)
        {
            PropertyInfo[] properties = obj.GetType().GetProperties();

            return from p in properties select p.Name;
        }

        /// <summary>
        /// 设定参数（属性）的值，触发属性值变化“通知”,writeVal
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName">属性名</param>
        /// <param name="v"></param>
        public static void SetValue(object obj, string propertyName, object v)
        {
            PropertyInfo property = obj.GetType().GetProperty(propertyName);
            if (property != null)
            {
                property.SetValue(obj, v, null);
            }
        }

        /// <summary>
        /// 设定参数（属性）的值，触发属性值变化“通知”,writeVal
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <param name="val"></param>
        public static void SetValue(object obj, string propertyName, string val)
        {
            PropertyInfo property = obj.GetType().GetProperty(propertyName);
            if (property != null)
            {
                object v = Converter.Parse(val, property.PropertyType);
                property.SetValue(obj, v, null);
            }
        }

        /// <summary>
        /// 获取设定的属性值，writeVal
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public static object GetValue(object obj, string propertyName)
        {
            PropertyInfo property = obj.GetType().GetProperty(propertyName);
            if (property != null)
            {
                return property.GetValue(obj, null);
            }
            else
                return null;
        }


        /// <summary>
        /// 把 src内的全部属性，复制到 dest
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dest"></param>
        public static void CopyTo(object src, object dest)
        {
            Type t = src.GetType();
            if (src.GetType() != dest.GetType())
            {
                throw new Exception("CopyTo src.GetType() != dest.GetType()");
            }

            foreach (PropertyInfo propertyInfo in t.GetProperties())
            {
                //忽略不复制！！！
                if (propertyInfo.GetCustomAttributes(typeof(CopyIgnoreAttribute), false).Count() > 0)
                    continue;

                //ICopiable
                //if (propertyInfo.PropertyType.IsSubclassOf(typeof(ICopiable)))
                if(typeof(ICopiable).IsAssignableFrom(propertyInfo.PropertyType))
                {
                    ICopiable copiable = propertyInfo.GetValue(dest, null) as ICopiable;
                    copiable.Copy(propertyInfo.GetValue(src, null));
                }
                else if(propertyInfo.CanWrite)
                {
                    propertyInfo.SetValue(dest, propertyInfo.GetValue(src, null), null);
                }
            }
        }

        /// <summary>
        /// 检测 dest是否 实现了ICopiable，如果是，调用dest.Copy ；否 调用CopyTo(src,dest)
        /// </summary>
        /// <param name="src"></param>
        /// <param name="dest"></param>
        public static void AutoCopyTo(object src, object dest)
        {
            if (dest is ICopiable)
            {
                ((ICopiable)dest).Copy(src);
            }
            else
            {
                CopyTo(src, dest);
            }
        }

    }

    /// <summary>
    /// 一般情况 用 Misc.PropertiesManager.CopyTo 就能解决问题
    /// 但 只有get的属性，是没法复制的
    /// 只能通过 继承 ICopiable， 解决 只能get 的属性 复制问题
    /// </summary>
    public interface ICopiable
    {
        /// <summary>
        /// 从 src 复制全部数据过来！！！
        /// 
        /// </summary>
        /// <param name="src"></param>
        void Copy(object src);
    }
}
