using System;

namespace Websocket.Client.Logging.LogProviders
{
	internal class LoupeLogProvider : LogProviderBase
	{
		public LoupeLogProvider()
		{
			if (!LoupeLogProvider.IsLoggerAvailable())
			{
				throw new LibLogException("Gibraltar.Agent.Log (Loupe) not found");
			}
			this._logWriteDelegate = LoupeLogProvider.GetLogWriteDelegate();
		}

		public static bool ProviderIsAvailableOverride { get; set; } = true;

		public override Logger GetLogger(string name)
		{
			return new Logger(new LoupeLogProvider.LoupeLogger(name, this._logWriteDelegate).Log);
		}

		public static bool IsLoggerAvailable()
		{
			return LoupeLogProvider.<ProviderIsAvailableOverride>k__BackingField && LoupeLogProvider.GetLogManagerType() != null;
		}

		private static Type GetTypeFromCoreOrFrameworkDll(object typeName)
		{
			return LogProviderBase.FindType(typeName, new string[] { "Loupe.Agent.NETCore", "Gibraltar.Agent" });
		}

		private static Type GetLogManagerType()
		{
			return LoupeLogProvider.GetTypeFromCoreOrFrameworkDll("Gibraltar.Agent.Log");
		}

		private static LoupeLogProvider.WriteDelegate GetLogWriteDelegate()
		{
			Type logManagerType = LoupeLogProvider.GetLogManagerType();
			Type typeFromCoreOrFrameworkDll = LoupeLogProvider.GetTypeFromCoreOrFrameworkDll("Gibraltar.Agent.LogMessageSeverity");
			Type typeFromCoreOrFrameworkDll2 = LoupeLogProvider.GetTypeFromCoreOrFrameworkDll("Gibraltar.Agent.LogWriteMode");
			return (LoupeLogProvider.WriteDelegate)logManagerType.GetMethod("Write", new Type[]
			{
				typeFromCoreOrFrameworkDll,
				typeof(string),
				typeof(int),
				typeof(Exception),
				typeof(bool),
				typeFromCoreOrFrameworkDll2,
				typeof(string),
				typeof(string),
				typeof(string),
				typeof(string),
				typeof(object[])
			}).CreateDelegate(typeof(LoupeLogProvider.WriteDelegate));
		}

		private readonly LoupeLogProvider.WriteDelegate _logWriteDelegate;

		internal delegate void WriteDelegate(int severity, string logSystem, int skipFrames, Exception exception, bool attributeToException, int writeMode, string detailsXml, string category, string caption, string description, params object[] args);

		internal class LoupeLogger
		{
			internal LoupeLogger(string category, LoupeLogProvider.WriteDelegate logWriteDelegate)
			{
				this._category = category;
				this._logWriteDelegate = logWriteDelegate;
				this._skipLevel = 1;
			}

			public bool Log(LogLevel logLevel, Func<string> messageFunc, Exception exception, params object[] formatParameters)
			{
				if (messageFunc == null)
				{
					return true;
				}
				messageFunc = LogMessageFormatter.SimulateStructuredLogging(messageFunc, formatParameters);
				this._logWriteDelegate(LoupeLogProvider.LoupeLogger.ToLogMessageSeverity(logLevel), "LibLog", this._skipLevel, exception, true, 0, null, this._category, null, messageFunc(), Array.Empty<object>());
				return true;
			}

			private static int ToLogMessageSeverity(LogLevel logLevel)
			{
				switch (logLevel)
				{
				case LogLevel.Trace:
					return TraceEventTypeValues.Verbose;
				case LogLevel.Debug:
					return TraceEventTypeValues.Verbose;
				case LogLevel.Info:
					return TraceEventTypeValues.Information;
				case LogLevel.Warn:
					return TraceEventTypeValues.Warning;
				case LogLevel.Error:
					return TraceEventTypeValues.Error;
				case LogLevel.Fatal:
					return TraceEventTypeValues.Critical;
				default:
					throw new ArgumentOutOfRangeException("logLevel");
				}
			}

			private readonly string _category;

			private readonly LoupeLogProvider.WriteDelegate _logWriteDelegate;

			private readonly int _skipLevel;
		}
	}
}
