using System;
using System.Collections.Generic;
using System.Text;
using System.Reflection;
using System.Collections;
using CommonLang.Log;

namespace CommonLang.Property
{
    public static class PropertyUtil
    {
        private static Logger s_log;
        public static Logger log { get { if (s_log == null) { s_log = LoggerFactory.GetLogger(typeof(PropertyUtil).Name); } return s_log; } }

        static public FieldInfo[] SortFields(FieldInfo[] fields)
        {
            Array.Sort(fields, BaseFieldSorter);
            return fields;
        }
        static public PropertyInfo[] SortProperties(PropertyInfo[] properties)
        {
            Array.Sort(properties, BasePropertySorter);
            return properties;
        }

        static public IComparer<FieldInfo> BaseFieldSorter = new FieldSorter();
        static public IComparer<PropertyInfo> BasePropertySorter = new PropertySorter();

        public class FieldSorter : IComparer<FieldInfo>
        {
            public int Compare(FieldInfo x, FieldInfo y)
            {
                return x.Name.CompareTo(y.Name);
            }
        }
        public class PropertySorter : IComparer<PropertyInfo>
        {
            public int Compare(PropertyInfo x, PropertyInfo y)
            {
                return x.Name.CompareTo(y.Name);
            }
        }

        public static Attribute GetAttributeByType(FieldInfo field, Type attributeType)
        {
            object[] cas = field.GetCustomAttributes(attributeType, false);
            if (cas != null && cas.Length > 0)
            {
                return cas[0] as Attribute;
            }
            return null;
        }


        public static T GetAttribute<T>(Type type) where T : System.Attribute
        {
            while (!type.Equals(typeof(object)))
            {
                object[] cas = type.GetCustomAttributes(typeof(T), false);
                if (cas != null && cas.Length > 0)
                {
                    return (T)cas[0];
                }
                else if (type.BaseType != null)
                {
                    type = type.BaseType;
                }
                else { return null; }
            }
            return null;
        }
        public static T GetAttribute<T>(MemberInfo member) where T : System.Attribute
        {
            object[] cas = member.GetCustomAttributes(typeof(T), false);
            if (cas != null && cas.Length > 0)
            {
                return (T)cas[0];
            }
            return null;
        }
        public static T GetAttribute<T>(FieldInfo field) where T : System.Attribute
        {
            object[] cas = field.GetCustomAttributes(typeof(T), false);
            if (cas != null && cas.Length > 0)
            {
                return (T)cas[0];
            }
            return null;
        }
        public static T GetAttribute<T>(PropertyInfo property) where T : System.Attribute
        {
            object[] cas = property.GetCustomAttributes(typeof(T), false);
            if (cas != null && cas.Length > 0)
            {
                return (T)cas[0];
            }
            return null;
        }
        static public T GetEnumAttribute<T>(object value) where T : Attribute
        {
            Type type = value.GetType();
            string name = Enum.GetName(type, value);
            if (name == null)
            {
                return null;
            }
            FieldInfo field = type.GetField(name);
            if (field != null)
            {
                return GetAttribute<T>(field);
            }
            return null;
        }



        public static DescAttribute GetDesc(Type type)
        {
            return GetAttribute<DescAttribute>(type);
        }
        public static DescAttribute GetDesc(MemberInfo field)
        {
            return GetAttribute<DescAttribute>(field);
        }
        public static ListAttribute GetListDesc(MemberInfo field)
        {
            return GetAttribute<ListAttribute>(field);
        }



