//==============================================================
//  版权所有：深圳杰文科技
//  文件名：QueryBuilder.cs
//  版本：V1.0  
//  创建者：Jay  ( QQ： 85363208 )
//  创建时间：2017-11-28 16:18
//  创建描述：
//  修改者：
//  修改时间：
//  修改说明： 
//==============================================================

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

namespace Infrastructure.Utilities
{
	public class QueryBuilder
	{
		public enum DatabaseType
		{
			SqlServer,
			Oracle,
			MySql
		}

		public static DatabaseType Database = DatabaseType.SqlServer;

		private string prefix = null;
		private StringBuilder _Where = new StringBuilder();

		public override string ToString()
		{
			string s = _Where.ToString();
			if (string.IsNullOrEmpty(s))
			{
				return "";
			}
			return " " + prefix + " " + s;
		}

		public QueryBuilder()
		{
		}

		public QueryBuilder(string prefix)
		{
			this.prefix = prefix;
		}

		public QueryBuilder Merge(string fieldName, string op, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and {0} {1} {2}", fieldName, op, value));
			}
			return this;
		}

		#region - Greater & Less-

		public QueryBuilder Greater(string fieldName, int value)
		{
			return Merge(fieldName, ">", value.ToString());
		}

		public QueryBuilder Greater(string fieldName, decimal value)
		{
			return Merge(fieldName, ">", value.ToString());
		}

		public QueryBuilder Greater(string fieldName, DateTime value)
		{
			if (Database == DatabaseType.SqlServer)
			{
				_Where.Append(string.Format(" and {0} > '{1}'", fieldName, value.ToString("yyyy-MM-dd HH:mm:ss")));
			}
			return this;
		}

		public QueryBuilder GreaterEqual(string fieldName, int value)
		{
			return Merge(fieldName, ">=", value.ToString());
		}

		public QueryBuilder GreaterEqual(string fieldName, string value)
		{
			return Merge(fieldName, ">=", value);
		}

		public QueryBuilder GreaterEqual(string fieldName, decimal value)
		{
			return Merge(fieldName, ">=", value.ToString());
		}

		public QueryBuilder GreaterEqual(string fieldName, DateTime value)
		{
			if (Database == DatabaseType.SqlServer)
			{
				_Where.Append(string.Format(" and {0} >= '{1}'", fieldName, value.ToString("yyyy-MM-dd HH:mm:ss")));
			}
			return this;
		}

		public QueryBuilder LessEqual(string fieldName, int value)
		{
			return Merge(fieldName, "<=", value.ToString());
		}

		public QueryBuilder LessEqual(string fieldName, decimal value)
		{
			return Merge(fieldName, "<=", value.ToString());
		}

		public QueryBuilder LessEqual(string fieldName, DateTime value)
		{
			if (Database == DatabaseType.SqlServer)
			{
				_Where.Append(string.Format(" and {0} <= '{1}'", fieldName, value.ToString("yyyy-MM-dd HH:mm:ss")));
			}
			return this;
		}

		public QueryBuilder Less(string fieldName, int value)
		{
			return Merge(fieldName, "<", value.ToString());
		}

		public QueryBuilder Less(string fieldName, decimal value)
		{
			return Merge(fieldName, "<", value.ToString());
		}

		public QueryBuilder Less(string fieldName, string value)
		{
			return Merge(fieldName, "<", value);
		}

		public QueryBuilder Less(string fieldName, DateTime value)
		{
			if (Database == DatabaseType.SqlServer)
			{
				_Where.Append(string.Format(" and {0} < '{1}'", fieldName, value.ToString("yyyy-MM-dd HH:mm:ss")));
			}
			return this;
		}

		#endregion

		#region - Equal -

		public QueryBuilder Equal(string fieldName, string value)
		{
			return Equal(fieldName, value, null);
		}

