#region License

/*
 * Copyright © 2002-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#endregion

#region Imports

using System;
using System.Reflection;
using System.Reflection.Emit;

#endregion

namespace Spring.Reflection.Dynamic
{
    /// <summary>
    /// Base class for dynamic members.
    /// </summary>
    /// <author>Aleksandar Seovic</author>
    public class BaseDynamicMember
    {
//#if NET_2_0
//#else
        /// <summary>
        /// Method attributes constant.
        /// </summary>
        protected const MethodAttributes METHOD_ATTRIBUTES =
            MethodAttributes.Public | MethodAttributes.HideBySig
            | MethodAttributes.NewSlot | MethodAttributes.Virtual
            | MethodAttributes.Final;

        private static ConstructorInfo invalidOperationException = 
            typeof(InvalidOperationException).GetConstructor(new Type[] { typeof(string) });

        /// <summary>
        /// Sets up target instance for invocation.
        /// </summary>
        /// <param name="il">IL generator to use.</param>
        /// <param name="targetType">Type of target instance.</param>
        protected static void SetupTargetInstance(ILGenerator il, Type targetType)
        {
            il.Emit(OpCodes.Ldarg_1);
            if (targetType.IsValueType)
            {
                LocalBuilder target = il.DeclareLocal(targetType);
//#if NET_2_0 
//                il.Emit(OpCodes.Unbox_Any, targetType);
//#else
            	il.Emit(OpCodes.Unbox, targetType);
            	il.Emit(OpCodes.Ldobj, targetType);
//#endif          
            	il.Emit(OpCodes.Stloc, target);
                il.Emit(OpCodes.Ldloca, target);
            }
            else
            {
                il.Emit(OpCodes.Castclass, targetType);
            }
        }

        /// <summary>
        /// Sets up invocation argument.
        /// </summary>
        /// <param name="il">IL generator to use.</param>
        /// <param name="argumentType">Argument type.</param>
        /// <param name="argumentPosition">Argument position.</param>
        protected static void SetupArgument(ILGenerator il, Type argumentType, int argumentPosition)
        {
            il.Emit(OpCodes.Ldarg, argumentPosition);
            if (argumentType.IsValueType)
            {
//#if NET_2_0 
//				il.Emit(OpCodes.Unbox_Any, argumentType);
//#else
				il.Emit(OpCodes.Unbox, argumentType);
				il.Emit(OpCodes.Ldobj, argumentType);
//#endif          
            }
            else
            {
                il.Emit(OpCodes.Castclass, argumentType);
            }
        }

        /// <summary>
        /// Generates method invocation code.
        /// </summary>
        /// <param name="il">IL generator to use.</param>
        /// <param name="isStatic">Flag specifying whether method is static.</param>
        /// <param name="isValueType">Flag specifying whether method is on the value type.</param>
        /// <param name="method">Method to invoke.</param>
        protected static void InvokeMethod(ILGenerator il, bool isStatic, bool isValueType, MethodInfo method)
        {
            if (isStatic || isValueType)
            {
                il.EmitCall(OpCodes.Call, method, null);
            }
            else
            {
                il.EmitCall(OpCodes.Callvirt, method, null);
            }
        }

        /// <summary>
        /// Generates code to process return value if necessary.
        /// </summary>
        /// <param name="il">IL generator to use.</param>
        /// <param name="returnValueType">Type of the return value.</param>
        protected static void ProcessReturnValue(ILGenerator il, Type returnValueType)
        {
            if (returnValueType == typeof(void))
            {
                il.Emit(OpCodes.Ldnull);
            }
            else if (returnValueType.IsValueType)
            {
                il.Emit(OpCodes.Box, returnValueType);
            }
        }

        /// <summary>
        /// Generates code that throws <see cref="InvalidOperationException"/>.
        /// </summary>
        /// <param name="il">IL generator to use.</param>
        /// <param name="message">Error message to use.</param>
        protected static void ThrowInvalidOperationException(ILGenerator il, string message)
        {
            il.Emit(OpCodes.Ldstr, message);
            il.Emit(OpCodes.Newobj, invalidOperationException);
            il.Emit(OpCodes.Throw);
        }
//#endif
    }
}