using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace wispro.sp.share
{
    [Serializable]
    public class ExpressTree {
        private ConditionTreeNode root ;

        
        public void AddCondition(LogicEnum logic, FieldCondition fieldCondition)
        {
            FieldConditionNode rightNode = new FieldConditionNode() { FieldCondition = fieldCondition };
            if (root == null)
            {
                Console.WriteLine("根节点为空时添加！");
                root = rightNode;
            }
            else
            {
                Console.WriteLine("根节点为空时添加！");
                OperateNode root1 = new OperateNode() { Operator = logic };
                Console.WriteLine("\t设定右节点！");
                root1.Right = rightNode;
                Console.WriteLine("\t设定左节点！");
                root1.Left = root;
                Console.WriteLine("\t设定根节点为操作节点！");
                root = root1;
                Console.WriteLine((root != null)?"\t根节点不为空": "\t根节点为空");

            }

            //Console.WriteLine($"表达式: {ToExpressString("s")}");
        }

        public string ToExpressString(string prefix)
        {
            if(root == null)
            {
                return null;
            }
            else
            {
                if(root is FieldConditionNode)
                {
                    return ((FieldConditionNode)root).FieldCondition.ToExpressString(prefix);
                }
                else
                {
                    OperateNode operate = (OperateNode)root;
                    if(root.Right != null && root.Left != null)
                    {
                        ExpressTree leftTree = new ExpressTree() { root = root.Left };
                        ExpressTree rightTree = new ExpressTree() { root = root.Right };
                        switch (operate.Operator)
                        {
                            case LogicEnum.And:
                                return $"({leftTree.ToExpressString(prefix)}) && ({rightTree.ToExpressString(prefix)})";
                                break;
                            case LogicEnum.Or:
                                return $"({leftTree.ToExpressString(prefix)}) || ({rightTree.ToExpressString(prefix)})";
                                break;
                            default:
                                return "";
                        }
                    }
                    else
                    {
                        throw (new ApplicationException("无效的表达是树!"));
                    }
                }
            }
        }
    }
    
    [Serializable]
    public class ConditionTreeNode
    {
        public ConditionTreeNode Right { get; set; }
        public ConditionTreeNode Left { get; set; }

        
    }

    [Serializable]
    public class OperateNode : ConditionTreeNode
    {
        public LogicEnum Operator { get; set; }

    }

    [Serializable]
    public class FieldConditionNode : ConditionTreeNode
    {
        public FieldCondition FieldCondition { get; set; }
    }

    [Serializable]
    public class OrderField
    {
        /// <summary>
        /// 排序栏位
        /// </summary>
        public string FieldName { get; set; }

        /// <summary>
        /// 排序 0：顺序；1：倒序
        /// </summary>
        public int Sort { get; set; }
    }

    [Serializable]
    public class FieldCondition
    {
        /// <summary>
        /// 字段名称
        /// </summary>
        public string FieldName { get; set; }
        /// <summary>
        /// 值
        /// </summary>
        public string Value { get; set; }
        /// <summary>
        /// 值类型
        /// </summary>
        public string ValueType { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public OperatorEnum Operator { get; set; }

        public LogicEnum LogicOperate { get; set; }

        public string ToExpressString(string prefix)
        {
            bool isString = (ValueType == typeof(string).ToString());

            switch (Operator) {
                case OperatorEnum.Contains:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName}.Contains(\"{Value}\")";
                    }
                    else
                    {
                        throw (new ApplicationException("Contains 操作只对字符字段有效！"));
                    }
                    break;
                case OperatorEnum.NotContains:
                    if (isString)
                    {
                        return $"!{prefix}.{FieldName}.Contains(\"{Value}\")";
                    }
                    else
                    {
                        throw (new ApplicationException("Contains 操作只对字符字段有效！"));
                    }
                    break;
                case OperatorEnum.Equal:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} == (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} == ({Value})";
                    }
                    break;
                case OperatorEnum.NotEqual:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} != (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} != ({Value})";
                    }
                    break;
                case OperatorEnum.Greater:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} > (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} > ({Value})";
                    }
                    break;
                case OperatorEnum.GreaterEqual:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} >= (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} >= ({Value})";
                    }
                    break;
                case OperatorEnum.Less :
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} < (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} < ({Value})";
                    }
                    break;
                case OperatorEnum.LessEqual:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName} <= (\"{Value}\")";
                    }
                    else
                    {
                        return $"{prefix}.{FieldName} <= ({Value})";
                    }
                    break;
                
                case OperatorEnum.StartsWith:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName}.StartsWith(\"{Value}\")";
                    }
                    else
                    {
                        throw (new ApplicationException("StartsWith 操作只对字符字段有效！"));
                    }
                    break;
                case OperatorEnum.EndWith:
                    if (isString)
                    {
                        return $"{prefix}.{FieldName}.EndWith(\"{Value}\")";
                    }
                    else
                    {
                        throw (new ApplicationException("EndWith 操作只对字符字段有效！"));
                    }
                    break;
                //case OperatorEnum.In:
                //    break;
                //case OperatorEnum.Between:
                //    break;
                default:
                    throw (new ApplicationException("还未实现的操作符号！"));
                

            }
        }
    }

    [Serializable]
    public enum OperatorEnum
    {
        Contains,
        Equal,
        Greater,
        GreaterEqual,
        Less,
        LessEqual,
        NotEqual,
        In,
        Between,
        StartsWith,
        EndWith,
        NotContains
    }

    [Serializable]
    public enum LogicEnum
    {
        And,
        Or
    }

    public static class EFCoreExt
    {
        public static IQueryable<T> OrderConditions<T>(this IQueryable<T> query, IList<OrderField> orderConditions)
        {
            foreach (var orderinfo in orderConditions)
            {
                //var t = typeof(T);

                //var propertyInfo = t.GetProperty(orderinfo.FieldName);
                //var parameter = Expression.Parameter(t);
                //Expression propertySelector = Expression.Property(parameter, propertyInfo);

                //var orderby = Expression.Lambda<Func<T, dynamic>>(propertySelector, parameter);
                //if (orderinfo.Sort == 1)
                //    query = query.OrderByDescending(orderby);
                //else
                //    query = query.OrderBy(orderby);

                var parameter = Expression.Parameter(typeof(T));
                var prop = Expression.PropertyOrField(parameter, orderinfo.FieldName);
                var sortLambda = Expression.Lambda(prop, parameter);

                Expression<Func<IOrderedQueryable<T>>> sortMethod = (() => query.OrderBy<T, object>(k => null));

                if(orderinfo.Sort == 1)
                {
                    sortMethod = (() => query.OrderByDescending<T, object>(k => null));
                }

                var methodCallExpression = (sortMethod.Body as MethodCallExpression);
                if (methodCallExpression == null)
                    throw new Exception("Oops");

                var method = methodCallExpression.Method.GetGenericMethodDefinition();
                var genericSortMethod = method.MakeGenericMethod(typeof(T), prop.Type);
                query = (IQueryable<T>)genericSortMethod.Invoke(query, new object[] { query, sortLambda });


            }
            return query;
        }

        public static IOrderedQueryable<TEntityType> SortMeDynamically<TEntityType>( this IQueryable<TEntityType> query, string propertyname)
        {
            var param = Expression.Parameter(typeof(TEntityType), "s");
            var prop = Expression.PropertyOrField(param, propertyname);
            var sortLambda = Expression.Lambda(prop, param);

            Expression<Func<IOrderedQueryable<TEntityType>>> sortMethod = (() => query.OrderBy<TEntityType, object>(k => null));

            var methodCallExpression = (sortMethod.Body as MethodCallExpression);
            if (methodCallExpression == null)
                throw new Exception("Oops");

            var method = methodCallExpression.Method.GetGenericMethodDefinition();
            var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
            var orderedQuery = (IOrderedQueryable<TEntityType>)genericSortMethod.Invoke(query, new object[] { query, sortLambda });

            return orderedQuery;
        }

        public static IQueryable<T> Pager<T>(this IQueryable<T> query, int pageindex, int pagesize, out int itemCount)
        {
            itemCount = query.Count();
            return query.Skip((pageindex - 1) * pagesize).Take(pagesize);
        }
    }
}
