using System;
using System.Text;

namespace BLToolkit.Data.Sql.SqlProvider
{
	public class MsSql2012SqlProvider : MsSqlSqlProvider
	{
		protected override string LimitFormat         { get { return SqlQuery.Select.SkipValue != null ? "FETCH NEXT {0} ROWS ONLY" : null; } }
		protected override string OffsetFormat        { get { return "OFFSET {0} ROWS"; } }
		protected override bool   OffsetFirst         { get { return true;              } }
		protected override bool   BuildAlternativeSql { get { return false;             } }

		protected override ISqlProvider CreateSqlProvider()
		{
			return new MsSql2012SqlProvider();
		}

		protected override void BuildInsertOrUpdateQuery(StringBuilder sb)
		{
			BuildInsertOrUpdateQueryAsMerge(sb, null);
			sb.AppendLine(";");
		}

		protected override void BuildSql(StringBuilder sb)
		{
			if (NeedSkip && SqlQuery.OrderBy.IsEmpty)
			{
				for (var i = 0; i < SqlQuery.Select.Columns.Count; i++)
					SqlQuery.OrderBy.ExprAsc(new SqlValue(i + 1));
			}

			base.BuildSql(sb);
		}

		protected override void BuildFunction(StringBuilder sb, SqlFunction func)
		{
			func = ConvertFunctionParameters(func);

			switch (func.Name)
			{
				case "CASE"     : func = ConvertCase(func.SystemType, func.Parameters, 0); break;
				case "Coalesce" :

					if (func.Parameters.Length > 2)
					{
						var parms = new ISqlExpression[func.Parameters.Length - 1];

						Array.Copy(func.Parameters, 1, parms, 0, parms.Length);
						BuildFunction(sb, new SqlFunction(func.SystemType, func.Name, func.Parameters[0],
						                  new SqlFunction(func.SystemType, func.Name, parms)));
						return;
					}

					var sc = new SqlQuery.SearchCondition();

					sc.Conditions.Add(new SqlQuery.Condition(false, new SqlQuery.Predicate.IsNull(func.Parameters[0], false)));

					func = new SqlFunction(func.SystemType, "IIF", sc, func.Parameters[1], func.Parameters[0]);

					break;
			}

			base.BuildFunction(sb, func);
		}

		static SqlFunction ConvertCase(Type systemType, ISqlExpression[] parameters, int start)
		{
			var len  = parameters.Length - start;
			var name = start == 0 ? "IIF" : "CASE";
			var cond = parameters[start];

			if (start == 0 && SqlExpression.NeedsEqual(cond))
			{
				cond = new SqlQuery.SearchCondition(
					new SqlQuery.Condition(
						false,
						new SqlQuery.Predicate.ExprExpr(cond, SqlQuery.Predicate.Operator.Equal, new SqlValue(1))));
			}

			if (len == 3)
				return new SqlFunction(systemType, name, cond, parameters[start + 1], parameters[start + 2]);

			return new SqlFunction(systemType, name,
				cond,
				parameters[start + 1],
				ConvertCase(systemType, parameters, start + 2));
		}
	}
}
