#region COPYRIGHT
//
//     THIS IS GENERATED BY TEMPLATE
//     
//     AUTHOR  :     ROYE
//     DATE       :     2010
//
//     COPYRIGHT (C) 2010, TIANXIAHOTEL TECHNOLOGIES CO., LTD. ALL RIGHTS RESERVED.
//
#endregion

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

namespace System.Reflection
{
    /// <summary>
    /// Extension methods for inspecting and working with members.
    /// </summary>
    public static class MemberInfoExtensions
    {
        /// <summary>
        /// Gets the static field or property identified by <paramref name="memberInfo"/>.
        /// </summary>
        public static object Get(this MemberInfo memberInfo)
        {
            var @delegate = (StaticMemberGetter)new MemberGetEmitter(memberInfo, Flags.StaticAnyVisibility).GetDelegate();
            return @delegate();
        }

        /// <summary>
        /// Sets the static field or property identified by <paramref name="memberInfo"/> with <paramref name="value"/>.
        /// </summary>
        public static void Set(this MemberInfo memberInfo, object value)
        {
            var @delegate = (StaticMemberSetter)new MemberSetEmitter(memberInfo, Flags.StaticAnyVisibility).GetDelegate();
            @delegate(value);
        }

        /// <summary>
        /// Gets the instance field or property identified by <paramref name="memberInfo"/> on
        /// the <paramref name="obj"/>.
        /// </summary>
        public static object Get(this MemberInfo memberInfo, object obj)
        {
            var @delegate = (MemberGetter)new MemberGetEmitter(memberInfo, Flags.InstanceAnyVisibility).GetDelegate();
            return @delegate(obj);
        }

        /// <summary>
        /// Sets the instance field or property identified by <paramref name="memberInfo"/> on
        /// the <paramref name="obj"/> object with <paramref name="value"/>.
        /// </summary>
        public static void Set(this MemberInfo memberInfo, object obj, object value)
        {
            var @delegate = (MemberSetter)new MemberSetEmitter(memberInfo, Flags.InstanceAnyVisibility).GetDelegate();
            @delegate(obj, value);
        }

        #region MemberInfo Helpers
        /// <summary>
        /// Gets the system type of the field or property identified by the <paramref name="member"/>.
        /// </summary>
        /// <returns>The system type of the member.</returns>
        public static Type Type(this MemberInfo member)
        {
            var field = member as FieldInfo;
            if (field != null)
            {
                return field.FieldType;
            }
            var property = member as PropertyInfo;
            if (property != null)
            {
                return property.PropertyType;
            }
            throw new NotSupportedException("Can only determine the type for fields and properties.");
        }

        /// <summary>
        /// Determines whether a value can be read from the field or property identified by
        /// the <paramref name="member"/>.
        /// </summary>
        /// <returns>True for fields and readable properties, false otherwise.</returns>
        public static bool IsReadable(this MemberInfo member)
        {
            var property = member as PropertyInfo;
            return member is FieldInfo || (property != null && property.CanRead);
        }

        /// <summary>
        /// Determines whether a value can be assigned to the field or property identified by
        /// the <paramref name="member"/>.
        /// </summary>
        /// <returns>True for updateable fields and properties, false otherwise.</returns>
        public static bool IsWritable(this MemberInfo member)
        {
            var field = member as FieldInfo;
            var property = member as PropertyInfo;
            return (field != null && !field.IsInitOnly && !field.IsLiteral) || (property != null && property.CanWrite);
        }
        /// <summary>
        /// Determines whether the given <paramref name="member"/> is invokable.
        /// </summary>
        /// <returns>True for methods and constructors, false otherwise.</returns>
        public static bool IsInvokable(this MemberInfo member)
        {
            return member is MethodBase;
        }

        /// <summary>
        /// Determines whether the given <paramref name="member"/> is a static member.
        /// </summary>
        /// <returns>True for static fields, properties and methods and false for instance fields,
        /// properties and methods. Throws an exception for all other <see href="MemberTypes" />.</returns>
        public static bool IsStatic(this MemberInfo member)
        {
            var field = member as FieldInfo;
            if (field != null)
                return field.IsStatic;
            var property = member as PropertyInfo;
            if (property != null)
                return property.CanRead ? property.GetGetMethod(true).IsStatic : property.GetSetMethod(true).IsStatic;
            var method = member as MethodInfo;
            if (method != null)
                return method.IsStatic;
            string message = string.Format("Unable to determine IsStatic for member {0}.{1}" +
                "MemberType was {2} but only fields, properties and methods are supported.",
                member.Name, member.MemberType, Environment.NewLine);
            throw new NotSupportedException(message);
        }

        /// <summary>
        /// Determines whether the given <paramref name="member"/> is an instance member.
        /// </summary>
        /// <returns>True for instance fields, properties and methods and false for static fields,
        /// properties and methods. Throws an exception for all other <see href="MemberTypes" />.</returns>
        public static bool IsInstance(this MemberInfo member)
        {
            return !member.IsStatic();
        }

        /// <summary>
        /// Determines whether the given <paramref name="member"/> has the given <paramref name="name"/>.
        /// The comparison uses OrdinalIgnoreCase and allows for a leading underscore in either name
        /// to be ignored.
        /// </summary>
        /// <returns>True if the name is considered identical, false otherwise. If either parameter
        /// is null an exception will be thrown.</returns>
        public static bool HasName(this MemberInfo member, string name)
        {
            string memberName = (member.Name.Length > 0 && member.Name[0] == '_') ?
                member.Name.Substring(1) :
                member.Name;
            name = name.Length > 0 && name[0] == '_' ? name.Substring(1) : name;
            return memberName.Equals(name, StringComparison.OrdinalIgnoreCase);
        }
        #endregion
    }
}
