using System.Linq.Expressions;
using NHibernate.Linq.Visitors;
using NHibernate.Proxy;
using NHibernate.Util;

namespace NHibernate.Linq
{
	internal static class LinqLogging
	{
		private static readonly INHibernateLogger Log = NHibernateLogger.For("NHibernate.Linq");

		/// <summary>
		/// If debug logging is enabled, log a string such as "msg: expression.ToString()".
		/// </summary>
		internal static void LogExpression(string msg, Expression expression)
		{
			if (Log.IsDebugEnabled())
			{
				// If the expression contains NHibernate proxies, those will be initialized
				// when we call ToString() on the exception. The string representation is
				// generated by a class internal to System.Linq.Expression, so we cannot
				// actually override that logic. Circumvent it by replacing such ConstantExpressions
				// with ParameterExpression, having their name set to the string we wish to display.
				var visitor = new ProxyReplacingExpressionVisitor();
				var preparedExpression = visitor.Visit(expression);

				Log.Debug("{0}: {1}", msg, preparedExpression.ToString());
			}
		}

		/// <summary>
		/// Replace all occurrences of ConstantExpression where the value is an NHibernate
		/// proxy with a ParameterExpression. The name of the parameter will be a string
		/// representing the proxied entity, without initializing it.
		/// </summary>
		private class ProxyReplacingExpressionVisitor : NhExpressionVisitor
		{
			// See also e.g. Remotion.Linq.Clauses.ExpressionVisitors.FormattingExpressionTreeVisitor
			// for another example of this technique.

			protected override Expression VisitConstant(ConstantExpression expression)
			{
				if (expression.Value.IsProxy())
					return Expression.Parameter(expression.Type, ObjectHelpers.IdentityToString(expression.Value));

				return base.VisitConstant(expression);
			}
		}
	}
}