using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Text;
using Wayne.Lib.IO;

namespace Wayne.Lib.Log
{
    /// <summary>
    /// Logger is a static class used to create log objects.
    /// </summary>
    public static class Logger
    {
        #region Fields

        /// <summary>
        /// Default date and time format to use in log files.
        /// </summary>
        public const string DefaultDateTimeFormat = "HH':'mm':'ss'.'fff'[#]'";

        private const int DefaultDebugMarkerBroadcastPort = 11001;

        private static readonly List<DebugLogger> OutstandingLogObjects = new List<DebugLogger>();
        private static readonly object OutstandingLogObjectsLock = new object(); // Locking object to safely access the OutstandingLogObjects.
        private static readonly Dictionary<string, IEventSubscriber> EventSubscriberDict = new Dictionary<string, IEventSubscriber>();
        private static readonly object EventSubscriberDictSyncObj = new object();
        private static readonly Dictionary<string, ExternalLogWriter> ExternalLogWriterNameDict = new Dictionary<string, ExternalLogWriter>();
        private static readonly Dictionary<ExternalLogWriterWrapper, ExternalLogWriter> ExternalLogWriterWrapperDict = new Dictionary<ExternalLogWriterWrapper, ExternalLogWriter>();
        private static readonly object ExternalLogWriterDictSyncObj = new object();
        private static readonly byte[] DebugMarkerBroadcastReceiverBuffer = new byte[100];
        private static readonly List<string> DebugConfigFileNameList = new List<string>();
        private static readonly List<string> EventConfigFileNameList = new List<string>();
        private static readonly List<string[]> DebugConfigFilesList = new List<string[]>();
        private static readonly List<string[]> EventConfigFilesList = new List<string[]>();
        private static readonly List<LogConfigBuilder> DebugConfigBuilderList = new List<LogConfigBuilder>();
        private static readonly List<LogConfigBuilder> EventConfigBuilderList = new List<LogConfigBuilder>();
        private static readonly List<LogConfigOutput> LeftoverLinesLogConfigOutputs = new List<LogConfigOutput>();
        private static readonly List<LogConfigOutput> LeftoverEntitiesLogConfigOutputs = new List<LogConfigOutput>();

        private static bool active;
        private static LoggerThread loggerThread;
        private static LoggerThread eventLoggerThread;
        private static LogConfig debugConfig;
        private static LogConfig eventConfig;
        private static DotNetLog dotNetLog;
        private static GlobalLogEntryCounter globalLogEntryCounter;
        private static IPEndPoint debugMarkerBroadcastAddress;
        private static System.Net.Sockets.Socket debugMarkerBroadcastReceiverSocket;
        #endregion

        #region Construction

        static Logger()
        {
            Reset();
        }

        #endregion

        #region Internal Properties

        /// <summary>
        /// Tells whether someone has called the Close() method. 
        /// </summary>
        public static bool IsClosed
        {
            get { return !active; }
        }

        /// <summary>
        /// Internal access to the DebugConfig.
        /// </summary>
        internal static LogConfig DebugConfig
        {
            get { return debugConfig; }
        }

        /// <summary>
        /// Internal access to the GlobalLogEntryCounter.
        /// </summary>
        internal static GlobalLogEntryCounter GlobalLogEntryCounter
        {
            get { return globalLogEntryCounter; }
        }

        #endregion

        #region Internal Methods

        /// <summary>
        /// This method is used to fire an event instead of throwing an exception if something
        /// "crashes" in a thread (in order to keep the thread alive but still "report" exceptions).
        /// </summary>
        /// <param name="exception"></param>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        internal static void FireOnThreadException(LogException exception)
        {
            try
            {
                if (OnThreadException != null)
                    OnThreadException(null, new EventArgs<LogException>(exception));
            }
            catch (Exception) { }
        }