		public QueryBuilder Equal(string fieldName, string value, string nullValue)
		{
			if (!nullValue.IsNullOrEmpty() && value == nullValue)
			{
				return this;
			}

			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and {0} = '{1}'", fieldName, value));
			}
			return this;
		}

		public QueryBuilder Equal<T>(string fieldName, object value, string propertyName = "Id")
		{
			if (value != null && value is T)
			{
				object id = value.GetType().GetProperty("Id").GetValue(value, null);
				Equal(fieldName, id.ToString());
			}
			return this;
		}

		public QueryBuilder Equal(string fieldName, Guid value)
		{
			if (value.ToString() != Guid.Empty.ToString())
			{
				return Equal(fieldName, value.ToString());
			}
			return this;
		}

		public QueryBuilder Equal(string fieldName, bool value)
		{
			return Equal(fieldName, value ? 1 : 0);
		}

		public QueryBuilder Equal(string fieldName, int value)
		{
			_Where.Append(string.Format(" and {0} = {1}", fieldName, value));
			return this;
		}

		public QueryBuilder Equal(string fieldName, int value, int nullValue)
		{
			if (value != nullValue)
			{
				return Equal(fieldName, value);
			}
			return this;
		}

		public QueryBuilder Equal(string fieldName, object value, int nullValue)
		{
			if (value == null)
			{
				return this;
			}

			int n;
			if (int.TryParse(value.ToString(), out n))
			{
				return Equal(fieldName, n, nullValue);
			}
			return this;
		}

		public QueryBuilder Equal(string fieldName, object value)
		{
			if (value == null)
			{
				return this;
			}

			string s = (string)value;
			return Equal(fieldName, s);
		}

		public QueryBuilder Equal(string fieldName, decimal value)
		{
			return Equal(fieldName, value.ToString());
		}

		public QueryBuilder Equal(string fieldName, DateTime value)
		{
			return Equal(fieldName, value.ToString());
		}

		// 不等 
		public QueryBuilder NotEqual(string fieldName, string value)
		{
			if (!string.IsNullOrWhiteSpace(value))
			{
				_Where.Append(string.Format(" and {0} <> '{1}'", fieldName, value));
			}
			return this;
		}

		public QueryBuilder NotEqual(string fieldName, Guid value)
		{
			return NotEqual(fieldName, value.ToString());
		}

		public QueryBuilder NotEqual(string fieldName, int value)
		{
			_Where.Append(string.Format(" and {0} <> {1}", fieldName, value));
			return this;
		}

		public QueryBuilder NotEqual(string fieldName, decimal value)
		{
			return NotEqual(fieldName, value.ToString());
		}

		#endregion

		#region - Like -

		public QueryBuilder Start(string fieldName, object value)
		{
			if (value != null && !string.IsNullOrEmpty(value.ToString()))
			{
				_Where.Append(string.Format(" and {0} like '{1}%'", fieldName, value));
			}
			return this;
		}

		public QueryBuilder Start<T>(string fieldName, object value, string propertyName)
		{
			if (value != null && value is T)
			{
				PropertyInfo pi = typeof(T).GetProperty(propertyName);
				object obj = pi.GetValue(value, null);
				Start(fieldName, obj);
			}
			return this;
		}

		public QueryBuilder End(string fieldName, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and {0} like '%{1}'", fieldName, value));
			}
			return this;
		}

		public QueryBuilder Like(string fieldName, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and {0} like '%{1}%'", fieldName, value));
			}
			return this;
		}

		#endregion

		#region - Between -

		public QueryBuilder Between(string fieldName, DateTime datetime1, DateTime datetime2, bool isUnixDateTime)
		{
			if (DateTimeUtil.IsValidDateRange(datetime1, datetime2)) //日期条件不超过默认日期范围才认为有效。
			{
				if (Database == DatabaseType.SqlServer)
				{
					_Where.Append(String.Format(" and ({0} >= '{1}' and {0} < '{2}')", fieldName, datetime1.ToString(DateTimeUtil.yyyyMMddHHmmss), datetime2.ToString(DateTimeUtil.yyyyMMddHHmmss)));
				}
				else if (Database == DatabaseType.Oracle)
				{
					_Where.Append(String.Format(" and ({0} >= to_date('{1}', 'yyyy-mm-dd  hh24:mi:ss') and {0} < to_date('{2}', 'yyyy-mm-dd  hh24:mi:ss'))", fieldName, datetime1.ToString(DateTimeUtil.yyyyMMddHHmmss), datetime2.ToString(DateTimeUtil.yyyyMMddHHmmss)));
				}
				else if (Database == DatabaseType.MySql)
				{
					if (isUnixDateTime)
					{
						_Where.Append(String.Format(" and ({0} >= {1} and {0} < {2})", fieldName, datetime1.ToUnixDateTime(), datetime2.ToUnixDateTime()));
					}
					else
					{
						_Where.Append(String.Format(" and ({0} >= '{1}' and {0} < '{2}')", fieldName, datetime1.ToString(DateTimeUtil.yyyyMMddHHmmss), datetime2.ToString(DateTimeUtil.yyyyMMddHHmmss)));
					}
				}
			}
			return this;
		}

		public QueryBuilder Between(string fieldName, DateTime datetime1, DateTime datetime2)
		{
			if (DateTimeUtil.IsValidDateRange(datetime1, datetime2)) //日期条件不超过默认日期范围才认为有效。
			{
				if (Database == DatabaseType.SqlServer)
				{
					_Where.Append(String.Format(" and ({0} >= '{1}' and {0} < '{2}')", fieldName, datetime1.ToString(DateTimeUtil.yyyyMMddHHmmss), datetime2.ToString(DateTimeUtil.yyyyMMddHHmmss)));
				}
				else if (Database == DatabaseType.Oracle)
				{
					_Where.Append(String.Format(" and ({0} >= to_date('{1}', 'yyyy-mm-dd  hh24:mi:ss') and {0} < to_date('{2}', 'yyyy-mm-dd  hh24:mi:ss'))", fieldName, datetime1.ToString(DateTimeUtil.yyyyMMddHHmmss), datetime2.ToString(DateTimeUtil.yyyyMMddHHmmss)));
				}
				else if (Database == DatabaseType.MySql)
				{
					_Where.Append(String.Format(" and ({0} >= {1} and {0} < {2})", fieldName, datetime1.ToUnixDateTime(), datetime2.ToUnixDateTime()));
				}
			}
			return this;
		}

		public QueryBuilder Between(string fieldName, string datetime1, string datetime2)
		{
			if (datetime1.IsNullOrEmpty() || datetime2.IsNullOrEmpty())
			{
				return this;
			}
			_Where.Append(String.Format(" and ({0} >= '{1}' and {0} < '{2}')", fieldName, datetime1, datetime2));
			return this;
		}

		#endregion

		#region - And -

		public QueryBuilder And(string text, params object[] args)
		{
			_Where.Append(" and " + string.Format(text, args));
			return this;
		}

		public QueryBuilder And(string text, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(" and " + string.Format(text, value));
			}
			return this;
		}

		#endregion

		#region - In -

		public QueryBuilder In(string fieldName, IEnumerable<Guid> args)
		{
			In<Guid>(fieldName, args, false, false);
			return this;
		}

		public QueryBuilder In(string fieldName, IEnumerable<int> args)
		{
			In<int>(fieldName, args, true, false);
			return this;
		}

		public QueryBuilder In(string fieldName, IEnumerable<long> args)
		{
			In<long>(fieldName, args, true, false);
			return this;
		}

		public QueryBuilder In(string fieldName, IEnumerable<string> args)
		{
			In<string>(fieldName, args, false, false);
			return this;
		}

		public QueryBuilder In(string fieldName, params Guid[] args)
		{
			In<Guid>(fieldName, args.AsEnumerable(), true, false);
			return this;
		}

		public QueryBuilder In(string fieldName, params int[] args)
		{
			In<int>(fieldName, args.AsEnumerable(), true, false);
			return this;
		}

		public QueryBuilder In(string fieldName, params string[] args)
		{
			if (args.Length == 1 && string.IsNullOrEmpty(args[0]))
			{
				return this;
			}
			In<string>(fieldName, args.AsEnumerable(), false, false);
			return this;
		}

		public QueryBuilder NotIn(string fieldName, params string[] args)
		{
			if (args.Length == 1 && string.IsNullOrEmpty(args[0]))
			{
				return this;
			}
			In<string>(fieldName, args.AsEnumerable(), false, true);
			return this;
		}

		private void In<T>(string fieldName, IEnumerable<T> values, bool valueIsNumeric, bool isNot)
		{
			string value = "";
			foreach (T obj in values)
			{
				if (!string.IsNullOrEmpty(value))
				{
					value += ",";
				}

				if (valueIsNumeric)
				{
					value += obj.ToString();
				}
				else if (obj.ToString().StartsWith("'"))
				{
					value += obj.ToString();
				}
				else
				{
					value += "'" + obj.ToString() + "'";
				}
			}

			if (string.IsNullOrEmpty(value))
			{
				return;
			}

			if (isNot)
			{
				_Where.Append(string.Format(" and {0} not in ({1})", fieldName, value));
			}
			else
			{
				_Where.Append(string.Format(" and {0} in ({1})", fieldName, value));
			}
		}

		#endregion

		#region - Null -

		public QueryBuilder IsNull(string fieldName)
		{
			_Where.Append(" and {0} is null ".Fmt(fieldName));
			return this;
		}

		public QueryBuilder IsNotNull(string fieldName)
		{
			_Where.Append(" and {0} is not null ".Fmt(fieldName));
			return this;
		}

		#endregion

		#region - Eq -

		public static string Eq(string fieldName, string value)
		{
			return string.Format(" and {0} = '{1}'", fieldName, value);
		}

		public static string Eq(string fieldName, Guid value)
		{
			return Eq(fieldName, value.ToString());
		}

		public static string Eq(string fieldName, bool value)
		{
			return Eq(fieldName, value ? 1 : 0);
		}

		public static string Eq(string fieldName, int value)
		{
			return Eq(fieldName, value.ToString());
		}

		public static string Eq(string fieldName, object value)
		{
			string s = (string)value;
			return Eq(fieldName, s);
		}

		public static string Eq(string fieldName, decimal value)
		{
			return Eq(fieldName, value.ToString());
		}

		public static string Eq(string fieldName, DateTime value)
		{
			return Eq(fieldName, value.ToString());
		}

		#endregion

		#region - Or Equal -

		public QueryBuilder Or(string fieldName1, string fieldName2, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = '{0}')", value, fieldName1, fieldName2));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, string fieldName3, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = '{0}' or {3} = '{0}')", value, fieldName1, fieldName2, fieldName3));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, string fieldName3, string fieldName4, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = '{0}' or {3} = '{0}' or {4} = '{0}')", value, fieldName1, fieldName2, fieldName3, fieldName4));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, Guid value)
		{
			if (value != Guid.Empty)
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = '{0}')", value.ToString(), fieldName1, fieldName2));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, string fieldName3, Guid value)
		{
			if (value != Guid.Empty)
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = '{0}' or {3} = '{0}')", value.ToString(), fieldName1, fieldName2, fieldName3));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, int value)
		{
			if (value != -1)
			{
				_Where.Append(string.Format(" and ({1} = '{0}' or {2} = {0})", value.ToString(), fieldName1, fieldName2));
			}
			return this;
		}

		public QueryBuilder Or(string fieldName1, string fieldName2, string fieldName3, int value)
		{
			if (value != -1)
			{
				_Where.Append(string.Format(" and ({1} = {0} or {2} = {0} or {3} = {0})", value.ToString(), fieldName1, fieldName2, fieldName3));
			}
			return this;
		}

		#endregion

		#region - Or Like -

		public QueryBuilder OrLike(string fieldName1, string fieldName2, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} like '%{0}%' or {2} like '%{0}%')", value, fieldName1, fieldName2));
			}
			return this;
		}

		public QueryBuilder OrLike(string fieldName1, string fieldName2, string fieldName3, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} like '%{0}%' or {2} like '%{0}%' or {3} like '%{0}%')", value, fieldName1, fieldName2, fieldName3));
			}
			return this;
		}

		public QueryBuilder OrLike(string fieldName1, string fieldName2, string fieldName3, string fieldName4, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} like '%{0}%' or {2} like '%{0}%' or {3} like '%{0}%' or {4} like '%{0}%')", value, fieldName1, fieldName2, fieldName3, fieldName4));
			}
			return this;
		}

		public QueryBuilder OrLike(string fieldName1, string fieldName2, string fieldName3, string fieldName4, string fieldName5, string value)
		{
			if (!string.IsNullOrEmpty(value))
			{
				_Where.Append(string.Format(" and ({1} like '%{0}%' or {2} like '%{0}%' or {3} like '%{0}%' or {4} like '%{0}%' or {5} like '%{0}%')", value, fieldName1, fieldName2, fieldName3, fieldName4, fieldName5));
			}
			return this;
		}

		public QueryBuilder OrLike(IEnumerable<string> fieldNames, string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return this;
			}

			string s = "";
			foreach (string fieldName in fieldNames)
			{
				if (!s.IsNullOrEmpty())
				{
					s += " or ";
				}
				s += string.Format("{0} like '%{1}%'", fieldName, value);
			}
			_Where.Append(" and ({0})".Fmt(s));
			return this;
		}

		#endregion
	}
}
