#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.Caching;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace System.Reflection.Emit
{
    internal abstract class BaseEmitter
    {
        protected static readonly MethodInfo StructGetMethod = Constants.StructType.GetMethod("get_Value", BindingFlags.Public | BindingFlags.Instance);
        protected static readonly MethodInfo StructSetMethod = Constants.StructType.GetMethod("set_Value", BindingFlags.Public | BindingFlags.Instance);
        private static readonly Cache<int, Delegate> _DelegateCaches = new Cache<int, Delegate>();

        /// <summary>
        /// 初始化实例 <see cref="BaseEmitter"/> class.
        /// </summary>
        /// <param name="callInfo">The call info.</param>
        protected BaseEmitter(CallInfo callInfo)
        {
            CallInfo = callInfo;
        }

        internal protected abstract DynamicMethod CreateDynamicMethod();
        internal protected abstract Delegate CreateDelegate();

        internal protected virtual int GetCacheKey()
        {
            return CallInfo.GetHashCode();
        }

        internal Delegate GetDelegate()
        {
            int cacheKey = GetCacheKey();
            Delegate action = _DelegateCaches.Get(cacheKey);
            if (action == null)
            {
                Method = CreateDynamicMethod();
                Generator = new EmitHelper(Method.GetILGenerator());
                action = CreateDelegate();
                _DelegateCaches.Insert(cacheKey, action, CacheStrategy.Temporary);
            }
            return action;
        }

        internal protected static DynamicMethod CreateDynamicMethod(string name, Type targetType, Type returnType, Type[] paramTypes)
        {
            return new DynamicMethod(name,
                MethodAttributes.Static | MethodAttributes.Public,
                CallingConventions.Standard,
                returnType,
                paramTypes,
                targetType.IsArray ? targetType.GetElementType() : targetType,
                true);
        }

        protected void LoadInnerStructToLocal(byte localPosition)
        {
            Generator
                .castclass(Constants.StructType) // (ValueTypeHolder)wrappedStruct
                .callvirt(StructGetMethod) // <stack>.get_Value()
                .unbox_any(CallInfo.TargetType) // unbox <stack>
                .stloc(localPosition) // localStr = <stack>
                .ldloca_s(localPosition); // load &localStr
        }

        protected void StoreLocalToInnerStruct(byte localPosition)
        {
            StoreLocalToInnerStruct(0, localPosition); // 0: 'this'
        }

        protected void StoreLocalToInnerStruct(byte argPosition, byte localPosition)
        {
            Generator
                .ldarg(argPosition)
                .castclass(Constants.StructType) // wrappedStruct = (ValueTypeHolder)this
                .ldloc(localPosition) // load localStr
                .boxIfValueType(CallInfo.TargetType) // box <stack>
                .callvirt(StructSetMethod); // wrappedStruct.set_Value(<stack>)
        }

        protected CallInfo CallInfo { get; set; }
        protected DynamicMethod Method { get; set; }
        protected EmitHelper Generator { get; set; }
    }
}
