/// <summary>
 /// Initializes a new instance of the <see cref="NamespaceEnumerator" /> class.
 /// </summary>
 /// <param name="listeners">The listeners.</param>
 public NamespaceEnumerator(IList <INamespaceEnumeratorListener> listeners)
 {
     _listeners     = listeners;
     _namespaceInfo = new NamespaceInfo();
 }
        /// <summary>
        /// implementation of post-order traversal of directory tree
        /// it is done this way to guarantee that by the time we finish any directory
        /// all of its subdirectories/files had been visited
        /// Note:
        /// if operaiton is cancelled - not all notifications will be emited,
        /// and namespace information will be partial.
        /// </summary>
        /// <param name="root">directory to scan</param>
        /// <param name="cancelationCallback">function to consult with for cancelation</param>
        /// <exception cref="System.IO.DirectoryNotFoundException">Cannot access directory: {root.FullName}</exception>
        private void EnumeratePostOrderNonRecursive(IDirectoryInfo root, Func <bool> cancelationCallback = null)
        {
            // handle the case than this is actually network share.
            if (!root.Exists())
            {
                throw new System.IO.DirectoryNotFoundException(string.Format(StorageSyncResources.NamespaceEnumeratorErrorFormat, root.FullName));
            }

            _namespaceInfo = new NamespaceInfo
            {
                Path       = root.FullName,
                IsComplete = false
            };

            Stack <IDirectoryInfo> stack1 = new Stack <IDirectoryInfo>(5000);
            Stack <IDirectoryInfo> stack2 = new Stack <IDirectoryInfo>(5000);

            Func <bool> shouldCancel = () =>
            {
                if (System.Diagnostics.Debugger.IsAttached)
                {
                    return(false);
                }

                return(cancelationCallback == null ? false : cancelationCallback.Invoke());
            };

            stack1.Push(root);

            _namespaceInfo.NumberOfDirectories++;

            while (stack1.Count > 0)
            {
                if (shouldCancel())
                {
                    return;
                }

                IDirectoryInfo currentDirectory = stack1.Pop();

                stack2.Push(currentDirectory);

                // notify we have started processing directory
                // processing means accessing subdirectories and files
                NotifyBeginDir(currentDirectory);

                IList <IDirectoryInfo> subdirs = null;

                try
                {
                    subdirs = new List <IDirectoryInfo>(currentDirectory.EnumerateDirectories());
                }
                catch (UnauthorizedAccessException)
                {
                    NotifyUnauthorizedDir(currentDirectory);
                    continue;
                }

                NotifyNamespaceHints(subdirs.Count, 0);

                foreach (IDirectoryInfo subdir in subdirs)
                {
                    stack1.Push(subdir);
                    _namespaceInfo.NumberOfDirectories++;
                }
            }

            while (stack2.Count > 0)
            {
                if (shouldCancel())
                {
                    return;
                }

                IDirectoryInfo currentDirectory = stack2.Pop();

                IList <IFileInfo> files = new List <IFileInfo>(currentDirectory.EnumerateFiles());

                NotifyNamespaceHints(0, files.Count);

                foreach (IFileInfo file in files)
                {
                    _namespaceInfo.NumberOfFiles++;
                    _namespaceInfo.TotalFileSizeInBytes += file.Length;

                    NotifyNextFile(file);
                }

                // notify we have finished processing directory
                NotifyEndDir(currentDirectory);
            }

            _namespaceInfo.IsComplete = true;
        }