// == LICENSE INFORMATION ==
/*
 * First author tiritomato 2013.
 * This program is distributed under the GNU General Public License(GPL).
 * support blog (Japanese only) http://d.hatena.ne.jp/tiri_tomato/
 */
// == LICENSE INFORMATION ==

namespace UVTexIntegra.Scripting
{
    //! @addtogroup UVTexIntegra-Scripting名前空間
    //! @{

    //! @brief 属性を操作するための拡張メソッドを主体にしたクラス
    public static class AttributeExtensions
    {
        //! @brief 属性クラスを取得します @details isInheritにtrueを設定するのと同義です。
        //! @return 見つかった属性の配列が返ります（見つからない場合は空の配列）。
        public static AttributeType[] GetAttributes<AttributeType>(this System.Reflection.MemberInfo info)
            where AttributeType : System.Attribute { return GetAttributes<AttributeType>(info, true); }

        //! @brief 属性クラスを取得します
        //! @return 見つかった属性の配列が返ります（見つからない場合は空の配列）。
        public static AttributeType[] GetAttributes<AttributeType>(this System.Reflection.MemberInfo info, bool isInherit)
            where AttributeType : System.Attribute
        {
            System.Collections.Generic.List<AttributeType> ret = new System.Collections.Generic.List<AttributeType>();
            System.Object[] attrs = null;
            if (info != null) attrs = info.GetCustomAttributes(typeof(AttributeType), isInherit);
            if (attrs != null) foreach (System.Object obj in attrs)
                {
                    AttributeType attr = obj as AttributeType;
                    if (attr != null) ret.Add(attr);
                }
            return ret.ToArray();
        }

        //! @brief DefaultValue属性を取得します。
        //! @return DefaultValue属性。この操作は GetPrimaryAttribute<System.ComponentModel.DefaultValueAttribute>(info) と等価です。
        public static System.ComponentModel.DefaultValueAttribute GetDefaultValueAttribute(this System.Reflection.MemberInfo info)
        {
            return GetPrimaryAttribute<System.ComponentModel.DefaultValueAttribute>(info);
        }

        //! @brief 属性クラスを取得します。
        //! @return 見つからない場合はnull。複数ある場合は先頭の属性が返ります。isInheritにtrueを設定して呼び出すのと同義です。
        public static AttributeType GetPrimaryAttribute<AttributeType>(this System.Reflection.MemberInfo info)
            where AttributeType : System.Attribute { return GetPrimaryAttribute<AttributeType>(info, true); }

        //! @brief 属性クラスを取得します。
        //! @return 見つからない場合はnull。複数ある場合は先頭の属性が返ります。
        public static AttributeType GetPrimaryAttribute<AttributeType>(this System.Reflection.MemberInfo info, bool isInherit)
            where AttributeType : System.Attribute
        {
            System.Object[] attrs = null;
            if (info != null) attrs = info.GetCustomAttributes(typeof(AttributeType), isInherit);
            if (attrs != null) foreach (System.Object obj in attrs)
                {
                    AttributeType attr = obj as AttributeType;
                    if (attr != null) return attr;
                }
            return null;
        }

