using System;

namespace Websocket.Client.Logging
{
	internal static class LogExtensions
	{
		public static bool IsDebugEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Debug, null, null, LogExtensions.EmptyParams);
		}

		public static bool IsErrorEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Error, null, null, LogExtensions.EmptyParams);
		}

		public static bool IsFatalEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Fatal, null, null, LogExtensions.EmptyParams);
		}

		public static bool IsInfoEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Info, null, null, LogExtensions.EmptyParams);
		}

		public static bool IsTraceEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Trace, null, null, LogExtensions.EmptyParams);
		}

		public static bool IsWarnEnabled(this ILog logger)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			return logger.Log(LogLevel.Warn, null, null, LogExtensions.EmptyParams);
		}

		public static void Debug(this ILog logger, Func<string> messageFunc)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			logger.Log(LogLevel.Debug, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Debug(this ILog logger, string message)
		{
			if (logger.IsDebugEnabled())
			{
				logger.Log(LogLevel.Debug, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Debug(this ILog logger, string message, params object[] args)
		{
			logger.DebugFormat(message, args);
		}

		public static void Debug(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.DebugException(message, exception, args);
		}

		public static void DebugFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsDebugEnabled())
			{
				logger.LogFormat(LogLevel.Debug, message, args);
			}
		}

		public static void DebugException(this ILog logger, string message, Exception exception)
		{
			if (logger.IsDebugEnabled())
			{
				logger.Log(LogLevel.Debug, message.AsFunc<string>(), exception, LogExtensions.EmptyParams);
			}
		}

		public static void DebugException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsDebugEnabled())
			{
				logger.Log(LogLevel.Debug, message.AsFunc<string>(), exception, args);
			}
		}

		public static void Error(this ILog logger, Func<string> messageFunc)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			logger.Log(LogLevel.Error, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Error(this ILog logger, string message)
		{
			if (logger.IsErrorEnabled())
			{
				logger.Log(LogLevel.Error, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Error(this ILog logger, string message, params object[] args)
		{
			logger.ErrorFormat(message, args);
		}

		public static void Error(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.ErrorException(message, exception, args);
		}

		public static void ErrorFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsErrorEnabled())
			{
				logger.LogFormat(LogLevel.Error, message, args);
			}
		}

		public static void ErrorException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsErrorEnabled())
			{
				logger.Log(LogLevel.Error, message.AsFunc<string>(), exception, args);
			}
		}

		public static void Fatal(this ILog logger, Func<string> messageFunc)
		{
			logger.Log(LogLevel.Fatal, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Fatal(this ILog logger, string message)
		{
			if (logger.IsFatalEnabled())
			{
				logger.Log(LogLevel.Fatal, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Fatal(this ILog logger, string message, params object[] args)
		{
			logger.FatalFormat(message, args);
		}

		public static void Fatal(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.FatalException(message, exception, args);
		}

		public static void FatalFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsFatalEnabled())
			{
				logger.LogFormat(LogLevel.Fatal, message, args);
			}
		}

		public static void FatalException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsFatalEnabled())
			{
				logger.Log(LogLevel.Fatal, message.AsFunc<string>(), exception, args);
			}
		}

		public static void Info(this ILog logger, Func<string> messageFunc)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			logger.Log(LogLevel.Info, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Info(this ILog logger, string message)
		{
			if (logger.IsInfoEnabled())
			{
				logger.Log(LogLevel.Info, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Info(this ILog logger, string message, params object[] args)
		{
			logger.InfoFormat(message, args);
		}

		public static void Info(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.InfoException(message, exception, args);
		}

		public static void InfoFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsInfoEnabled())
			{
				logger.LogFormat(LogLevel.Info, message, args);
			}
		}

		public static void InfoException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsInfoEnabled())
			{
				logger.Log(LogLevel.Info, message.AsFunc<string>(), exception, args);
			}
		}

		public static void Trace(this ILog logger, Func<string> messageFunc)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			logger.Log(LogLevel.Trace, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Trace(this ILog logger, string message)
		{
			if (logger.IsTraceEnabled())
			{
				logger.Log(LogLevel.Trace, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Trace(this ILog logger, string message, params object[] args)
		{
			logger.TraceFormat(message, args);
		}

		public static void Trace(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.TraceException(message, exception, args);
		}

		public static void TraceFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsTraceEnabled())
			{
				logger.LogFormat(LogLevel.Trace, message, args);
			}
		}

		public static void TraceException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsTraceEnabled())
			{
				logger.Log(LogLevel.Trace, message.AsFunc<string>(), exception, args);
			}
		}

		public static void Warn(this ILog logger, Func<string> messageFunc)
		{
			LogExtensions.GuardAgainstNullLogger(logger);
			logger.Log(LogLevel.Warn, LogExtensions.WrapLogInternal(messageFunc), null, LogExtensions.EmptyParams);
		}

		public static void Warn(this ILog logger, string message)
		{
			if (logger.IsWarnEnabled())
			{
				logger.Log(LogLevel.Warn, message.AsFunc<string>(), null, LogExtensions.EmptyParams);
			}
		}

		public static void Warn(this ILog logger, string message, params object[] args)
		{
			logger.WarnFormat(message, args);
		}

		public static void Warn(this ILog logger, Exception exception, string message, params object[] args)
		{
			logger.WarnException(message, exception, args);
		}

		public static void WarnFormat(this ILog logger, string message, params object[] args)
		{
			if (logger.IsWarnEnabled())
			{
				logger.LogFormat(LogLevel.Warn, message, args);
			}
		}

		public static void WarnException(this ILog logger, string message, Exception exception, params object[] args)
		{
			if (logger.IsWarnEnabled())
			{
				logger.Log(LogLevel.Warn, message.AsFunc<string>(), exception, args);
			}
		}

		private static void GuardAgainstNullLogger(object logger)
		{
			if (logger == null)
			{
				throw new ArgumentNullException("logger");
			}
		}

		private static void LogFormat(this object logger, LogLevel logLevel, object message, params object[] args)
		{
			((ILog)logger).Log(logLevel, message.AsFunc<string>(), null, args);
		}

		private static Func<T> AsFunc<T>(this T value) where T : class
		{
			return new Func<T>(value.Return<T>);
		}

		private static T Return<T>(this T value)
		{
			return value;
		}

		internal static Func<string> WrapLogSafeInternal(LoggerExecutionWrapper logger, Func<string> messageFunc)
		{
			return delegate
			{
				try
				{
					return messageFunc();
				}
				catch (Exception ex)
				{
					logger.WrappedLogger(LogLevel.Error, () => "Failed to generate log message", ex, LogExtensions.EmptyParams);
				}
				return null;
			};
		}

		private static Func<string> WrapLogInternal(Func<string> messageFunc)
		{
			return () => messageFunc();
		}

		internal static readonly object[] EmptyParams = new object[0];
	}
}