        /// <summary>
        /// Internal method that is used by the EventLogSubscriptionLogWriter to publish an
        /// event entry to a specific subscriber.
        /// </summary>
        /// <param name="eventSubscriberId"></param>
        /// <param name="eventLogEntry"></param>
        /// <param name="storageType"></param>
        internal static void PublishEventLog(string eventSubscriberId, EventLogEntry eventLogEntry,
            LogConfigEventLogStorageType storageType)
        {
            //eventLogStorage.Add(eventSubscriberId, eventLogEntry, storageType);

            IEventSubscriber eventSubscriber;
            lock (EventSubscriberDictSyncObj)
            {
                if (!EventSubscriberDict.TryGetValue(eventSubscriberId, out eventSubscriber))
                    eventSubscriber = null;
            }

            if (eventSubscriber != null)
                eventSubscriber.HandleEvent(eventLogEntry);
        }

        #endregion

        #region Internal Methods: Persistent log objects

        internal static void RegisterPersistentLogObject(DebugLogger debugLogger)
        {
            lock (OutstandingLogObjectsLock)
            {
                if (!OutstandingLogObjects.Contains(debugLogger))
                    OutstandingLogObjects.Add(debugLogger);
            }
        }

        internal static void UnregisterPersistentLogObject(DebugLogger debugLogger)
        {
            lock (OutstandingLogObjectsLock)
            {
                if (OutstandingLogObjects.Contains(debugLogger))
                    OutstandingLogObjects.Remove(debugLogger);
            }
        }

        #endregion

        #region Public Events

        /// <summary>
        /// An event that is fired when the logging thread is catching an exception.
        /// </summary>
        public static event EventHandler<Wayne.Lib.EventArgs<LogException>> OnThreadException;

        #endregion

        #region Public Properties

        /// <summary>
        /// The current debug log configuration file.
        /// </summary>
        [Obsolete("This property is no longer supported. Use the property DebugLoggingConfigured to check whether it's active or not.", true)]
        public static string DebugConfigFileName
        {
            get { return string.Empty; }
        }

        /// <summary>
        /// The current event log configuration file.
        /// </summary>
        [Obsolete("This property is no longer supported. Use the property EventLoggingConfigured to check whether it's active or not.", true)]
        public static string EventConfigFileName
        {
            get { return string.Empty; }
        }

        /// <summary>
        /// Is any debug logging configured?
        /// </summary>
        public static bool DebugLoggingConfigured
        {
            get { return debugConfig.IsConfigured; }
        }

        /// <summary>
        /// Is any event logging configured?
        /// </summary>
        public static bool EventLoggingConfigured
        {
            get { return eventConfig.IsConfigured; }
        }

        /// <summary>
        /// Should the logger be synchronized or not?
        /// Default is false (the log writing is performed in another thread).
        /// </summary>
        public static bool Synchronized { get; set; }

        #endregion

        #region Public Methods: ConfigFile handling

