/// <summary>
        /// Creates a new file, with the correct naming scheme.
        /// </summary>
        /// <param name="fileInfo">The <see cref="ProlificDataFileInfo"/> instance representing the name of the file created.</param>
        /// <param name="useWriteThroughIfSupported"><c>true</c> to try to use <see cref="IFileSystemWriter.CreateWriteThroughBinary"/> instead of <see cref="IFileSystemWriter.CreateNewBinary"/>.</param>
        /// <returns>The <see cref="IBinaryWriter"/> representing the file created.</returns>
        public IBinaryWriter CreateFile( out ProlificDataFileInfo fileInfo, bool useWriteThroughIfSupported = false )
        {
            Ensure.That(this).NotDisposed();

            bool writeThroughSupported = false;
            if( useWriteThroughIfSupported )
                writeThroughSupported = this.fileSystem.SupportsCreateWriteThroughBinary;

            // we may have to try multiple times, if we don't yet have an
            // app instance ID, that's verified to be unique
            while( true )
            {
                string appID = appInstanceID;
                bool isNewAppID = false;
                if( appID.NullReference() )
                {
                    // NOTE: random does not necessarily mean unique. But since I find it unlikely to
                    //       have even a few dozen hashes at the same time, we should be in the clear:
                    //       32^6 ~= 1+ billion; and we probably wouldn't see conflicts before we had
                    //       40000-50000 hashes present at the same time. Based on an answer here:
                    //       http://stackoverflow.com/a/9543797
                    appID = GenerateRandomHash(DefaultAppIDLength);
                    isNewAppID = true;
                }

                // try to create the file
                var file = ToFileInfo(this.fileManagerID, appID, this.fileExtension, this.fileSystem.EscapesNames);
                IBinaryWriter fileStream;
                try
                {
                    if( useWriteThroughIfSupported && writeThroughSupported )
                        fileStream = this.fileSystem.CreateWriteThroughBinary(file.DataStoreName, overwriteIfExists: false);
                    else
                        fileStream = this.fileSystem.CreateNewBinary(file.DataStoreName, overwriteIfExists: false);
                }
                catch( Exception ex )
                {
                    //// the file already exists, or there was another problem

                    bool fileAlreadyExists;
                    try
                    {
                        fileAlreadyExists = FindAllFiles(this.fileSystem)
                            .Where(f => DataStore.Comparer.Equals(f.DataStoreName, file.DataStoreName))
                            .FirstOrNullable()
                            .HasValue;
                    }
                    catch( Exception exx )
                    {
                        // there is definitely a problem with the file system: report both exceptions
                        throw new AggregateException("Failed to create file!", ex, exx).StoreFileLine();
                    }

                    if( fileAlreadyExists )
                    {
                        //// NOTE: we are assuming here, that files created by the same source,
                        ////       in the same app instance, are at least a minute apart.

                        // try again with a new app instance ID
                        continue;
                    }
                    else
                    {
                        ex.StoreFileLine();
                        throw; // there was some kind of problem, trying to create the file...
                    }
                }

                // alright, we successfully created the file, but that does not mean, that
                // the app instance ID was not already in use, sometime before that
                if( isNewAppID )
                {
                    bool appIDAlreadyInUse = FindAllFiles(this.fileSystem)
                        .Where(f => !DataStore.Comparer.Equals(f.DataStoreName, file.DataStoreName))
                        .Where(f => AppInstanceIDComparer.Equals(f.AppInstanceID, appID))
                        .FirstOrNullable()
                        .HasValue;

                    if( appIDAlreadyInUse )
                    {
                        // remove this file to avoid confusion, and try again
                        this.fileSystem.DeleteFile(file.DataStoreName);
                        continue;
                    }
                    else
                    {
                        // a new and unique app ID was found
                        var prevAppID = Interlocked.CompareExchange(ref appInstanceID, appID, comparand: null);
                        if( !prevAppID.NullReference() )
                        {
                            // however, this method was called concurrently, and another unique app ID was already established
                            // clean up, and try again with that ID
                            this.fileSystem.DeleteFile(file.DataStoreName);
                            continue;
                        }
                    }
                }

                // either the app ID was not generated just now,
                // or it was, and we successfully saved it
                fileInfo = file;
                return fileStream;
            }
        }
        private static bool TryParseDataStoreName( string dataStoreName, bool escapesFileNames, out ProlificDataFileInfo fileInfo )
        {
            if( dataStoreName.NullOrEmpty() )
            {
                fileInfo = default(ProlificDataFileInfo);
                return false;
            }

            string fileName = escapesFileNames ? DataStore.UnescapeName(dataStoreName) : dataStoreName;
            string[] parts = fileName.Split(new char[] { '_' }, StringSplitOptions.None);
            if( parts.Length != 3 )
            {
                fileInfo = default(ProlificDataFileInfo);
                return false;
            }

            string fileManagerID = parts[0];
            string creationTimeString = parts[1];
            string appInstanceID = parts[2];
            string fileExtension = null;

            if( escapesFileNames
             && appInstanceID.Length > DefaultAppIDLength )
            {
                fileExtension = appInstanceID.Substring(startIndex: DefaultAppIDLength + 1);
                appInstanceID = appInstanceID.Substring(startIndex: 0, length: DefaultAppIDLength);
            }

            DateTime creationTime;
            if( !DateTime.TryParseExact(creationTimeString, CreationTimeFormat, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal, out creationTime) )
            {
                fileInfo = default(ProlificDataFileInfo);
                return false;
            }

            fileInfo = new ProlificDataFileInfo(fileManagerID, appInstanceID, creationTime, fileExtension, dataStoreName);
            return true;
        }