context: // Licensed to the .NET Foundation under one or more agreements.
  // The .NET Foundation licenses this file to you under the MIT license.
  // See the LICENSE file in the project root for more information.
  
  using System.ComponentModel;
  using System.Diagnostics;
  using System.Diagnostics.CodeAnalysis;
  using System.Threading;
  using System.Threading.Tasks;
  using System.IO;
  using System;
  using System.Collections.Generic;
  
  namespace MonoDevelop.FSW.OSX
  {
  	/// <devdoc>
  	///    Listens to the system directory change notifications and
  	///    raises events when a directory or file within a directory changes.
  	/// </devdoc>
  
  	internal partial class FileSystemWatcher : Component, ISupportInitialize
  	{
  		/// <devdoc>
  		///     Private instance variables
  		/// </devdoc>
  		// Directory being monitored
  		private string _directory;
  
  		// Filter for name matching
  		private string _filter;
  
  		// The watch filter for the API call.
  		private const NotifyFilters c_defaultNotifyFilters = NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  		private NotifyFilters _notifyFilters = c_defaultNotifyFilters;
  
  		// Flag to watch subtree of this directory
  		private bool _includeSubdirectories = false;
  
  		// Flag to note whether we are attached to the thread pool and responding to changes
  		private bool _enabled = false;
  
  		// Are we in init?
  		private bool _initializing = false;
  
  		// Buffer size
  		private int _internalBufferSize = 8192;
  
  		// Used for synchronization
  		private bool _disposed;
  		private ISynchronizeInvoke _synchronizingObject;
  
  		// Event handlers
  		private FileSystemEventHandler _onChangedHandler = null;
  		private FileSystemEventHandler _onCreatedHandler = null;
  		private FileSystemEventHandler _onDeletedHandler = null;
  		private RenamedEventHandler _onRenamedHandler = null;
  		private ErrorEventHandler _onErrorHandler = null;
  
  		// To validate the input for "path"
  		private static readonly char [] s_wildcards = new char [] { '?', '*' };
  
  		private const int c_notifyFiltersValidMask = (int)(NotifyFilters.Attributes |
  														   NotifyFilters.CreationTime |
  														   NotifyFilters.DirectoryName |
  														   NotifyFilters.FileName |
  														   NotifyFilters.LastAccess |
  														   NotifyFilters.LastWrite |
  														   NotifyFilters.Security |
  														   NotifyFilters.Size);
  
  #if DEBUG
  		static FileSystemWatcher ()
  		{
  			int s_notifyFiltersValidMask = 0;
  			foreach (int enumValue in Enum.GetValues (typeof (NotifyFilters)))
  				s_notifyFiltersValidMask |= enumValue;
  			Debug.Assert (c_notifyFiltersValidMask == s_notifyFiltersValidMask, "The NotifyFilters enum has changed. The c_notifyFiltersValidMask must be updated to reflect the values of the NotifyFilters enum.");
  		}
  #endif
  		static HashSet<FileSystemWatcher> fileSystemWatchers = new HashSet<FileSystemWatcher> ();
  		public static void DisposeAll()
  		{
  			lock (fileSystemWatchers) {
  				foreach (var fsw in fileSystemWatchers) {
  					fsw.Dispose ();
  				}
  				fileSystemWatchers.Clear ();
  			}
  		}
  
  		/// <devdoc>
  		///    Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class.
  		/// </devdoc>
  		public FileSystemWatcher ()
  		{
  			_directory = string.Empty;
  			_filter = "*.*";
  		}
  
  		/// <devdoc>
  		///    Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class,
  		///    given the specified directory to monitor.
  		/// </devdoc>
  		public FileSystemWatcher (string path) : this (path, "*.*")
  		{
  		}
  
  		/// <devdoc>
  		///    Initializes a new instance of the <see cref='System.IO.FileSystemWatcher'/> class,
  		///    given the specified directory and type of files to monitor.
  		/// </devdoc>
  		public FileSystemWatcher (string path, string filter)
  		{
  			if (path == null)
  				throw new ArgumentNullException (nameof (path));
  
  			if (filter == null)
  				throw new ArgumentNullException (nameof (filter));
  
  			// Early check for directory parameter so that an exception can be thrown as early as possible.
  			if (path.Length == 0 || !Directory.Exists (path))
  				throw new ArgumentException (SR.Format (SR.InvalidDirName, path), nameof (path));
  
  			_directory = path;
  			_filter = filter;
  		}
  
  		/// <devdoc>
  		///    Gets or sets the type of changes to watch for.
  		/// </devdoc>
  		public NotifyFilters NotifyFilter {
  			get {
  				return _notifyFilters;
  			}
  			set {
  				if (((int)value & ~c_notifyFiltersValidMask) != 0)
  					throw new ArgumentException (SR.Format (SR.InvalidEnumArgument, nameof (value), (int)value, nameof (NotifyFilters)));
  
  				if (_notifyFilters != value) {
  					_notifyFilters = value;
  
  					Restart ();
  				}
  			}
  		}
  
  		/// <devdoc>
  		///    Gets or sets a value indicating whether the component is enabled.
  		/// </devdoc>
  		public bool EnableRaisingEvents {
  			get {
  				return _enabled;
  			}
  			set {
  				if (_enabled == value) {
  					return;
  				}
  
  				if (IsSuspended ()) {
  					_enabled = value; // Alert the Component to start watching for events when EndInit is called.
  				} else {
  					if (value) {
  						StartRaisingEventsIfNotDisposed (); // will set _enabled to true once successfully started
  					} else {
  						StopRaisingEvents (); // will set _enabled to false
  					}
  				}
  			}
  		}
  
  		/// <devdoc>
  		///    Gets or sets the filter string, used to determine what files are monitored in a directory.
  		/// </devdoc>
  		public string Filter {
  			get {
  				return _filter;
  			}
  			set {
  				if (string.IsNullOrEmpty (value)) {
  					// Skip the string compare for "*.*" since it has no case-insensitive representation that differs from
  					// the case-sensitive representation.
  					_filter = "*.*";
  				} else if (!string.Equals (_filter, value, PathInternal.StringComparison)) {
  					_filter = value;
  				}
  			}
  		}
  
  		/// <devdoc>
  		///    Gets or sets a value indicating whether subdirectories within the specified path should be monitored.
  		/// </devdoc>
  		public bool IncludeSubdirectories {
  			get {
  				return _includeSubdirectories;
  			}
  			set {
  				if (_includeSubdirectories != value) {
  					_includeSubdirectories = value;
  
  					Restart ();
  				}
  			}
  		}
  
  		/// <devdoc>
  		///    Gets or sets the size of the internal buffer.
  		/// </devdoc>
  		public int InternalBufferSize {
  			get {
  				return _internalBufferSize;
  			}
  			set {
  				if (_internalBufferSize != value) {
  					if (value < 4096) {
  						_internalBufferSize = 4096;
  					} else {
  						_internalBufferSize = value;
  					}
  
  					Restart ();
  				}
  			}
  		}
  
  		/// <summary>Allocates a buffer of the requested internal buffer size.</summary>
  		/// <returns>The allocated buffer.</returns>
  		private byte [] AllocateBuffer ()
  		{
  			try {
  				return new byte [_internalBufferSize];
  			} catch (OutOfMemoryException) {
  				throw new OutOfMemoryException (SR.Format (SR.BufferSizeTooLarge, _internalBufferSize));
  			}
  		}
  
  		/// <devdoc>
  		///    Gets or sets the path of the directory to watch.
  		/// </devdoc>
  		public string Path {
  			get {
  				return _directory;
  			}
  			set {
  				value = (value == null) ? string.Empty : value;
  				if (!string.Equals (_directory, value, PathInternal.StringComparison)) {
  
gt: 					if (!Directory.Exists (value)) {