using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Reflection;
using System.Text.RegularExpressions;
namespace SqlSugar
{
    /// <summary>
    /// ** 描述：拉姆达解析类
    /// ** 创始时间：2015-7-20
    /// ** 修改时间：2016-9-26
    /// ** 作者：sunkaixuan
    /// ** qq：610262374 
    /// ** 使用说明：使用请注名作者
    /// </summary>
    internal partial class ResolveExpress
    {
        public SqlSugarClient Context { get; set; }
        private ISqlBuilder SqlBuilder { get; set; }
        private ResolveExpress() { }
        private string ParSymbol { get; set; }
        public ResolveExpress(SqlSugarClient context)
        {
            this.Context = context;
            this.SqlBuilder = context.SqlBuilder;
            this.ParSymbol = context.SqlBuilder.SqlParameterKeyWord;
        }

        /// <summary>
        /// 解析拉姆达
        /// </summary>
        /// <param name="sameIndex">区分相同参数名的索引号</param>
        public ResolveExpress(int sameIndex = 1)
        {
            this.SameIndex = sameIndex;
        }

        public string SqlWhere = null;
        public ResolveExpressType Type = ResolveExpressType.Single;
        public List<SqlParameter> Paras = new List<SqlParameter>();
        private int SameIndex = 1;
        private SqlSugarClient DB;


        /// <summary>
        /// 解析表达式
        /// </summary>
        /// <param name="re">当前解析对象</param>
        /// <param name="exp">要解析的表达式</param>
        /// <param name="db">数据库访问对象</param>
        public void ResolveExpression(Expression exp)
        {
            DB = this.Context;
            //初始化表达式
            Init(this, exp);
        }

        public string GetLeftString(Expression exp)
        {
            var reval = Regex.Match(exp.ToString(), @"\((.+?)\).+").Groups[1].Value;
            return reval;
        }
        public string[] GetLeftArray(Expression exp)
        {
            var reval = GetLeftString(exp).Split(',');
            return reval;
        }

        /// <summary>
        /// 初始化表达式
        /// </summary>
        /// <param name="re"></param>
        /// <param name="exp"></param>
        private void Init(ResolveExpress re, Expression exp)
        {
            ResolveExpress.MemberType type = ResolveExpress.MemberType.None;
            this.SqlWhere = string.Format(" {1} {0} ", re.CreateSqlElements(exp, ref type, true), Context.SqlBuilder.LambadaQueryBuilder.WhereInfos.Count == 0 ? "WHERE" : "AND");
            //还原bool值
            foreach (var item in ConstantBoolDictionary)
            {
                if (this.SqlWhere.IsValuable())
                {
                    this.SqlWhere = this.SqlWhere.Replace(item.Key.ToString(), item.ConditionalValue);
                }
            }

        }

        /// <summary>
        /// 递归解析表达式路由计算
        /// </summary>
        /// <returns></returns>
        private string CreateSqlElements(Expression exp, ref MemberType type, bool isTure, bool? isComparisonOperator = null)
        {
            //主入口
            if (exp is LambdaExpression)
            {
                return LambdaExpression(exp);
            }
            else if (exp is BinaryExpression)
            {
                return BinaryExpression(exp);
            }
            else if (exp is BlockExpression)
            {
                throw new SqlSugarException(ExpBlockExpression + exp.ToString());
            }
            else if (exp is ConditionalExpression)
            {
                throw new SqlSugarException(ExpConditionalExpression + exp.ToString());
            }
            else if (exp is MethodCallExpression)
            {
                return MethodCallExpression(exp, ref type, isTure);
            }
            else if (exp is ConstantExpression)
            {
                return ConstantExpression(exp, ref type, isComparisonOperator);
            }
            else if (exp is MemberExpression)
            {
                return MemberExpression(ref exp, ref type, isComparisonOperator);
            }
            else if (exp is UnaryExpression)
            {
                return UnaryExpression(exp, ref type);
            }
            else if (exp != null && exp.NodeType.IsIn(ExpressionType.New, ExpressionType.NewArrayBounds, ExpressionType.NewArrayInit))
            {
                throw new SqlSugarException(ExpNew + exp.ToString());
            }
            return null;
        }