        //! @brief プロパティにDefaultValue属性の値をロードします。
        //! @details プロパティの列挙フラグにはSystem.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
        //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
        //! applyOnNullDefaultValue にはtrueが設定されます。
        public static void ApplyDefeultValueAttributeToProperties(this System.Object target)
        {
            ApplyDefeultValueAttributeToProperties(target, true);
        }
        //! @brief プロパティにDefaultValue属性の値をロードします。
        //! @details プロパティの列挙フラグにはSystem.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
        //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToProperties(this System.Object target, bool applyOnNullDefaultValue)
        {
            System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance |
                 System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
            ApplyDefeultValueAttributeToProperties(target, flags, applyOnNullDefaultValue);
        }
        //! @brief オブジェクトのプロパティにDefaultValue属性の値をロードします。
        //! @param [in] flags プロパティを列挙する時のフラグを指定します。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToProperties(this System.Object target, System.Reflection.BindingFlags flags, bool applyOnNullDefaultValue)
        {
            if (target == null) return;
            System.Type type = target.GetType();
            System.Reflection.PropertyInfo[] props = type.GetProperties(flags);
            if ((props == null) || (props.Length <= 0)) return;
            foreach (System.Reflection.PropertyInfo prop in props)
            {
                if ((prop == null) || (prop.CanWrite == false)) continue;
                System.ComponentModel.DefaultValueAttribute attr = prop.GetPrimaryAttribute<System.ComponentModel.DefaultValueAttribute>();
                if (attr == null) continue;
                if ((attr.Value == null) && (applyOnNullDefaultValue == false)) continue;
                try { prop.SetValue(target, attr.Value, null); }
                catch (System.Exception) { }
            }
        }
        //! @brief オブジェクトのプロパティにDefaultValue属性の値をロードします。
        //! @param [in] defaultValueType DefaultValueAttribute属性をサーチする型を明示的に指定します。nullで省略するとtargetインスタンスからGetType()されます。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToProperties(this System.Object target, System.Type defaultValueType, bool applyOnNullDefaultValue)
        {
            System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance |
                 System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
            ApplyDefeultValueAttributeToProperties(target, defaultValueType, flags, applyOnNullDefaultValue);
        }
        //! @brief オブジェクトのプロパティにDefaultValue属性の値をロードします。
        //! @param [in] defaultValueType DefaultValueAttribute属性をサーチする型を明示的に指定します。nullで省略するとtargetインスタンスからGetType()されます。
        //! @param [in] flags プロパティを列挙する時のフラグを指定します。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToProperties(this System.Object target, System.Type defaultValueType, System.Reflection.BindingFlags flags, bool applyOnNullDefaultValue)
        {
            if (target == null) return;
            System.Type type = target.GetType();
            if (defaultValueType == null) defaultValueType = type;
            System.Reflection.PropertyInfo[] props = type.GetProperties(flags);
            
            System.Text.StringBuilder sb = new System.Text.StringBuilder();
            foreach (System.Reflection.PropertyInfo prop_info in defaultValueType.GetProperties(flags)) sb.AppendLine(prop_info.Name);
            System.Windows.Forms.MessageBox.Show( sb.ToString() );

            if ((props == null) || (props.Length <= 0)) return;
            foreach (System.Reflection.PropertyInfo prop in props)
            {
                if ((prop == null) || (prop.CanWrite == false)) continue;
                System.Reflection.PropertyInfo sourceProp = defaultValueType.GetProperty(prop.Name, flags);
                if (sourceProp == null) continue;
                System.ComponentModel.DefaultValueAttribute attr = sourceProp.GetPrimaryAttribute<System.ComponentModel.DefaultValueAttribute>();
                if (attr == null) continue;
                if ((attr.Value == null) && (applyOnNullDefaultValue == false)) continue;
                try { prop.SetValue(target, attr.Value, null); }
                catch (System.Exception) { }
            }
        }

        //! @brief フィールドにDefaultValue属性の値をロードします。
        //! @details フィールドの列挙フラグにはSystem.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
        //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
        //! applyOnNullDefaultValue にはtrueが設定されます。
        public static void ApplyDefeultValueAttributeToFields(this System.Object target)
        {
            ApplyDefeultValueAttributeToFields(target, true);
        }
        //! @brief フィールドにDefaultValue属性の値をロードします。
        //! @details フィールドの列挙フラグにはSystem.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | 
        //! System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic が指定されます。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToFields(this System.Object target, bool applyOnNullDefaultValue)
        {
            System.Reflection.BindingFlags flags = System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance |
                 System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic;
            ApplyDefeultValueAttributeToFields(target, flags, applyOnNullDefaultValue);
        }
        //! @brief オブジェクトのフィールドにDefaultValue属性の値をロードします。
        //! @param [in] flags フィールドを列挙する時のフラグを指定します。
        //! @param [in] applyOnNullDefaultValue trueを設定すると、DefaultValueAttributeがnullの時でもDefaultValueを適用します。
        public static void ApplyDefeultValueAttributeToFields(this System.Object target, System.Reflection.BindingFlags flags, bool applyOnNullDefaultValue)
        {
            if (target == null) return;
            System.Type type = target.GetType();
            System.Reflection.FieldInfo[] fields = type.GetFields(flags);
            if ((fields == null) || (fields.Length <= 0)) return;
            foreach (System.Reflection.FieldInfo field in fields)
            {
                if ((field == null) || field.IsInitOnly || field.IsLiteral) continue;
                System.ComponentModel.DefaultValueAttribute attr = field.GetPrimaryAttribute<System.ComponentModel.DefaultValueAttribute>();
                if (attr == null) continue;
                if ((attr.Value == null) && (applyOnNullDefaultValue == false)) continue;
                try { field.SetValue(target, attr.Value); }
                catch (System.Exception) { }
            }
        }
    }

    //! @}
}