        /// <summary>
        /// 将一个对象里面所有的Attribute的标记的Field值,全部取出
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="collection"></param>
        /// <returns></returns>
        static public void CollectFieldTypeValues<T>(object data, List<T> collection)
        {
            if (data != null)
            {
                Type fieldType = typeof(T);
                Type type = data.GetType();

                if (data is T)
                {
                    collection.Add((T)data);
                }
                if (type.IsClass && !type.IsPrimitive)
                {
                    if (data is IDictionary)
                    {
                        IDictionary map = data as IDictionary;
                        foreach (object o in map.Values)
                        {
                            CollectFieldTypeValues<T>(o, collection);
                        }
                    }
                    else if (data is ICollection)
                    {
                        ICollection list = data as ICollection;
                        foreach (object o in list)
                        {
                            CollectFieldTypeValues<T>(o, collection);
                        }
                    }
                    else if (type.IsArray)
                    {
                        Array array = (Array)data;
                        foreach (object o in array)
                        {
                            CollectFieldTypeValues<T>(o, collection);
                        }
                    }
                    else
                    {
                        foreach (FieldInfo field in type.GetFields())
                        {
                            if (!field.IsStatic)
                            {
                                object fd = field.GetValue(data);
                                if (fd != null)
                                {
                                    CollectFieldTypeValues<T>(fd, collection);
                                }
                            }
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 将一个对象里面所有的Attribute的标记的Field值,全部取出
        /// </summary>
        /// <param name="data"></param>
        /// <param name="attributeType"></param>
        /// <param name="collection"></param>
        static public void CollectFieldAttributeValues(object data, Type attributeType, List<FieldAttributeValue> collection)
        {
            if (data != null)
            {
                Type type = data.GetType();

                if (data is IDictionary)
                {
                    IDictionary map = data as IDictionary;
                    foreach (object o in map.Values)
                    {
                        CollectFieldAttributeValues(o, attributeType, collection);
                    }
                }
                else if (data is ICollection)
                {
                    ICollection list = data as ICollection;
                    foreach (object o in list)
                    {
                        CollectFieldAttributeValues(o, attributeType, collection);
                    }
                }
                else if (type.IsArray)
                {
                    Array array = (Array)data;
                    foreach (object o in array)
                    {
                        CollectFieldAttributeValues(o, attributeType, collection);
                    }
                }
                else if (type.IsClass)
                {
                    foreach (FieldInfo field in type.GetFields())
                    {
                        if (!field.IsStatic)
                        {
                            object fd = field.GetValue(data);
                            if (fd != null)
                            {
                                Attribute attr = PropertyUtil.GetAttributeByType(field, attributeType);
                                if (attr != null)
                                {
                                    FieldAttributeValue fv = new FieldAttributeValue(field, attr, fd, data);
                                    collection.Add(fv);
                                }
                                else
                                {
                                    CollectFieldAttributeValues(fd, attributeType, collection);
                                }
                            }
                        }
                    }
                }
            }
        }
        static public void CollectFieldAttributeValuesGeneric<A, T>(object data, List<TFieldAttributeValue<A, T>> collection) where A : Attribute
        {
            List<FieldAttributeValue> list = new List<FieldAttributeValue>();
            CollectFieldAttributeValues(data, typeof(A), list);
            foreach (FieldAttributeValue fv in list)
            {
                collection.Add(new TFieldAttributeValue<A, T>(fv.Field, fv.AttributeData as A, fv.FieldValue, fv.FieldOwner));
            }
        }

        /// <summary>
        /// 返回书写习惯的类型字符串
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string ToTypeDefineString(Type type)
        {
            if (type.IsGenericType)
            {
                StringBuilder sb = new StringBuilder();
                int tidx = type.Name.IndexOf('`');
                if (tidx > 0)
                {
                    sb.Append(type.Name.Substring(0, tidx));
                }
                else
                {
                    sb.Append(type.Name);
                }
                sb.Append("<");
                Type[] g_args = type.GetGenericArguments();
                foreach (Type g_arg in g_args)
                {
                    sb.Append(ToTypeDefineString(g_arg));
                    if (!g_arg.Equals(g_args[g_args.Length - 1]))
                    {
                        sb.Append(", ");
                    }
                }
                sb.Append(">");
                return sb.ToString();
            }
            return type.Name;
        }


        public static T GetFieldOrPropertyOrMethodValue<T>(object componentData, string fieldName)
        {
            Type componentType = componentData.GetType();
            FieldInfo depend_field = componentType.GetField(fieldName);
            if (depend_field != null)
            {
                try
                {
                    T ret = (T)depend_field.GetValue(componentData);
                    return ret;
                }
                catch (Exception err) { log.Error(err.Message, err); }
            }
            PropertyInfo depend_property = componentType.GetProperty(fieldName);
            if (depend_property != null)
            {
                try
                {
                    T ret = (T)depend_property.GetValue(componentData, null);
                    return ret;
                }
                catch (Exception err) { log.Error(err.Message, err); }
            }
            MethodInfo depend_method = componentType.GetMethod(fieldName);
            if (depend_method != null)
            {
                try
                {
                    T ret = (T)depend_method.Invoke(componentData, ZERO_ARGS);
                    return ret;
                }
                catch (Exception err) { log.Error(err.Message, err); }
            }
            return default(T);
        }

        public readonly static object[] ZERO_ARGS = new object[] { };

        public static void SetMemberValue(MemberInfo field, object obj, object value)
        {
            if (field is FieldInfo)
            {
                (field as FieldInfo).SetValue(obj, value);
            }
            else if (field is PropertyInfo)
            {
                var set = (field as PropertyInfo).GetSetMethod();
                if (set != null)
                {
                    set.Invoke(obj, new object[] { value });
                }
            }
        }
        public static object GetMemberValue(MemberInfo field, object obj)
        {
            if (field is FieldInfo)
            {
                return (field as FieldInfo).GetValue(obj);
            }
            else if (field is PropertyInfo)
            {
                var get = (field as PropertyInfo).GetGetMethod();
                if (get != null)
                {
                    return get.Invoke(obj, ZERO_ARGS);
                }
            }
            return null;
        }
    }

    public struct FieldAttributeValue
    {
        public Attribute AttributeData;
        public FieldInfo Field;
        public object FieldOwner;
        public object FieldValue;
        public FieldAttributeValue(FieldInfo field, Attribute attr, object fieldData, object fieldOwner)
        {
            this.AttributeData = attr;
            this.Field = field;
            this.FieldOwner = fieldOwner;
            this.FieldValue = fieldData;
        }
    }
    public struct TFieldAttributeValue<A, T> where A : Attribute
    {
        public A AttributeData;
        public FieldInfo Field;
        public object FieldOwner;
        public T FieldValue;
        public TFieldAttributeValue(FieldInfo field, A attr, object fieldData, object fieldOwner)
        {
            this.AttributeData = attr;
            this.Field = field;
            this.FieldOwner = fieldOwner;
            this.FieldValue = (T)fieldData;
        }
    }

    public class TypeDescAttribute : IComparable<TypeDescAttribute>
    {
        public DescAttribute Desc;
        public Type DataType;
        public TypeDescAttribute(Type type)
        {
            DataType = type;
            Desc = PropertyUtil.GetAttribute<DescAttribute>(type);
        }
        public override string ToString()
        {
            return Desc.Desc;
        }
        public int CompareTo(TypeDescAttribute other)
        {
            return this.ToString().CompareTo(other.ToString());
        }


        /// <summary>
        /// 如果此类型有DescAttribute签名,则返回Desc
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetDescText(Type type)
        {
            DescAttribute desc = PropertyUtil.GetAttribute<DescAttribute>(type);
            if (desc != null)
            {
                return desc.Desc;
            }
            return "";
        }
        /// <summary>
        /// 如果此类型有DescAttribute签名,则返回Catgory
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static string GetCatgoryName(Type type)
        {
            DescAttribute desc = PropertyUtil.GetAttribute<DescAttribute>(type);
            if (desc != null)
            {
                return desc.Category;
            }
            return "";
        }

    }


    public class MemberDescAttribute : IComparable<MemberDescAttribute>
    {
        public readonly DescAttribute Desc;
        public readonly MemberInfo DataField;
        public readonly Type FieldType;

        public MemberDescAttribute(MemberInfo field)
        {
            DataField = field;
            Desc = PropertyUtil.GetAttribute<DescAttribute>(field);
            if (field is FieldInfo)
            {
                FieldType = (field as FieldInfo).FieldType;
            }
            else if (field is PropertyInfo)
            {
                FieldType = (field as PropertyInfo).PropertyType;
            }
            else if (field is MethodInfo)
            {
                FieldType = (field as MethodInfo).ReturnType;
            }
            else
            {
                throw new Exception("Error MemberDescAttribute : " + field);
            }
        }
        public string ToDescString()
        {
            return (Desc != null ? string.Format("[{0}]", Desc.Desc) : "");
        }
        public override string ToString()
        {
            if (DataField is MethodInfo)
            {
                return string.Format("{0}() {1}", DataField.Name, ToDescString());
            }
            else
            {
                return string.Format("{0} {1}", DataField.Name, ToDescString());
            }
        }
        public int CompareTo(MemberDescAttribute other)
        {
            return DataField.Name.CompareTo(other.DataField.Name);
        }

        public object GetValue(object owner)
        {
            try
            {
                if (DataField is FieldInfo)
                {
                    FieldInfo field = DataField as FieldInfo;
                    return field.GetValue(owner);
                }
                else if (DataField is PropertyInfo)
                {
                    PropertyInfo field = DataField as PropertyInfo;
                    return field.GetGetMethod().Invoke(owner, new object[0]);
                }
                else if (DataField is MethodInfo)
                {
                    MethodInfo method = DataField as MethodInfo;
                    return method.Invoke(owner, new object[0]);
                }
            }
            catch (Exception err) { PropertyUtil.log.Error(err.Message, err); }
            return null;
        }

        public void SetValue(object owner, object value)
        {
            try
            {
                if (DataField is FieldInfo)
                {
                    FieldInfo field = DataField as FieldInfo;
                    field.SetValue(owner, value);
                }
                else if (DataField is PropertyInfo)
                {
                    PropertyInfo field = DataField as PropertyInfo;
                    field.GetSetMethod().Invoke(owner, new object[] { value });
                }
                else if (DataField is MethodInfo)
                {
                    MethodInfo method = DataField as MethodInfo;
                    method.Invoke(owner, new object[] { value });
                }
            }
            catch (Exception err) { PropertyUtil.log.Error(err.Message, err); }
        }

    }
}