        /// <summary>
        /// 将解析值赋给dynInv
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="me"></param>
        /// <param name="dynInv"></param>
        private static void SetMemberValueToDynInv(ref Expression exp, MemberExpression me, ref object dynInv)
        {
            var conExp = me.Expression as ConstantExpression;
            var fieldInfo = me.Member as System.Reflection.FieldInfo;
            if (conExp != null && fieldInfo != null)
            {
                dynInv = (fieldInfo).GetValue((me.Expression as ConstantExpression).Value);
                if (fieldInfo.FieldType.IsEnum)
                {
                    dynInv = Convert.ToInt64(Enum.ToObject(fieldInfo.FieldType, dynInv));
                }
            }
            else
            {

                var memberInfos = new Stack<MemberInfo>();

                // "descend" toward's the root object reference:
                while (exp is MemberExpression)
                {
                    var memberExpr = exp as MemberExpression;
                    memberInfos.Push(memberExpr.Member);
                    if (memberExpr.Expression == null)
                    {
                        if (memberExpr.Member.MemberType == MemberTypes.Property)
                        {
                            PropertyInfo pro = (PropertyInfo)memberExpr.Member;
                            dynInv = pro.GetValue(memberExpr.Member, null);
                            if (dynInv != null && dynInv.GetType().IsClass)
                            {
                                var fieldName = me.Member.Name;
                                var proInfo = dynInv.GetType().GetProperty(fieldName);
                                if (proInfo != null)
                                {
                                    dynInv = proInfo.GetValue(dynInv, null);
                                }
                                var fieInfo = dynInv.GetType().GetField(fieldName);
                                if (fieInfo != null)
                                {
                                    dynInv = fieInfo.GetValue(dynInv);
                                }
                                if (fieInfo == null && proInfo == null)
                                {
                                    throw new SqlSugarException(string.Format(ExpNoSupportObjectOrAttr, dynInv.GetType().FullName, dynInv.GetType().FullName));
                                }
                            }
                            return;
                        }
                        else if (memberExpr.Member.MemberType == MemberTypes.Field)
                        {
                            FieldInfo field = (FieldInfo)memberExpr.Member;
                            dynInv = field.GetValue(memberExpr.Member);
                            if (dynInv != null && dynInv.GetType().IsClass)
                            {
                                var fieldName = me.Member.Name;
                                var proInfo = dynInv.GetType().GetProperty(fieldName);
                                if (proInfo != null)
                                {
                                    dynInv = proInfo.GetValue(dynInv, null);
                                }
                                var fieInfo = dynInv.GetType().GetField(fieldName);
                                if (fieInfo != null)
                                {
                                    dynInv = fieInfo.GetValue(dynInv);
                                }
                                if (fieInfo == null && proInfo == null)
                                {
                                    throw new SqlSugarException(string.Format(ExpNoSupportObjectOrAttr, dynInv.GetType().FullName, dynInv.GetType().FullName));
                                }
                            }
                            return;
                        }

                    }
                    if (memberExpr.Expression == null)
                    {
                        dynInv = ExpErrorUniqueKey;
                        return;
                    }
                    exp = memberExpr.Expression;
                }

                // fetch the root object reference:
                var constExpr = exp as ConstantExpression;
                if (constExpr == null)
                {
                    dynInv = ExpErrorUniqueKey;
                    return;
                }
                var objReference = constExpr.Value;

                // "ascend" back whence we came from and resolve object references along the way:
                while (memberInfos.Count > 0)  // or some other break condition
                {
                    var mi = memberInfos.Pop();
                    if (mi.MemberType == MemberTypes.Property)
                    {
                        var objProp = objReference.GetType().GetProperty(mi.Name);
                        if (objProp == null)
                        {
                            dynInv = ExpErrorUniqueKey;
                            return;
                        }
                        objReference = objProp.GetValue(objReference, null);
                    }
                    else if (mi.MemberType == MemberTypes.Field)
                    {
                        var objField = objReference.GetType().GetField(mi.Name);
                        if (objField == null)
                        {
                            dynInv = ExpErrorUniqueKey;
                            return;
                        }
                        objReference = objField.GetValue(objReference);
                    }
                }
                dynInv = objReference;
            }
        }

        /// <summary>
        /// 添加参数
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        private string AddParas(ref string left, object right)
        {
            string oldLeft = left;
            left = left + SameIndex;
            SameIndex++;
            if (Type != ResolveExpressType.Single)
            {
                left = left.Replace(".", "_");
            }
            if (right == null)
            {
                this.Paras.Add(new SqlParameter(this.ParSymbol + left, DBNull.Value));
            }
            else
            {
                this.Paras.Add(new SqlParameter(this.ParSymbol + left, right));
            }
            return oldLeft;
        }

        /// <summary>
        /// 添加参数并返回右边值
        /// </summary>
        /// <param name="left"></param>
        /// <param name="right"></param>
        /// <returns></returns>
        private string AddParasReturnRight(object left, ref string right)
        {
            string oldRight = right;
            right = right + SameIndex;
            SameIndex++;
            if (Type != ResolveExpressType.Single)
            {
                right = right.Replace(".", "_");
            }
            if (left == null)
            {
                this.Paras.Add(new SqlParameter(this.ParSymbol + right, DBNull.Value));
            }
            else
            {
                this.Paras.Add(new SqlParameter(this.ParSymbol + right, left));
            }
            return oldRight;
        }

        /// <summary>
        /// 根据条件生成对应的sql查询操作符
        /// </summary>
        /// <param name="expressiontype"></param>
        /// <returns></returns>
        private string GetOperator(ExpressionType expressiontype)
        {
            switch (expressiontype)
            {
                case ExpressionType.And:
                case ExpressionType.AndAlso:
                    return " AND ";
                case ExpressionType.Equal:
                    return " =";
                case ExpressionType.GreaterThan:
                    return " >";
                case ExpressionType.GreaterThanOrEqual:
                    return ">=";
                case ExpressionType.LessThan:
                    return "<";
                case ExpressionType.LessThanOrEqual:
                    return "<=";
                case ExpressionType.NotEqual:
                    return "<>";
                case ExpressionType.Or:
                case ExpressionType.OrElse:
                    return " OR ";
                case ExpressionType.Add:
                case ExpressionType.AddChecked:
                    return "+";
                case ExpressionType.Subtract:
                case ExpressionType.SubtractChecked:
                    return "-";
                case ExpressionType.Divide:
                    return "/";
                case ExpressionType.Multiply:
                case ExpressionType.MultiplyChecked:
                    return "*";
                default:
                    throw new SqlSugarException(string.Format(OperatorError + expressiontype));
            }
        }

    }
}
