private void TestHtmlAfterCompression(string originalBookHtml, Action <string> actionsOnFolderBeforeCompressing = null,
                                              Action <string> assertionsOnResultingHtmlString = null,
                                              Action <ZipFile> assertionsOnZipArchive         = null,
                                              Action <ZipFile> assertionsOnRepeat             = null)
        {
            var testBook     = CreateBookWithPhysicalFile(originalBookHtml, bringBookUpToDate: true);
            var bookFileName = Path.GetFileName(testBook.GetPathHtmlFile());

            actionsOnFolderBeforeCompressing?.Invoke(testBook.FolderPath);

            using (var bloomdTempFile = TempFile.WithFilenameInTempFolder(testBook.Title + BookCompressor.ExtensionForDeviceBloomBook))
            {
                BookCompressor.CompressBookForDevice(bloomdTempFile.Path, testBook, _bookServer, Color.Azure, new NullWebSocketProgress());
                var zip = new ZipFile(bloomdTempFile.Path);
                assertionsOnZipArchive?.Invoke(zip);
                var newHtml = GetEntryContents(zip, bookFileName);
                assertionsOnResultingHtmlString?.Invoke(newHtml);
                if (assertionsOnRepeat != null)
                {
                    // compress it again! Used for checking important repeatable results
                    using (var extraTempFile =
                               TempFile.WithFilenameInTempFolder(testBook.Title + "2" + BookCompressor.ExtensionForDeviceBloomBook))
                    {
                        BookCompressor.CompressBookForDevice(extraTempFile.Path, testBook, _bookServer, Color.Azure, new NullWebSocketProgress());
                        zip = new ZipFile(extraTempFile.Path);
                        assertionsOnRepeat(zip);
                    }
                }
            }
        }
        public void CompressBookForDevice_FileNameIsCorrect()
        {
            var testBook = CreateBookWithPhysicalFile(kMinimumValidBookHtml, bringBookUpToDate: true);

            using (var bloomdTempFile = TempFile.WithFilenameInTempFolder(testBook.Title + BookCompressor.ExtensionForDeviceBloomBook))
            {
                BookCompressor.CompressBookForDevice(bloomdTempFile.Path, testBook, _bookServer, Color.Azure, new NullWebSocketProgress());
                Assert.AreEqual(testBook.Title + BookCompressor.ExtensionForDeviceBloomBook,
                                Path.GetFileName(bloomdTempFile.Path));
            }
        }
        /// <summary>
        /// This is the core of sending a book to a device. We need a book and a bookServer in order to come up
        /// with the .bloomd file.
        /// We are either simply saving the .bloomd to destFileName, or else we will make a temporary .bloomd file and
        /// actually send it using sendAction.
        /// We report important progress on the progress control. This includes reporting that we are starting
        /// the actual transmission using startingMessageAction, which is passed the safe file name (for checking pre-existence
        /// in UsbPublisher) and the book title (typically inserted into the message).
        /// If a confirmAction is passed (currently only by UsbPublisher), we use it check for a successful transfer
        /// before reporting completion (except for file save, where the current message is inappropriate).
        /// This is an awkward case where the three ways of publishing are similar enough that
        /// it's annoying and dangerous to have three entirely separate methods but somewhat awkward to combine them.
        /// Possibly we could eventually make them more similar, e.g., it would simplify things if they all said
        /// "Sending X to Y", though I'm not sure that would be good i18n if Y is sometimes a device name
        /// and sometimes a path.
        /// </summary>
        /// <param name="book"></param>
        /// <param name="destFileName"></param>
        /// <param name="sendAction"></param>
        /// <param name="progress"></param>
        /// <param name="bookServer"></param>
        /// <param name="startingMessageFunction"></param>
        public static void SendBook(Book.Book book, BookServer bookServer, string destFileName, Action <string, string> sendAction, WebSocketProgress progress, Func <string, string, string> startingMessageFunction,
                                    Func <string, bool> confirmFunction, Color backColor)
        {
            var bookTitle = book.Title;

            progress.MessageUsingTitle("PackagingBook", "Packaging \"{0}\" for use with Bloom Reader...", bookTitle);

            // compress audio if needed, with progress message
            if (AudioProcessor.IsAnyCompressedAudioMissing(book.FolderPath, book.RawDom))
            {
                progress.Message("CompressingAudio", "Compressing audio files");
                AudioProcessor.TryCompressingAudioAsNeeded(book.FolderPath, book.RawDom);
            }
            var publishedFileName = BookStorage.SanitizeNameForFileSystem(bookTitle) + BookCompressor.ExtensionForDeviceBloomBook;

            if (startingMessageFunction != null)
            {
                progress.MessageWithoutLocalizing(startingMessageFunction(publishedFileName, bookTitle));
            }
            if (destFileName == null)
            {
                // wifi or usb...make the .bloomd in a temp folder.
                using (var bloomdTempFile = TempFile.WithFilenameInTempFolder(publishedFileName))
                {
                    BookCompressor.CompressBookForDevice(bloomdTempFile.Path, book, bookServer, backColor, progress);
                    sendAction(publishedFileName, bloomdTempFile.Path);
                    if (confirmFunction != null && !confirmFunction(publishedFileName))
                    {
                        throw new ApplicationException("Book does not exist after write operation.");
                    }
                    progress.MessageUsingTitle("BookSent", "You can now read \"{0}\" in Bloom Reader!", bookTitle);
                }
            }
            else
            {
                // save file...user has supplied name, there is no further action.
                Debug.Assert(sendAction == null, "further actions are not supported when passing a path name");
                BookCompressor.CompressBookForDevice(destFileName, book, bookServer, backColor, progress);
            }
        }