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

namespace DapperORM.ORMExtention
{
    internal class SqlParameterEmit
    {
        /// <summary>
        /// 获取类型下指定部分字段的委托方法
        /// </summary>
        /// <typeparam name="MType"></typeparam>
        /// <param name="proList"></param>
        /// <returns></returns>
        public static Func<object, Dictionary<string, object>> CreateDicDeleMothed<MType>(IEnumerable<PropertyInfo> proList)
        {
            if (proList == null)
                return null;

            var dicType = typeof(Dictionary<string, object>);
            var dirAddMethod = dicType.GetMethod("Add");
            var dicCtor = dicType.GetConstructor(new[] { typeof(int) });

            var conFunM = new DynamicMethod(string.Concat("ConverTofunc_", Guid.NewGuid().GetHashCode()),
                MethodAttributes.Public | MethodAttributes.Static,
                CallingConventions.Standard, dicType, new[] { typeof(object) }, typeof(MType), false);

            var ilT = conFunM.GetILGenerator();
            ilT.DeclareLocal(dicType);

            ilT.Emit(OpCodes.Nop);
            ilT.Emit(OpCodes.Ldc_I4, proList.Count());
            ilT.Emit(OpCodes.Newobj, dicCtor);

            ilT.Emit(OpCodes.Stloc_0);
            foreach (var pro in proList)
            {
                ilT.Emit(OpCodes.Nop);
                ilT.Emit(OpCodes.Ldloc_0);

                ilT.Emit(OpCodes.Ldstr, pro.Name);
                ilT.Emit(OpCodes.Ldarg_0);
                ilT.Emit(OpCodes.Callvirt, pro.GetGetMethod());
                if (pro.PropertyType.GetTypeInfo().IsValueType)
                {
                    if (pro.PropertyType == typeof(bool))
                    {
                        var boolJumpLabs = new[]
                        {
                            ilT.DefineLabel(),
                            ilT.DefineLabel()
                        };
                        ilT.Emit(OpCodes.Brtrue_S, boolJumpLabs[0]);

                        ilT.Emit(OpCodes.Ldc_I4_0);
                        ilT.Emit(OpCodes.Br_S, boolJumpLabs[1]);

                        ilT.MarkLabel(boolJumpLabs[0]);
                        ilT.Emit(OpCodes.Ldc_I4_1);
                        ilT.MarkLabel(boolJumpLabs[1]);
                        ilT.Emit(OpCodes.Box, typeof(int));
                    }
                    else
                    {
                        ilT.Emit(OpCodes.Box, pro.PropertyType);
                    }
                }
                ilT.Emit(OpCodes.Callvirt, dirAddMethod);
            }
            ilT.Emit(OpCodes.Nop);
            ilT.Emit(OpCodes.Ldloc_0);
            ilT.Emit(OpCodes.Ret);

            return
                conFunM.CreateDelegate(typeof(Func<object, Dictionary<string, object>>)) as
                    Func<object, Dictionary<string, object>>;
        }
    }
}
