using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace CPF.Windows.Json.Serializer
{
    [ExpressionBuildType(SerializerBuildTypeEnum.IDictionaryGeneric)]
    internal class IDictionaryGenericBuild : ExpressionJsonFormatter
    {
        internal static Expression Build(Type type, ParameterExpression instanceArg)
        {
            Type[] genericArguments = type.GetGenericArguments();
            if (genericArguments.Length == 0)
            {
                Type idic = type.GetInterface("IDictionary`2");
                genericArguments = idic.GetGenericArguments();
            }

            Type keyType = genericArguments[0];
            Type valueType = genericArguments[1];


            List<Expression> methodCall = new List<Expression>();
            if (!type.IsValueType)
            {
                ConditionalExpression ifNullAppendNull = Expression.IfThen(
                        Expression.Equal(instanceArg, Expression.Constant(null, type)),
                        Expression.Block(ExpressionMembers.Append("null"),
                                            Expression.Return(ExpressionMembers.ReturnLable)
                                             ));

                methodCall.Add(ifNullAppendNull);
            }
            methodCall.Add(ExpressionMembers.IsIgnoreSelfRefernce(instanceArg, ExpressionMembers.RefernceByEmptyType.Dictionary));

            methodCall.Add(ExpressionMembers.Append("{"));

            ParameterExpression isFirst = Expression.Variable(typeof(bool), "isFirst");

            Expression foreachs = ExpressionHelper.ForEach(instanceArg, Expression.Parameter(typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType), "loopVar"),
                    item =>
                    {
                        List<Expression> calls = new List<Expression>
                        {
                                Expression.IfThenElse(
                                    Expression.IsFalse(isFirst),
                                    Expression.Assign(isFirst, Expression.Constant(true)),
                                    ExpressionMembers.Append(",")
                                )
                        };

                        MemberExpression key = Expression.MakeMemberAccess(item, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType).GetProperty("Key"));
                        MemberExpression value = Expression.MakeMemberAccess(item, typeof(KeyValuePair<,>).MakeGenericType(keyType, valueType).GetProperty("Value"));

                        calls.Add(ExpressionMembers.WriteKeyValuePairKey(keyType, key));
                        calls.Add(ExpressionMembers.GetMethodCall(valueType, value));

                        return Expression.Block(new[] { isFirst }, calls);
                    }
                    );

            methodCall.Add(foreachs);


            methodCall.Add(ExpressionMembers.Append("}"));
            methodCall.Add(ExpressionMembers.IsReferenceLoopHandlingIsNoneSerializeStacksArgPop);
            methodCall.Add(Expression.Label(ExpressionMembers.ReturnLable));
            return Expression.Block(methodCall);
        }
    }
}