        /// <summary>
        /// Clear all loaded log configuration .
        /// </summary>
        public static void ClearConfigFiles()
        {
            if (!active)
                return;

            DebugConfigFileNameList.Clear();
            EventConfigFileNameList.Clear();
            DebugConfigFilesList.Clear();
            EventConfigFilesList.Clear();
            DebugConfigBuilderList.Clear();
            EventConfigBuilderList.Clear();
            LeftoverLinesLogConfigOutputs.Clear();
            LeftoverEntitiesLogConfigOutputs.Clear();

            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration from the specified configuration file.
        /// </summary>
        /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
        /// <param name="eventConfigFileName">Log configuration for the event logging.</param>
        public static void SetConfigFile(string debugConfigFileName, string eventConfigFileName)
        {
            if (!active)
                return;

            ClearConfigFiles();
            AddDebugConfigFile(debugConfigFileName);
            AddEventConfigFile(eventConfigFileName);
        }

        /// <summary>
        /// Reloads the configuration from the specified configuration file for the debug logging. To activate the event logging
        /// SetConfigFile(string,string) should be called.
        /// </summary>
        /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
        public static void SetConfigFile(string debugConfigFileName)
        {
            if (!active)
                return;

            ClearConfigFiles();
            AddDebugConfigFile(debugConfigFileName);
        }

        /// <summary>
        /// Reloads the configuration, adding the given debug log config file.
        /// </summary>
        /// <param name="debugConfigFileName">Log configuration for the debug logging.</param>
        public static void AddDebugConfigFile(string debugConfigFileName)
        {
            if (!active)
                return;

            DebugConfigFileNameList.Add(debugConfigFileName);
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given debug log config file.
        /// </summary>
        /// <param name="logConfigBuilders">Log configurations for the debug logging.</param>
        public static void AddDebugConfigFile(params LogConfigBuilder[] logConfigBuilders)
        {
            if (!active)
                return;

            DebugConfigBuilderList.AddRange(logConfigBuilders);
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given debug log config file.
        /// </summary>
        /// <param name="debugConfigFileStream">Log configuration for the debug logging.</param>
        public static void AddDebugConfigFile(Stream debugConfigFileStream)
        {
            AddDebugConfigFile(debugConfigFileStream, Encoding.UTF8);
        }

        /// <summary>
        /// Reloads the configuration, adding the given debug log config file.
        /// </summary>
        /// <param name="debugConfigFileStream">Log configuration for the debug logging.</param>
        /// <param name="encoding">The encoding of the stream.</param>
        public static void AddDebugConfigFile(Stream debugConfigFileStream, Encoding encoding)
        {
            if (!active)
                return;

            DebugConfigFilesList.Add(GetFileLines(debugConfigFileStream, encoding));
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given event log config file.
        /// </summary>
        /// <param name="eventConfigFileName">Log configuration for the event logging.</param>
        public static void AddEventConfigFile(string eventConfigFileName)
        {
            if (!active)
                return;

            EventConfigFileNameList.Add(eventConfigFileName);
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given event log config file.
        /// </summary>
        /// <param name="logConfigBuilders">Log configurations for the event logging.</param>
        public static void AddEventConfigFile(params LogConfigBuilder[] logConfigBuilders)
        {
            if (!active)
                return;

            EventConfigBuilderList.AddRange(logConfigBuilders);
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given event log config file.
        /// </summary>
        /// <param name="eventConfigFileStream">Log configuration for the event logging.</param>
        public static void AddEventConfigFile(Stream eventConfigFileStream)
        {
            AddDebugConfigFile(eventConfigFileStream, Encoding.UTF8);
        }

        /// <summary>
        /// Reloads the configuration, adding the given event log config file.
        /// </summary>
        /// <param name="eventConfigFileStream">Log configuration for the event logging.</param>
        /// <param name="encoding">The encoding of the stream.</param>
        public static void AddEventConfigFile(Stream eventConfigFileStream, Encoding encoding)
        {
            if (!active)
                return;

            EventConfigFilesList.Add(GetFileLines(eventConfigFileStream, encoding));
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given leftover log line output.
        /// </summary>
        /// <param name="output">An output to receive leftover log lines.</param>
        public static void AddLeftoverLinesOutput(LogConfigOutput output)
        {
            if (!active)
                return;

            LeftoverLinesLogConfigOutputs.Add((LogConfigOutput)output.Clone());
            RefreshConfig();
        }

        /// <summary>
        /// Reloads the configuration, adding the given leftover log entity output.
        /// </summary>
        /// <param name="output">An output to receive leftover log entities.</param>
        public static void AddLeftoverEntitiesOutput(LogConfigOutput output)
        {
            if (!active)
                return;

            LeftoverEntitiesLogConfigOutputs.Add((LogConfigOutput)output.Clone());
            RefreshConfig();
        }

        private static string[] GetFileLines(Stream fileStream, Encoding encoding)
        {
            List<string> lines = new List<string>();
            using (StreamReader reader = new StreamReader(fileStream, encoding))
            {
                while (!reader.EndOfStream)
                    lines.Add(reader.ReadLine());
            }
            return lines.ToArray();
        }

        #endregion

        #region Public Methods: Refresh

        /// <summary>
        /// Re-loads the configuration for the logging.
        /// </summary>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        [SuppressMessage("Microsoft.Security", "CA2102:CatchNonClsCompliantExceptionsInGeneralHandlers")]
        public static void RefreshConfig()
        {
            if (!active)
                return;

            InitDebugMarkerBroadcastListener();

            // Refresh the DebugConfig.
            List<string[]> debugConfigFiles = new List<string[]>(DebugConfigFilesList);
            foreach (string fileName in DebugConfigFileNameList)
                debugConfigFiles.Add(FileSupport.LoadToStringArray(fileName));
            if (DebugConfigBuilderList.Count > 0)
                debugConfigFiles.Add(LogConfigBuilder.GetLogConfigFileLines(DebugConfigBuilderList.ToArray()));
            DebugConfig.Refresh(debugConfigFiles, LeftoverLinesLogConfigOutputs.ToArray(), LeftoverEntitiesLogConfigOutputs.ToArray());


            // Refresh the EventConfig.
            List<string[]> eventConfigFiles = new List<string[]>(EventConfigFilesList);
            foreach (string fileName in EventConfigFileNameList)
                eventConfigFiles.Add(FileSupport.LoadToStringArray(fileName));
            if (EventConfigBuilderList.Count > 0)
                eventConfigFiles.Add(LogConfigBuilder.GetLogConfigFileLines(EventConfigBuilderList.ToArray()));
            eventConfig.Refresh(eventConfigFiles, new LogConfigOutput[0], new LogConfigOutput[0]);


            // Start the thread if it's not yet started (first time it's null).
            if ((loggerThread == null) && (debugConfigFiles.Count > 0))
                loggerThread = new LoggerThread(LogType.Debug, debugConfig);
            if ((eventLoggerThread == null) && (eventConfigFiles.Count > 0))
                eventLoggerThread = new LoggerThread(LogType.Event, eventConfig);


            // To prevent locking the list while iterating, take a copy and iterate through the copy.
            // The danger of doing this is that an object could be removed after the copy and before its
            // Invalidate-method is called. But nothing really bad can happen due to this...
            List<DebugLogger> outstandingLogObjectsCopy;
            lock (OutstandingLogObjectsLock)
            {
                outstandingLogObjectsCopy = new List<DebugLogger>(OutstandingLogObjects);
            }
            foreach (DebugLogger debugLogger in outstandingLogObjectsCopy)
            {
                try
                {
                    debugLogger.Invalidate();
                }
                catch (Exception) { }
            }

            // Check if the dotNetLog should be active or not.
            dotNetLog.CheckActive();
        }

        #endregion

        #region Public Methods: Reset

        /// <summary>
        /// Resets the logger completely.
        /// </summary>
        public static void Reset()
        {
            Close();
            active = true;
            debugConfig = new LogConfig();
            eventConfig = new LogConfig();
            dotNetLog = new DotNetLog();
            globalLogEntryCounter = new GlobalLogEntryCounter();
        }

        #endregion

        #region Public Methods: Close

        /// <summary>
        /// Closes the logger. This should be done as the last things before the application terminates.        
        /// </summary>
        public static void Close()
        {
            if (active)
            {
                // Shut down the config.
                if (debugConfig != null)
                {
                    debugConfig.Dispose();
                    debugConfig = null;
                }

                if (eventConfig != null)
                {
                    eventConfig.Dispose();
                    eventConfig = null;
                }

                if (dotNetLog != null)
                {
                    // Shut down the dotNetLog.
                    dotNetLog.Dispose();
                    dotNetLog = null;
                }

                ExternalLogWriterNameDict.Clear();
                ExternalLogWriterWrapperDict.Clear();

                // Now we're closed.
                active = false;

                // Shut down the loggerThread.
                if (loggerThread != null)
                {
                    loggerThread.Dispose();
                    loggerThread = null;
                }

                if (eventLoggerThread != null)
                {
                    eventLoggerThread.Dispose();
                    eventLoggerThread = null;
                }

                if (debugMarkerBroadcastReceiverSocket != null)
                {
                    try
                    {
                        if (debugMarkerBroadcastReceiverSocket.Connected)
                        {
                            debugMarkerBroadcastReceiverSocket.Shutdown(SocketShutdown.Both);
                        }
                    }
                    catch { }
                    try
                    {
                        debugMarkerBroadcastReceiverSocket.Close();
                    }
                    catch { }
                    debugMarkerBroadcastReceiverSocket = null;
                }

                if (globalLogEntryCounter != null)
                {
                    globalLogEntryCounter.Dispose();
                    globalLogEntryCounter = null;
                }

                debugMarkerBroadcastAddress = null;

                DebugConfigFileNameList.Clear();
                DebugConfigFilesList.Clear();
                EventConfigFileNameList.Clear();
                EventConfigFilesList.Clear();
                DebugConfigBuilderList.Clear();
                EventConfigBuilderList.Clear();
                OutstandingLogObjects.Clear();
                EventSubscriberDict.Clear();
                LeftoverLinesLogConfigOutputs.Clear();
                LeftoverEntitiesLogConfigOutputs.Clear();

                Synchronized = false;
            }
        }

        #endregion

        #region Public Methods: LogEntry

        /// <summary>
        /// Logs the given LogEntry.
        /// </summary>
        /// <param name="logEntry">The LogEntry to log.</param>
        public static void AddEntry(LogEntry logEntry)
        {
            if (!active)
                return;

            if (loggerThread != null)
                loggerThread.AddEntry(logEntry);

            if ((eventLoggerThread != null) && (logEntry is EventLogEntry))
                eventLoggerThread.AddEntry(logEntry);  // eventThread has higher priority?
        }

        /// <summary>
        /// Logs the given ExceptionLogEntry.
        /// </summary>
        /// <param name="logEntry">The LogEntry to log.</param>
        public static void AddExceptionLogEntry(ExceptionLogEntry logEntry)
        {
            AddEntry(logEntry);
        }

        /// <summary>
        /// Logs the given ErrorLogEntry.
        /// </summary>
        /// <param name="logEntry">The LogEntry to log.</param>
        public static void AddErrorLogEntry(ErrorLogEntry logEntry)
        {
            AddEntry(logEntry);
        }

        /// <summary>
        /// Logs the given EventLogEntry.
        /// </summary>
        /// <param name="logEntry">The LogEntry to log.</param>
        public static void AddEventLogEntry(EventLogEntry logEntry)
        {
            AddEntry(logEntry);
        }

        #endregion

        #region Public Methods: Unregister entity

        /// <summary>
        /// Removes the specified entity from the internal filter buffers.
        /// </summary>
        /// <param name="entity"></param>
        public static void UnregisterEntity(IIdentifiableEntity entity)
        {
            if (debugConfig != null)
                debugConfig.UnregisterEntity(entity);
            if (eventConfig != null)
                eventConfig.UnregisterEntity(entity);
        }

        #endregion

        #region Public Methods for Event Subscribers

        /// <summary>
        /// Remove an event log from the storage. This method should be called from a registered IEventSubscriber when 
        /// it has handled an event.
        /// </summary>
        /// <param name="eventSubscriber"></param>
        /// <param name="eventLogEntry"></param>
        public static void EventLogHandled(IEventSubscriber eventSubscriber, EventLogEntry eventLogEntry)
        {
            //eventLogStorage.Remove(eventSubscriber, eventLogEntry);
        }

        /// <summary>
        /// Register an IEventSubscriber, so it can start receiving events. The event subscriber will be sent the pending events that 
        /// has been stored since the subscriber was registered the last time.
        /// </summary>
        /// <param name="eventSubscriber"></param>
        public static void RegisterEventSubscriber(IEventSubscriber eventSubscriber)
        {
            if (eventSubscriber == null)
                throw new ArgumentNullException("eventSubscriber");

            lock (EventSubscriberDictSyncObj)
            {
                EventSubscriberDict[eventSubscriber.SubscriberId] = eventSubscriber;
            }

            //EventLogEntry[] entries = eventLogStorage.GetPendingEvents(eventSubscriber.SubscriberId);
            //foreach (EventLogEntry entry in entries)
            //    eventSubscriber.HandleEvent(entry);
        }

        /// <summary>
        /// Unregister a registered IEventSubscriber.
        /// </summary>
        /// <param name="eventSubscriber"></param>
        public static void UnregisterEventSubscriber(IEventSubscriber eventSubscriber)
        {
            if (eventSubscriber == null)
                throw new ArgumentNullException("eventSubscriber");

            lock (EventSubscriberDictSyncObj)
            {
                EventSubscriberDict.Remove(eventSubscriber.SubscriberId);
            }
        }

        #endregion

        #region ExternalLogging

        private static string GetExternalLogWriterKey(ExternalLogWriter externalLogWriter)
        {
            return string.Format("{0}#{1}", externalLogWriter.ExternalLogType, externalLogWriter.ExternalLogName);
        }

        private static string GetExternalLogWriterKey(ExternalLogWriterWrapper externalLogWriterWrapper)
        {
            return string.Format("{0}#{1}", externalLogWriterWrapper.ExternalLogType, externalLogWriterWrapper.ExternalLogName);
        }

        /// <summary>
        /// Register an ExternalLogWriter.
        /// </summary>
        /// <param name="externalLogWriter">The external log writer to register.</param>
        public static void RegisterExternalLogger(ExternalLogWriter externalLogWriter)
        {
            if (ExternalLogWriterDictSyncObj != null)
                lock (ExternalLogWriterDictSyncObj)
                    ExternalLogWriterNameDict[GetExternalLogWriterKey(externalLogWriter)] = externalLogWriter;
        }

        /// <summary>
        /// Unregister an ExternalLogWriter.
        /// </summary>
        /// <param name="externalLogWriter">The external log writer to unregister.</param>
        public static void UnregisterExternalLogger(ExternalLogWriter externalLogWriter)
        {
            if (ExternalLogWriterDictSyncObj != null)
                lock (ExternalLogWriterDictSyncObj)
                {
                    ExternalLogWriterNameDict.Remove(GetExternalLogWriterKey(externalLogWriter));
                    bool found;
                    do
                    {
                        found = false;
                        foreach (KeyValuePair<ExternalLogWriterWrapper, ExternalLogWriter> pair in ExternalLogWriterWrapperDict)
                        {
                            if (pair.Value == externalLogWriter)
                            {
                                ExternalLogWriterWrapperDict.Remove(pair.Key);
                                found = true;
                                break;
                            }
                        }
                    }
                    while (found);
                }
        }

        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        internal static void PerformExternalLogEntry(ExternalLogWriterWrapper externalLogWriterWrapper, LogEntry logEntry)
        {
            if (ExternalLogWriterDictSyncObj != null)
            {
                lock (ExternalLogWriterDictSyncObj)
                {
                    ExternalLogWriter externalLogWriter;
                    if (!ExternalLogWriterWrapperDict.TryGetValue(externalLogWriterWrapper, out externalLogWriter))
                    {
                        if (ExternalLogWriterNameDict.TryGetValue(GetExternalLogWriterKey(externalLogWriterWrapper), out externalLogWriter))
                        {
                            externalLogWriter.InitParametersInternal(externalLogWriterWrapper.Parameters);
                            ExternalLogWriterWrapperDict[externalLogWriterWrapper] = externalLogWriter;
                        }
                    }

                    if (externalLogWriter != null)
                    {
                        try
                        {
                            if (externalLogWriter.Active)
                            {
                                string text = LogTextWriting.GetLogEntryText(externalLogWriterWrapper, logEntry,
                                    externalLogWriterWrapper.WritingParameters);
                                externalLogWriter.LogInternal(logEntry, text);
                            }
                        }
                        catch { }
                    }
                }
            }
        }

        /// <summary>
        /// Get the LogTextWritingParameters for the requested ExternalLogWriter.
        /// </summary>
        /// <param name="externalLogWriter">The ExternalLogWriter to get the parameters of.</param>
        [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#")]
        internal static LogTextWritingParameters GetExternalLoggerWritingParameters(ExternalLogWriter externalLogWriter)
        {
            if (ExternalLogWriterDictSyncObj != null)
            {
                lock (ExternalLogWriterDictSyncObj)
                {
                    foreach (KeyValuePair<ExternalLogWriterWrapper, ExternalLogWriter> pair in ExternalLogWriterWrapperDict)
                    {
                        if (pair.Value == externalLogWriter)
                        {
                            return pair.Key.WritingParameters;
                        }
                    }
                }
            }
            return null;
        }

        #endregion

        #region Debug Marker Broadcasting

        /// <summary>
        /// Broadcasts a debug marker to be inserted into all active logfiles.
        /// </summary>
        /// <param name="debugMarker">The text to insert into all the log files.</param>
      //  public static void InjectDebugMarker(string debugMarker)
      //  {
      //      try
      //      {
      //          System.Net.Sockets.Socket transmitterSocket = null;
      //          try
      //          {
      //              try
      //              {
						//transmitterSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.IP);
      //                  transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
      //                  var lingerOption = new LingerOption(true, 0);
      //                  transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Linger, lingerOption);
      //                  transmitterSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, 1);
      //                  transmitterSocket.Bind(new IPEndPoint(IPAddress.Any, 0)); // Bind the socket to the "any" end point.

      //                  byte[] data = Encoding.UTF8.GetBytes(debugMarker);
      //                  int offset = 0;
      //                  int size = data.Length;
      //                  if (size > DebugMarkerBroadcastReceiverBuffer.Length)
      //                      size = DebugMarkerBroadcastReceiverBuffer.Length;
      //                  int currentSentSize;
      //                  do
      //                  {
      //                      currentSentSize = transmitterSocket.SendTo(data, offset, size, SocketFlags.None, GetDebugMarkerBroadcastIPEndPoint());
      //                      offset += currentSentSize;
      //                      size -= currentSentSize;
      //                  }
      //                  while ((size > 0) && (currentSentSize > 0));
      //              }
      //              catch (Exception) { }
      //          }
      //          finally
      //          {
      //              if (transmitterSocket != null)
      //              {
      //                  try
      //                  {
      //                      transmitterSocket.Shutdown(SocketShutdown.Both);
      //                  }
      //                  finally
      //                  {
      //                      transmitterSocket.Close();
      //              }
      //          }
      //      }
      //      }
      //      catch (Exception) { }
      //  }

        private static void InitDebugMarkerBroadcastListener()
        {
            if (debugMarkerBroadcastReceiverSocket == null)
            {
                try
                {
					debugMarkerBroadcastReceiverSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.IP);

                    debugMarkerBroadcastReceiverSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, 1);
                    //debugMarkerBroadcastReceiverSocket.Bind(new IPEndPoint(IPAddress.Any, GetDebugMarkerBroadcastIPEndPoint().Port)); // Bind the socket to the "any" end point.
                    debugMarkerBroadcastReceiverSocket.BeginReceive(DebugMarkerBroadcastReceiverBuffer, 0, DebugMarkerBroadcastReceiverBuffer.Length, 0, ReceiveCallback, debugMarkerBroadcastReceiverSocket); // Start to listen.
                }
                catch
                {
                    try
                    {
                        debugMarkerBroadcastReceiverSocket.Close();
                    }
                    catch (Exception)
                    {
                    }
                    debugMarkerBroadcastReceiverSocket = null;
                }
            }
        }

        private static void ReceiveCallback(IAsyncResult ar)
        {
            if (debugMarkerBroadcastReceiverSocket != null)
            {
                try
                {
                    try
                    {
                        System.Net.Sockets.Socket socket = (System.Net.Sockets.Socket)ar.AsyncState;
                        int bytesReceived = 0;
                        try
                        {
                            try
                            {
                                bytesReceived = socket.EndReceive(ar);
                            }
                            catch { }
                        }
                        finally
                        {
                            if (bytesReceived > 0)
                            {
                                string debugMarker = Encoding.UTF8.GetString(DebugMarkerBroadcastReceiverBuffer, 0, bytesReceived);
                                AddEntry(new DebugMarkerLogEntry(debugMarker));
                            }
                        }
                    }
                    finally
                    {
                        if (active)
                            debugMarkerBroadcastReceiverSocket.BeginReceive(DebugMarkerBroadcastReceiverBuffer, 0, DebugMarkerBroadcastReceiverBuffer.Length, 0, ReceiveCallback, debugMarkerBroadcastReceiverSocket);
                    }
                }
                catch { }
            }
        }

        //[System.Diagnostics.DebuggerStepThrough()]
        //private static IPEndPoint GetDebugMarkerBroadcastIPEndPoint()
        //{
        //    try
        //    {
        //        if (debugMarkerBroadcastAddress == null)
        //        {
        //            using (Microsoft.Win32.RegistryKey key = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(@"Software\Dresser Wayne\ISM Nucleus\TCP IP\UDP Broadcast", false))
        //            {
        //                IPAddress broadcastAddress;
        //                int broadcastPort;
        //                if (key != null)
        //                {
        //                    broadcastAddress = IPAddress.Parse((string)key.GetValue("BroadcastAddress", "255.255.255.255"));
        //                    broadcastPort = (int)key.GetValue("LogMarkerInjectorPort", DefaultDebugMarkerBroadcastPort);
        //                }
        //                else
        //                {
        //                    broadcastPort = DefaultDebugMarkerBroadcastPort;
        //                    broadcastAddress = IPAddress.Broadcast;
        //                }
        //                if (broadcastAddress != null)
        //                    debugMarkerBroadcastAddress = new IPEndPoint(broadcastAddress, broadcastPort);
        //            }
        //        }
        //    }
        //    catch
        //    {
        //        if (debugMarkerBroadcastAddress == null)
        //            debugMarkerBroadcastAddress = new IPEndPoint(IPAddress.Broadcast, DefaultDebugMarkerBroadcastPort);
        //    }
        //    return debugMarkerBroadcastAddress;
        //}

        #endregion

        #region DateTimeToString

        internal static string DateTimeToString(DateTime dateTime, string dateTimeFormat)
        {
            if (GlobalLogEntryCounter != null)
                return DateTimeToString(dateTime, dateTimeFormat, GlobalLogEntryCounter.GetNextValue());
            return DateTimeToString(dateTime, dateTimeFormat, 0);
        }

        internal static string DateTimeToString(DateTime dateTime, string dateTimeFormat, UInt64 logEntryIndex)
        {
            return FormatDateTime(dateTime) + "[" + logEntryIndex.ToString("00000") + "]";
        }

        private static string FormatDateTime(DateTime dt)
        {
            char[] chars = new char[12];
            Write2Chars(chars, 0, dt.Hour);
            chars[2] = ':';
            Write2Chars(chars, 3, dt.Minute);
            chars[5] = ':';
            Write2Chars(chars, 6, dt.Second);
            chars[8] = '.';
            Write2Chars(chars, 9, dt.Millisecond / 10);
            chars[11] = Digit(dt.Millisecond % 10);
            return new string(chars);
        }

        private static void Write2Chars(char[] chars, int offset, int value)
        {
            chars[offset] = Digit(value / 10);
            chars[offset + 1] = Digit(value % 10);
        }

        private static char Digit(int value)
        {
            return (char)(value + '0');
        }
        #endregion
    }
}