// ZipOutputStream.cs
//
// ------------------------------------------------------------------
//
// Copyright (c) 2009 Dino Chiesa.
// All rights reserved.
//
// This code module is part of DotNetZip, a zipfile class library.
//
// ------------------------------------------------------------------
//
// This code is licensed under the Microsoft Public License.
// See the file License.txt for the license details.
// More info on: http://dotnetzip.codeplex.com
//
// ------------------------------------------------------------------
//
// last saved (in emacs):
// Time-stamp: <2011-July-28 06:34:30>
//
// ------------------------------------------------------------------
//
// This module defines the ZipOutputStream class, which is a stream metaphor for
// generating zip files.  This class does not depend on Ionic.Zip.ZipFile, but rather
// stands alongside it as an alternative "container" for ZipEntry.  It replicates a
// subset of the properties, including these:
//
//  - Comment
//  - Encryption
//  - Password
//  - CodecBufferSize
//  - CompressionLevel
//  - CompressionMethod
//  - EnableZip64 (UseZip64WhenSaving)
//  - IgnoreCase (!CaseSensitiveRetrieval)
//
// It adds these novel methods:
//
//  - PutNextEntry
//
//
// ------------------------------------------------------------------
//

using System;
using System.Threading;
using System.Collections.Generic;
using System.IO;
using Ionic.Zip;

namespace Ionic.Zip
{
	/// <summary>
	///   Provides a stream metaphor for generating zip files.
	/// </summary>
	///
	/// <remarks>
	/// <para>
	///   This class writes zip files, as defined in the <see
	///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">specification
	///   for zip files described by PKWare</see>.  The compression for this
	///   implementation is provided by a managed-code version of Zlib, included with
	///   DotNetZip in the classes in the Ionic.Zlib namespace.
	/// </para>
	///
	/// <para>
	///   This class provides an alternative programming model to the one enabled by the
	///   <see cref="ZipFile"/> class. Use this when creating zip files, as an
	///   alternative to the <see cref="ZipFile"/> class, when you would like to use a
	///   <c>Stream</c> type to write the zip file.
	/// </para>
	///
	/// <para>
	///   Both the <c>ZipOutputStream</c> class and the <c>ZipFile</c> class can be used
	///   to create zip files. Both of them support many of the common zip features,
	///   including Unicode, different compression levels, and ZIP64.   They provide
	///   very similar performance when creating zip files.
	/// </para>
	///
	/// <para>
	///   The <c>ZipFile</c> class is generally easier to use than
	///   <c>ZipOutputStream</c> and should be considered a higher-level interface.  For
	///   example, when creating a zip file via calls to the <c>PutNextEntry()</c> and
	///   <c>Write()</c> methods on the <c>ZipOutputStream</c> class, the caller is
	///   responsible for opening the file, reading the bytes from the file, writing
	///   those bytes into the <c>ZipOutputStream</c>, setting the attributes on the
	///   <c>ZipEntry</c>, and setting the created, last modified, and last accessed
	///   timestamps on the zip entry. All of these things are done automatically by a
	///   call to <see cref="ZipFile.AddFile(string,string)">ZipFile.AddFile()</see>.
	///   For this reason, the <c>ZipOutputStream</c> is generally recommended for use
	///   only when your application emits arbitrary data, not necessarily data from a
	///   filesystem file, directly into a zip file, and does so using a <c>Stream</c>
	///   metaphor.
	/// </para>
	///
	/// <para>
	///   Aside from the differences in programming model, there are other
	///   differences in capability between the two classes.
	/// </para>
	///
	/// <list type="bullet">
	///   <item>
	///     <c>ZipFile</c> can be used to read and extract zip files, in addition to
	///     creating zip files. <c>ZipOutputStream</c> cannot read zip files. If you want
	///     to use a stream to read zip files, check out the <see cref="ZipInputStream"/> class.
	///   </item>
	///
	///   <item>
	///     <c>ZipOutputStream</c> does not support the creation of segmented or spanned
	///     zip files.
	///   </item>
	///
	///   <item>
	///     <c>ZipOutputStream</c> cannot produce a self-extracting archive.
	///   </item>
	/// </list>
	///
	/// <para>
	///   Be aware that the <c>ZipOutputStream</c> class implements the <see
	///   cref="System.IDisposable"/> interface.  In order for
	///   <c>ZipOutputStream</c> to produce a valid zip file, you use use it within
	///   a using clause (<c>Using</c> in VB), or call the <c>Dispose()</c> method
	///   explicitly.  See the examples for how to employ a using clause.
	/// </para>
	///
	/// <para>
	///   Also, a note regarding compression performance: On the desktop .NET
	///   Framework, DotNetZip can use a multi-threaded compression implementation
	///   that provides significant speed increases on large files, over 300k or so,
	///   at the cost of increased memory use at runtime.  (The output of the
	///   compression is almost exactly the same size).  But, the multi-threaded
	///   approach incurs a performance hit on smaller files. There's no way for the
	///   ZipOutputStream to know whether parallel compression will be beneficial,
	///   because the ZipOutputStream does not know how much data you will write
	///   through the stream.  You may wish to set the <see
	///   cref="ParallelDeflateThreshold"/> property to zero, if you are compressing
	///   large files through <c>ZipOutputStream</c>.  This will cause parallel
	///   compression to be used, always.
	/// </para>
	/// </remarks>
	internal class ZipOutputStream : Stream
    {
        /// <summary>
        ///   Create a ZipOutputStream, wrapping an existing stream.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The <see cref="ZipFile"/> class is generally easier to use when creating
        ///   zip files. The ZipOutputStream offers a different metaphor for creating a
        ///   zip file, based on the <see cref="System.IO.Stream"/> class.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="stream">
        /// The stream to wrap. It must be writable. This stream will be closed at
        /// the time the ZipOutputStream is closed.
        /// </param>
        ///
        /// <example>
        ///
        ///   This example shows how to create a zip file, using the
        ///   ZipOutputStream class.
        ///
        /// <code lang="C#">
        /// private void Zipup()
        /// {
        ///     if (filesToZip.Count == 0)
        ///     {
        ///         System.Console.WriteLine("Nothing to do.");
        ///         return;
        ///     }
        ///
        ///     using (var raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
        ///     {
        ///         using (var output= new ZipOutputStream(raw))
        ///         {
        ///             output.Password = "VerySecret!";
        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
        ///
        ///             foreach (string inputFileName in filesToZip)
        ///             {
        ///                 System.Console.WriteLine("file: {0}", inputFileName);
        ///
        ///                 output.PutNextEntry(inputFileName);
        ///                 using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Write ))
        ///                 {
        ///                     byte[] buffer= new byte[2048];
        ///                     int n;
        ///                     while ((n= input.Read(buffer,0,buffer.Length)) > 0)
        ///                     {
        ///                         output.Write(buffer,0,n);
        ///                     }
        ///                 }
        ///             }
        ///         }
        ///     }
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        /// Private Sub Zipup()
        ///     Dim outputFileName As String = "XmlData.zip"
        ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
        ///     If (filesToZip.Length = 0) Then
        ///         Console.WriteLine("Nothing to do.")
        ///     Else
        ///         Using raw As FileStream = File.Open(outputFileName, FileMode.Create, FileAccess.ReadWrite)
        ///             Using output As ZipOutputStream = New ZipOutputStream(raw)
        ///                 output.Password = "VerySecret!"
        ///                 output.Encryption = EncryptionAlgorithm.WinZipAes256
        ///                 Dim inputFileName As String
        ///                 For Each inputFileName In filesToZip
        ///                     Console.WriteLine("file: {0}", inputFileName)
        ///                     output.PutNextEntry(inputFileName)
        ///                     Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        ///                         Dim n As Integer
        ///                         Dim buffer As Byte() = New Byte(2048) {}
        ///                         Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
        ///                             output.Write(buffer, 0, n)
        ///                         Loop
        ///                     End Using
        ///                 Next
        ///             End Using
        ///         End Using
        ///     End If
        /// End Sub
        /// </code>
        /// </example>
        public ZipOutputStream(Stream stream) : this(stream, false) { }


        /// <summary>
        ///   Create a ZipOutputStream that writes to a filesystem file.
        /// </summary>
        ///
        /// <remarks>
        ///   The <see cref="ZipFile"/> class is generally easier to use when creating
        ///   zip files. The ZipOutputStream offers a different metaphor for creating a
        ///   zip file, based on the <see cref="System.IO.Stream"/> class.
        /// </remarks>
        ///
        /// <param name="fileName">
        ///   The name of the zip file to create.
        /// </param>
        ///
        /// <example>
        ///
        ///   This example shows how to create a zip file, using the
        ///   ZipOutputStream class.
        ///
        /// <code lang="C#">
        /// private void Zipup()
        /// {
        ///     if (filesToZip.Count == 0)
        ///     {
        ///         System.Console.WriteLine("Nothing to do.");
        ///         return;
        ///     }
        ///
        ///     using (var output= new ZipOutputStream(outputFileName))
        ///     {
        ///         output.Password = "VerySecret!";
        ///         output.Encryption = EncryptionAlgorithm.WinZipAes256;
        ///
        ///         foreach (string inputFileName in filesToZip)
        ///         {
        ///             System.Console.WriteLine("file: {0}", inputFileName);
        ///
        ///             output.PutNextEntry(inputFileName);
        ///             using (var input = File.Open(inputFileName, FileMode.Open, FileAccess.Read,
        ///                                          FileShare.Read | FileShare.Write ))
        ///             {
        ///                 byte[] buffer= new byte[2048];
        ///                 int n;
        ///                 while ((n= input.Read(buffer,0,buffer.Length)) > 0)
        ///                 {
        ///                     output.Write(buffer,0,n);
        ///                 }
        ///             }
        ///         }
        ///     }
        /// }
        /// </code>
        ///
        /// <code lang="VB">
        /// Private Sub Zipup()
        ///     Dim outputFileName As String = "XmlData.zip"
        ///     Dim filesToZip As String() = Directory.GetFiles(".", "*.xml")
        ///     If (filesToZip.Length = 0) Then
        ///         Console.WriteLine("Nothing to do.")
        ///     Else
        ///         Using output As ZipOutputStream = New ZipOutputStream(outputFileName)
        ///             output.Password = "VerySecret!"
        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256
        ///             Dim inputFileName As String
        ///             For Each inputFileName In filesToZip
        ///                 Console.WriteLine("file: {0}", inputFileName)
        ///                 output.PutNextEntry(inputFileName)
        ///                 Using input As FileStream = File.Open(inputFileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
        ///                     Dim n As Integer
        ///                     Dim buffer As Byte() = New Byte(2048) {}
        ///                     Do While (n = input.Read(buffer, 0, buffer.Length) > 0)
        ///                         output.Write(buffer, 0, n)
        ///                     Loop
        ///                 End Using
        ///             Next
        ///         End Using
        ///     End If
        /// End Sub
        /// </code>
        /// </example>
        public ZipOutputStream(String fileName)
        {
            Stream stream = File.Open(fileName, FileMode.Create, FileAccess.ReadWrite, FileShare.None);
            _Init(stream, false, fileName);
        }


        /// <summary>
        ///   Create a ZipOutputStream.
        /// </summary>
        ///
        /// <remarks>
        ///   See the documentation for the <see
        ///   cref="ZipOutputStream(Stream)">ZipOutputStream(Stream)</see>
        ///   constructor for an example.
        /// </remarks>
        ///
        /// <param name="stream">
        ///   The stream to wrap. It must be writable.
        /// </param>
        ///
        /// <param name="leaveOpen">
        ///   true if the application would like the stream
        ///   to remain open after the <c>ZipOutputStream</c> has been closed.
        /// </param>
        public ZipOutputStream(Stream stream, bool leaveOpen)
        {
            _Init(stream, leaveOpen, null);
        }

        private void _Init(Stream stream, bool leaveOpen, string name)
        {
            // workitem 9307
            _outputStream = stream.CanRead ? stream : new CountingStream(stream);
            CompressionLevel = Ionic.Zlib.CompressionLevel.Default;
            CompressionMethod = Ionic.Zip.CompressionMethod.Deflate;
            _encryption = EncryptionAlgorithm.None;
            _entriesWritten = new Dictionary<String, ZipEntry>(StringComparer.Ordinal);
            _zip64 = Zip64Option.Never;
            _leaveUnderlyingStreamOpen = leaveOpen;
            Strategy = Ionic.Zlib.CompressionStrategy.Default;
            _name = name ?? "(stream)";
#if !NETCF
            ParallelDeflateThreshold = -1L;
#endif
        }


        /// <summary>Provides a string representation of the instance.</summary>
        /// <remarks>
        ///   <para>
        ///     This can be useful for debugging purposes.
        ///   </para>
        /// </remarks>
        /// <returns>a string representation of the instance.</returns>
        public override String ToString()
        {
            return String.Format ("ZipOutputStream::{0}(leaveOpen({1})))", _name, _leaveUnderlyingStreamOpen);
        }


        /// <summary>
        ///   Sets the password to be used on the <c>ZipOutputStream</c> instance.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   When writing a zip archive, this password is applied to the entries, not
        ///   to the zip archive itself. It applies to any <c>ZipEntry</c> subsequently
        ///   written to the <c>ZipOutputStream</c>.
        /// </para>
        ///
        /// <para>
        ///   Using a password does not encrypt or protect the "directory" of the
        ///   archive - the list of entries contained in the archive.  If you set the
        ///   <c>Password</c> property, the password actually applies to individual
        ///   entries that are added to the archive, subsequent to the setting of this
        ///   property.  The list of filenames in the archive that is eventually created
        ///   will appear in clear text, but the contents of the individual files are
        ///   encrypted.  This is how Zip encryption works.
        /// </para>
        ///
        /// <para>
        ///   If you set this property, and then add a set of entries to the archive via
        ///   calls to <c>PutNextEntry</c>, then each entry is encrypted with that
        ///   password.  You may also want to change the password between adding
        ///   different entries. If you set the password, add an entry, then set the
        ///   password to <c>null</c> (<c>Nothing</c> in VB), and add another entry, the
        ///   first entry is encrypted and the second is not.
        /// </para>
        ///
        /// <para>
        ///   When setting the <c>Password</c>, you may also want to explicitly set the <see
        ///   cref="Encryption"/> property, to specify how to encrypt the entries added
        ///   to the ZipFile.  If you set the <c>Password</c> to a non-null value and do not
        ///   set <see cref="Encryption"/>, then PKZip 2.0 ("Weak") encryption is used.
        ///   This encryption is relatively weak but is very interoperable. If
        ///   you set the password to a <c>null</c> value (<c>Nothing</c> in VB),
        ///   <c>Encryption</c> is reset to None.
        /// </para>
        ///
        /// <para>
        ///   Special case: if you wrap a ZipOutputStream around a non-seekable stream,
        ///   and use encryption, and emit an entry of zero bytes, the <c>Close()</c> or
        ///   <c>PutNextEntry()</c> following the entry will throw an exception.
        /// </para>
        ///
        /// </remarks>
        public String Password
        {
            set
            {
                if (_disposed)
                {
                    _exceptionPending = true;
                    throw new System.InvalidOperationException("The stream has been closed.");
                }

                _password = value;
                if (_password == null)
                {
                    _encryption = EncryptionAlgorithm.None;
                }
                else if (_encryption == EncryptionAlgorithm.None)
                {
                    _encryption = EncryptionAlgorithm.PkzipWeak;
                }
            }
        }


        /// <summary>
        ///   The Encryption to use for entries added to the <c>ZipOutputStream</c>.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The specified Encryption is applied to the entries subsequently
        ///   written to the <c>ZipOutputStream</c> instance.
        /// </para>
        ///
        /// <para>
        ///   If you set this to something other than
        ///   EncryptionAlgorithm.None, you will also need to set the
        ///   <see cref="Password"/> to a non-null, non-empty value in
        ///   order to actually get encryption on the entry.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <seealso cref="Password">ZipOutputStream.Password</seealso>
        /// <seealso cref="Ionic.Zip.ZipEntry.Encryption">ZipEntry.Encryption</seealso>
        public EncryptionAlgorithm Encryption
        {
            get
            {
                return _encryption;
            }
            set
            {
                if (_disposed)
                {
                    _exceptionPending = true;
                    throw new System.InvalidOperationException("The stream has been closed.");
                }
                if (value == EncryptionAlgorithm.Unsupported)
                {
                    _exceptionPending = true;
                    throw new InvalidOperationException("You may not set Encryption to that value.");
                }
                _encryption = value;
            }
        }


        /// <summary>
        ///   Size of the work buffer to use for the ZLIB codec during compression.
        /// </summary>
        ///
        /// <remarks>
        ///   Setting this may affect performance.  For larger files, setting this to a
        ///   larger size may improve performance, but I'm not sure.  Sorry, I don't
        ///   currently have good recommendations on how to set it.  You can test it if
        ///   you like.
        /// </remarks>
        public int CodecBufferSize
        {
            get;
            set;
        }


        /// <summary>
        ///   The compression strategy to use for all entries.
        /// </summary>
        ///
        /// <remarks>
        ///   Set the Strategy used by the ZLIB-compatible compressor, when compressing
        ///   data for the entries in the zip archive. Different compression strategies
        ///   work better on different sorts of data. The strategy parameter can affect
        ///   the compression ratio and the speed of compression but not the correctness
        ///   of the compresssion.  For more information see <see
        ///   cref="Ionic.Zlib.CompressionStrategy "/>.
        /// </remarks>
        public Ionic.Zlib.CompressionStrategy Strategy
        {
            get;
            set;
        }


        /// <summary>
        ///   The type of timestamp attached to the ZipEntry.
        /// </summary>
        ///
        /// <remarks>
        ///   Set this in order to specify the kind of timestamp that should be emitted
        ///   into the zip file for each entry.
        /// </remarks>
        public ZipEntryTimestamp Timestamp
        {
            get
            {
                return _timestamp;
            }
            set
            {
                if (_disposed)
                {
                    _exceptionPending = true;
                    throw new System.InvalidOperationException("The stream has been closed.");
                }
                _timestamp = value;
            }
        }


        /// <summary>
        ///   Sets the compression level to be used for entries subsequently added to
        ///   the zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///  <para>
        ///    Varying the compression level used on entries can affect the
        ///    size-vs-speed tradeoff when compression and decompressing data streams
        ///    or files.
        ///  </para>
        ///
        ///  <para>
        ///    As with some other properties on the <c>ZipOutputStream</c> class, like <see
        ///    cref="Password"/>, and <see cref="Encryption"/>,
        ///    setting this property on a <c>ZipOutputStream</c>
        ///    instance will cause the specified <c>CompressionLevel</c> to be used on all
        ///    <see cref="ZipEntry"/> items that are subsequently added to the
        ///    <c>ZipOutputStream</c> instance.
        ///  </para>
        ///
        ///  <para>
        ///    If you do not set this property, the default compression level is used,
        ///    which normally gives a good balance of compression efficiency and
        ///    compression speed.  In some tests, using <c>BestCompression</c> can
        ///    double the time it takes to compress, while delivering just a small
        ///    increase in compression efficiency.  This behavior will vary with the
        ///    type of data you compress.  If you are in doubt, just leave this setting
        ///    alone, and accept the default.
        ///  </para>
        /// </remarks>
        public Ionic.Zlib.CompressionLevel CompressionLevel
        {
            get;
            set;
        }

        /// <summary>
        ///   The compression method used on each entry added to the ZipOutputStream.
        /// </summary>
        public Ionic.Zip.CompressionMethod CompressionMethod
        {
            get;
            set;
        }


        /// <summary>
        ///   A comment attached to the zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///
        /// <para>
        ///   The application sets this property to specify a comment to be embedded
        ///   into the generated zip archive.
        /// </para>
        ///
        /// <para>
        ///   According to <see
        ///   href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">PKWARE's
        ///   zip specification</see>, the comment is not encrypted, even if there is a
        ///   password set on the zip file.
        /// </para>
        ///
        /// <para>
        ///   The specification does not describe how to indicate the encoding used
        ///   on a comment string. Many "compliant" zip tools and libraries use
        ///   IBM437 as the code page for comments; DotNetZip, too, follows that
        ///   practice.  On the other hand, there are situations where you want a
        ///   Comment to be encoded with something else, for example using code page
        ///   950 "Big-5 Chinese". To fill that need, DotNetZip will encode the
        ///   comment following the same procedure it follows for encoding
        ///   filenames: (a) if <see cref="AlternateEncodingUsage"/> is
        ///   <c>Never</c>, it uses the default encoding (IBM437). (b) if <see
        ///   cref="AlternateEncodingUsage"/> is <c>Always</c>, it always uses the
        ///   alternate encoding (<see cref="AlternateEncoding"/>). (c) if <see
        ///   cref="AlternateEncodingUsage"/> is <c>AsNecessary</c>, it uses the
        ///   alternate encoding only if the default encoding is not sufficient for
        ///   encoding the comment - in other words if decoding the result does not
        ///   produce the original string.  This decision is taken at the time of
        ///   the call to <c>ZipFile.Save()</c>.
        /// </para>
        ///
        /// </remarks>
        public string Comment
        {
            get { return _comment; }
            set
            {
                if (_disposed)
                {
                    _exceptionPending = true;
                    throw new System.InvalidOperationException("The stream has been closed.");
                }
                _comment = value;
            }
        }



        /// <summary>
        ///   Specify whether to use ZIP64 extensions when saving a zip archive.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   The default value for the property is <see
        ///   cref="Zip64Option.Never"/>. <see cref="Zip64Option.AsNecessary"/> is
        ///   safest, in the sense that you will not get an Exception if a
        ///   pre-ZIP64 limit is exceeded.
        /// </para>
        ///
        /// <para>
        ///   You must set this property before calling <c>Write()</c>.
        /// </para>
        ///
        /// </remarks>
        public Zip64Option EnableZip64
        {
            get
            {
                return _zip64;
            }
            set
            {
                if (_disposed)
                {
                    _exceptionPending = true;
                    throw new System.InvalidOperationException("The stream has been closed.");
                }
                _zip64 = value;
            }
        }


        /// <summary>
        ///   Indicates whether ZIP64 extensions were used when saving the zip archive.
        /// </summary>
        ///
        /// <remarks>
        ///   The value is defined only after the <c>ZipOutputStream</c> has been closed.
        /// </remarks>
        public bool OutputUsedZip64
        {
            get
            {
                return _anyEntriesUsedZip64 || _directoryNeededZip64;
            }
        }


        /// <summary>
        ///   Whether the ZipOutputStream should use case-insensitive comparisons when
        ///   checking for uniqueness of zip entries.
        /// </summary>
        ///
        /// <remarks>
        ///   <para>
        ///   Though the zip specification doesn't prohibit zipfiles with duplicate
        ///   entries, Sane zip files have no duplicates, and the DotNetZip library
        ///   cannot create zip files with duplicate entries. If an application attempts
        ///   to call <see cref="PutNextEntry(String)"/> with a name that duplicates one
        ///   already used within the archive, the library will throw an Exception.
        ///   </para>
        ///   <para>
        ///   This property allows the application to specify whether the
        ///   ZipOutputStream instance considers ordinal case when checking for
        ///   uniqueness of zip entries.
        ///   </para>
        /// </remarks>
        public bool IgnoreCase
        {
          get
          {
              return !_DontIgnoreCase;
          }

          set
          {
              _DontIgnoreCase = !value;
          }

        }


        /// <summary>
        ///   Indicates whether to encode entry filenames and entry comments using
        ///   Unicode (UTF-8).
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">The
        ///   PKWare zip specification</see> provides for encoding file names and file
        ///   comments in either the IBM437 code page, or in UTF-8.  This flag selects
        ///   the encoding according to that specification.  By default, this flag is
        ///   false, and filenames and comments are encoded into the zip file in the
        ///   IBM437 codepage.  Setting this flag to true will specify that filenames
        ///   and comments that cannot be encoded with IBM437 will be encoded with
        ///   UTF-8.
        /// </para>
        ///
        /// <para>
        ///   Zip files created with strict adherence to the PKWare specification with
        ///   respect to UTF-8 encoding can contain entries with filenames containing
        ///   any combination of Unicode characters, including the full range of
        ///   characters from Chinese, Latin, Hebrew, Greek, Cyrillic, and many other
        ///   alphabets.  However, because at this time, the UTF-8 portion of the PKWare
        ///   specification is not broadly supported by other zip libraries and
        ///   utilities, such zip files may not be readable by your favorite zip tool or
        ///   archiver. In other words, interoperability will decrease if you set this
        ///   flag to true.
        /// </para>
        ///
        /// <para>
        ///   In particular, Zip files created with strict adherence to the PKWare
        ///   specification with respect to UTF-8 encoding will not work well with
        ///   Explorer in Windows XP or Windows Vista, because Windows compressed
        ///   folders, as far as I know, do not support UTF-8 in zip files.  Vista can
        ///   read the zip files, but shows the filenames incorrectly. Unpacking from
        ///   Windows Vista Explorer will result in filenames that have rubbish
        ///   characters in place of the high-order UTF-8 bytes.
        /// </para>
        ///
        /// <para>
        ///   Also, zip files that use UTF-8 encoding will not work well with Java
        ///   applications that use the java.util.zip classes, as of v5.0 of the Java
        ///   runtime. The Java runtime does not correctly implement the PKWare
        ///   specification in this regard.
        /// </para>
        ///
        /// <para>
        ///   As a result, we have the unfortunate situation that "correct" behavior by
        ///   the DotNetZip library with regard to Unicode encoding of filenames during
        ///   zip creation will result in zip files that are readable by strictly
        ///   compliant and current tools (for example the most recent release of the
        ///   commercial WinZip tool); but these zip files will not be readable by
        ///   various other tools or libraries, including Windows Explorer.
        /// </para>
        ///
        /// <para>
        ///   The DotNetZip library can read and write zip files with UTF8-encoded
        ///   entries, according to the PKware spec.  If you use DotNetZip for both
        ///   creating and reading the zip file, and you use UTF-8, there will be no
        ///   loss of information in the filenames. For example, using a self-extractor
        ///   created by this library will allow you to unpack files correctly with no
        ///   loss of information in the filenames.
        /// </para>
        ///
        /// <para>
        ///   If you do not set this flag, it will remain false.  If this flag is false,
        ///   the <c>ZipOutputStream</c> will encode all filenames and comments using
        ///   the IBM437 codepage.  This can cause "loss of information" on some
        ///   filenames, but the resulting zipfile will be more interoperable with other
        ///   utilities. As an example of the loss of information, diacritics can be
        ///   lost.  The o-tilde character will be down-coded to plain o.  The c with a
        ///   cedilla (Unicode 0xE7) used in Portugese will be downcoded to a c.
        ///   Likewise, the O-stroke character (Unicode 248), used in Danish and
        ///   Norwegian, will be down-coded to plain o. Chinese characters cannot be
        ///   represented in codepage IBM437; when using the default encoding, Chinese
        ///   characters in filenames will be represented as ?. These are all examples
        ///   of "information loss".
        /// </para>
        ///
        /// <para>
        ///   The loss of information associated to the use of the IBM437 encoding is
        ///   inconvenient, and can also lead to runtime errors. For example, using
        ///   IBM437, any sequence of 4 Chinese characters will be encoded as ????.  If
        ///   your application creates a <c>ZipOutputStream</c>, does not set the
        ///   encoding, then adds two files, each with names of four Chinese characters
        ///   each, this will result in a duplicate filename exception.  In the case
        ///   where you add a single file with a name containing four Chinese
        ///   characters, the zipfile will save properly, but extracting that file
        ///   later, with any zip tool, will result in an error, because the question
        ///   mark is not legal for use within filenames on Windows.  These are just a
        ///   few examples of the problems associated to loss of information.
        /// </para>
        ///
        /// <para>
        ///   This flag is independent of the encoding of the content within the entries
        ///   in the zip file. Think of the zip file as a container - it supports an
        ///   encoding.  Within the container are other "containers" - the file entries
        ///   themselves.  The encoding within those entries is independent of the
        ///   encoding of the zip archive container for those entries.
        /// </para>
        ///
        /// <para>
        ///   Rather than specify the encoding in a binary fashion using this flag, an
        ///   application can specify an arbitrary encoding via the <see
        ///   cref="ProvisionalAlternateEncoding"/> property.  Setting the encoding
        ///   explicitly when creating zip archives will result in non-compliant zip
        ///   files that, curiously, are fairly interoperable.  The challenge is, the
        ///   PKWare specification does not provide for a way to specify that an entry
        ///   in a zip archive uses a code page that is neither IBM437 nor UTF-8.
        ///   Therefore if you set the encoding explicitly when creating a zip archive,
        ///   you must take care upon reading the zip archive to use the same code page.
        ///   If you get it wrong, the behavior is undefined and may result in incorrect
        ///   filenames, exceptions, stomach upset, hair loss, and acne.
        /// </para>
        /// </remarks>
        /// <seealso cref="ProvisionalAlternateEncoding"/>
        [Obsolete("Beginning with v1.9.1.6 of DotNetZip, this property is obsolete. It will be removed in a future version of the library. Use AlternateEncoding and AlternateEncodingUsage instead.")]
        public bool UseUnicodeAsNecessary
        {
            get
            {
                return (_alternateEncoding == System.Text.Encoding.UTF8) &&
                    (AlternateEncodingUsage == ZipOption.AsNecessary);
            }
            set
            {
                if (value)
                {
                    _alternateEncoding = System.Text.Encoding.UTF8;
                    _alternateEncodingUsage = ZipOption.AsNecessary;

                }
                else
                {
                    _alternateEncoding = Ionic.Zip.ZipOutputStream.DefaultEncoding;
                    _alternateEncodingUsage = ZipOption.Never;
                }
            }
        }


        /// <summary>
        ///   The text encoding to use when emitting entries into the zip archive, for
        ///   those entries whose filenames or comments cannot be encoded with the
        ///   default (IBM437) encoding.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   In <see href="http://www.pkware.com/documents/casestudies/APPNOTE.TXT">its
        ///   zip specification</see>, PKWare describes two options for encoding
        ///   filenames and comments: using IBM437 or UTF-8.  But, some archiving tools
        ///   or libraries do not follow the specification, and instead encode
        ///   characters using the system default code page.  For example, WinRAR when
        ///   run on a machine in Shanghai may encode filenames with the Big-5 Chinese
        ///   (950) code page.  This behavior is contrary to the Zip specification, but
        ///   it occurs anyway.
        /// </para>
        ///
        /// <para>
        ///   When using DotNetZip to write zip archives that will be read by one of
        ///   these other archivers, set this property to specify the code page to use
        ///   when encoding the <see cref="ZipEntry.FileName"/> and <see
        ///   cref="ZipEntry.Comment"/> for each <c>ZipEntry</c> in the zip file, for
        ///   values that cannot be encoded with the default codepage for zip files,
        ///   IBM437.  This is why this property is "provisional".  In all cases, IBM437
        ///   is used where possible, in other words, where no loss of data would
        ///   result. It is possible, therefore, to have a given entry with a
        ///   <c>Comment</c> encoded in IBM437 and a <c>FileName</c> encoded with the
        ///   specified "provisional" codepage.
        /// </para>
        ///
        /// <para>
        ///   Be aware that a zip file created after you've explicitly set the
        ///   <c>ProvisionalAlternateEncoding</c> property to a value other than
        ///   IBM437 may not be compliant to the PKWare specification, and may not be
        ///   readable by compliant archivers.  On the other hand, many (most?)
        ///   archivers are non-compliant and can read zip files created in arbitrary
        ///   code pages.  The trick is to use or specify the proper codepage when
        ///   reading the zip.
        /// </para>
        ///
        /// <para>
        ///   When creating a zip archive using this library, it is possible to change
        ///   the value of <c>ProvisionalAlternateEncoding</c> between each entry you
        ///   add, and between adding entries and the call to <c>Close()</c>. Don't do
        ///   this. It will likely result in a zipfile that is not readable.  For best
        ///   interoperability, either leave <c>ProvisionalAlternateEncoding</c>
        ///   alone, or specify it only once, before adding any entries to the
        ///   <c>ZipOutputStream</c> instance.  There is one exception to this
        ///   recommendation, described later.
        /// </para>
        ///
        /// <para>
        ///   When using an arbitrary, non-UTF8 code page for encoding, there is no
        ///   standard way for the creator application - whether DotNetZip, WinZip,
        ///   WinRar, or something else - to formally specify in the zip file which
        ///   codepage has been used for the entries. As a result, readers of zip files
        ///   are not able to inspect the zip file and determine the codepage that was
        ///   used for the entries contained within it.  It is left to the application
        ///   or user to determine the necessary codepage when reading zip files encoded
        ///   this way.  If you use an incorrect codepage when reading a zipfile, you
        ///   will get entries with filenames that are incorrect, and the incorrect
        ///   filenames may even contain characters that are not legal for use within
        ///   filenames in Windows. Extracting entries with illegal characters in the
        ///   filenames will lead to exceptions. It's too bad, but this is just the way
        ///   things are with code pages in zip files. Caveat Emptor.
        /// </para>
        ///
        /// <para>
        ///   One possible approach for specifying the code page for a given zip file is
        ///   to describe the code page in a human-readable form in the Zip comment. For
        ///   example, the comment may read "Entries in this archive are encoded in the
        ///   Big5 code page".  For maximum interoperability, the zip comment in this
        ///   case should be encoded in the default, IBM437 code page.  In this case,
        ///   the zip comment is encoded using a different page than the filenames.  To
        ///   do this, Specify <c>ProvisionalAlternateEncoding</c> to your desired
        ///   region-specific code page, once before adding any entries, and then set
        ///   the <see cref="Comment"/> property and reset
        ///   <c>ProvisionalAlternateEncoding</c> to IBM437 before calling <c>Close()</c>.
        /// </para>
        /// </remarks>
        [Obsolete("use AlternateEncoding and AlternateEncodingUsage instead.")]
        public System.Text.Encoding ProvisionalAlternateEncoding
        {
            get
            {
                if (_alternateEncodingUsage == ZipOption.AsNecessary)
                    return _alternateEncoding;
                return null;
            }
            set
            {
                _alternateEncoding = value;
                _alternateEncodingUsage = ZipOption.AsNecessary;
            }
        }

        /// <summary>
        ///   A Text Encoding to use when encoding the filenames and comments for
        ///   all the ZipEntry items, during a ZipFile.Save() operation.
        /// </summary>
        /// <remarks>
        ///   <para>
        ///     Whether the encoding specified here is used during the save depends
        ///     on <see cref="AlternateEncodingUsage"/>.
        ///   </para>
        /// </remarks>
        public System.Text.Encoding AlternateEncoding
        {
            get
            {
                return _alternateEncoding;
            }
            set
            {
                _alternateEncoding = value;
            }
        }

        /// <summary>
        ///   A flag that tells if and when this instance should apply
        ///   AlternateEncoding to encode the filenames and comments associated to
        ///   of ZipEntry objects contained within this instance.
        /// </summary>
        public ZipOption AlternateEncodingUsage
        {
            get
            {
                return _alternateEncodingUsage;
            }
            set
            {
                _alternateEncodingUsage = value;
            }
        }

        /// <summary>
        /// The default text encoding used in zip archives.  It is numeric 437, also
        /// known as IBM437.
        /// </summary>
        /// <seealso cref="Ionic.Zip.ZipFile.ProvisionalAlternateEncoding"/>
        public static System.Text.Encoding DefaultEncoding
        {
            get
            {
                return System.Text.Encoding.GetEncoding("IBM437");
            }
        }


#if !NETCF
        /// <summary>
        ///   The size threshold for an entry, above which a parallel deflate is used.
        /// </summary>
        ///
        /// <remarks>
        ///
        ///   <para>
        ///     DotNetZip will use multiple threads to compress any ZipEntry, when
        ///     the <c>CompressionMethod</c> is Deflate, and if the entry is
        ///     larger than the given size.  Zero means "always use parallel
        ///     deflate", while -1 means "never use parallel deflate".
        ///   </para>
        ///
        ///   <para>
        ///     If the entry size cannot be known before compression, as with any entry
        ///     added via a ZipOutputStream, then Parallel deflate will never be
        ///     performed, unless the value of this property is zero.
        ///   </para>
        ///
        ///   <para>
        ///     A parallel deflate operations will speed up the compression of
        ///     large files, on computers with multiple CPUs or multiple CPU
        ///     cores.  For files above 1mb, on a dual core or dual-cpu (2p)
        ///     machine, the time required to compress the file can be 70% of the
        ///     single-threaded deflate.  For very large files on 4p machines the
        ///     compression can be done in 30% of the normal time.  The downside
        ///     is that parallel deflate consumes extra memory during the deflate,
        ///     and the deflation is slightly less effective.
        ///   </para>
        ///
        ///   <para>
        ///     Parallel deflate tends to not be as effective as single-threaded deflate
        ///     because the original data stream is split into multiple independent
        ///     buffers, each of which is compressed in parallel.  But because they are
        ///     treated independently, there is no opportunity to share compression
        ///     dictionaries, and additional framing bytes must be added to the output
        ///     stream.  For that reason, a deflated stream may be slightly larger when
        ///     compressed using parallel deflate, as compared to a traditional
        ///     single-threaded deflate. For files of about 512k, the increase over the
        ///     normal deflate is as much as 5% of the total compressed size. For larger
        ///     files, the difference can be as small as 0.1%.
        ///   </para>
        ///
        ///   <para>
        ///     Multi-threaded compression does not give as much an advantage when using
        ///     Encryption. This is primarily because encryption tends to slow down
        ///     the entire pipeline. Also, multi-threaded compression gives less of an
        ///     advantage when using lower compression levels, for example <see
        ///     cref="Ionic.Zlib.CompressionLevel.BestSpeed"/>.  You may have to perform
        ///     some tests to determine the best approach for your situation.
        ///   </para>
        ///
        ///   <para>
        ///     The default value for this property is -1, which means parallel
        ///     compression will not be performed unless you set it to zero.
        ///   </para>
        ///
        /// </remarks>
        public long ParallelDeflateThreshold
        {
            set
            {
                if ((value != 0) && (value != -1) && (value < 64 * 1024))
                    throw new ArgumentOutOfRangeException("value must be greater than 64k, or 0, or -1");
                _ParallelDeflateThreshold = value;
            }
            get
            {
                return _ParallelDeflateThreshold;
            }
        }


        /// <summary>
        ///   The maximum number of buffer pairs to use when performing
        ///   parallel compression.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This property sets an upper limit on the number of memory
        ///   buffer pairs to create when performing parallel
        ///   compression.  The implementation of the parallel
        ///   compression stream allocates multiple buffers to
        ///   facilitate parallel compression.  As each buffer fills up,
        ///   the stream uses <see
        ///   cref="System.Threading.ThreadPool.QueueUserWorkItem(WaitCallback)">
        ///   ThreadPool.QueueUserWorkItem()</see> to compress those
        ///   buffers in a background threadpool thread. After a buffer
        ///   is compressed, it is re-ordered and written to the output
        ///   stream.
        /// </para>
        ///
        /// <para>
        ///   A higher number of buffer pairs enables a higher degree of
        ///   parallelism, which tends to increase the speed of compression on
        ///   multi-cpu computers.  On the other hand, a higher number of buffer
        ///   pairs also implies a larger memory consumption, more active worker
        ///   threads, and a higher cpu utilization for any compression. This
        ///   property enables the application to limit its memory consumption and
        ///   CPU utilization behavior depending on requirements.
        /// </para>
        ///
        /// <para>
        ///   For each compression "task" that occurs in parallel, there are 2
        ///   buffers allocated: one for input and one for output.  This property
        ///   sets a limit for the number of pairs.  The total amount of storage
        ///   space allocated for buffering will then be (N*S*2), where N is the
        ///   number of buffer pairs, S is the size of each buffer (<see
        ///   cref="CodecBufferSize"/>).  By default, DotNetZip allocates 4 buffer
        ///   pairs per CPU core, so if your machine has 4 cores, and you retain
        ///   the default buffer size of 128k, then the
        ///   ParallelDeflateOutputStream will use 4 * 4 * 2 * 128kb of buffer
        ///   memory in total, or 4mb, in blocks of 128kb.  If you then set this
        ///   property to 8, then the number will be 8 * 2 * 128kb of buffer
        ///   memory, or 2mb.
        /// </para>
        ///
        /// <para>
        ///   CPU utilization will also go up with additional buffers, because a
        ///   larger number of buffer pairs allows a larger number of background
        ///   threads to compress in parallel. If you find that parallel
        ///   compression is consuming too much memory or CPU, you can adjust this
        ///   value downward.
        /// </para>
        ///
        /// <para>
        ///   The default value is 16. Different values may deliver better or
        ///   worse results, depending on your priorities and the dynamic
        ///   performance characteristics of your storage and compute resources.
        /// </para>
        ///
        /// <para>
        ///   This property is not the number of buffer pairs to use; it is an
        ///   upper limit. An illustration: Suppose you have an application that
        ///   uses the default value of this property (which is 16), and it runs
        ///   on a machine with 2 CPU cores. In that case, DotNetZip will allocate
        ///   4 buffer pairs per CPU core, for a total of 8 pairs.  The upper
        ///   limit specified by this property has no effect.
        /// </para>
        ///
        /// <para>
        ///   The application can set this value at any time, but it is
        ///   effective only if set before calling
        ///   <c>ZipOutputStream.Write()</c> for the first time.
        /// </para>
        /// </remarks>
        ///
        /// <seealso cref="ParallelDeflateThreshold"/>
        ///
        public int ParallelDeflateMaxBufferPairs
        {
            get
            {
                return _maxBufferPairs;
            }
            set
            {
                if (value < 4)
                    throw new ArgumentOutOfRangeException("ParallelDeflateMaxBufferPairs",
                                                "Value must be 4 or greater.");
                _maxBufferPairs = value;
            }
        }
#endif


        private void InsureUniqueEntry(ZipEntry ze1)
        {
            if (_entriesWritten.ContainsKey(ze1.FileName))
            {
                _exceptionPending = true;
                throw new ArgumentException(String.Format("The entry '{0}' already exists in the zip archive.", ze1.FileName));
            }
        }


        internal Stream OutputStream
        {
            get
            {
                return _outputStream;
            }
        }

        internal String Name
        {
            get
            {
                return _name;
            }
        }

        /// <summary>
        ///   Returns true if an entry by the given name has already been written
        ///   to the ZipOutputStream.
        /// </summary>
        ///
        /// <param name="name">
        ///   The name of the entry to scan for.
        /// </param>
        ///
        /// <returns>
        /// true if an entry by the given name has already been written.
        /// </returns>
        public bool ContainsEntry(string name)
        {
            return _entriesWritten.ContainsKey(SharedUtilities.NormalizePathForUseInZipFile(name));
        }


        /// <summary>
        ///   Write the data from the buffer to the stream.
        /// </summary>
        ///
        /// <remarks>
        ///   As the application writes data into this stream, the data may be
        ///   compressed and encrypted before being written out to the underlying
        ///   stream, depending on the settings of the <see cref="CompressionLevel"/>
        ///   and the <see cref="Encryption"/> properties.
        /// </remarks>
        ///
        /// <param name="buffer">The buffer holding data to write to the stream.</param>
        /// <param name="offset">the offset within that data array to find the first byte to write.</param>
        /// <param name="count">the number of bytes to write.</param>
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (_disposed)
            {
                _exceptionPending = true;
                throw new System.InvalidOperationException("The stream has been closed.");
            }

            if (buffer==null)
            {
                _exceptionPending = true;
                throw new System.ArgumentNullException("buffer");
            }

            if (_currentEntry == null)
            {
                _exceptionPending = true;
                throw new System.InvalidOperationException("You must call PutNextEntry() before calling Write().");
            }

            if (_currentEntry.IsDirectory)
            {
                _exceptionPending = true;
                throw new System.InvalidOperationException("You cannot Write() data for an entry that is a directory.");
            }

            if (_needToWriteEntryHeader)
                _InitiateCurrentEntry(false);

            if (count != 0)
                _entryOutputStream.Write(buffer, offset, count);
        }



        /// <summary>
        ///   Specify the name of the next entry that will be written to the zip file.
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   Call this method just before calling <see cref="Write(byte[], int, int)"/>, to
        ///   specify the name of the entry that the next set of bytes written to
        ///   the <c>ZipOutputStream</c> belongs to. All subsequent calls to <c>Write</c>,
        ///   until the next call to <c>PutNextEntry</c>,
        ///   will be inserted into the named entry in the zip file.
        /// </para>
        ///
        /// <para>
        ///   If the <paramref name="entryName"/> used in <c>PutNextEntry()</c> ends in
        ///   a slash, then the entry added is marked as a directory. Because directory
        ///   entries do not contain data, a call to <c>Write()</c>, before an
        ///   intervening additional call to <c>PutNextEntry()</c>, will throw an
        ///   exception.
        /// </para>
        ///
        /// <para>
        ///   If you don't call <c>Write()</c> between two calls to
        ///   <c>PutNextEntry()</c>, the first entry is inserted into the zip file as a
        ///   file of zero size.  This may be what you want.
        /// </para>
        ///
        /// <para>
        ///   Because <c>PutNextEntry()</c> closes out the prior entry, if any, this
        ///   method may throw if there is a problem with the prior entry.
        /// </para>
        ///
        /// <para>
        ///   This method returns the <c>ZipEntry</c>.  You can modify public properties
        ///   on the <c>ZipEntry</c>, such as <see cref="ZipEntry.Encryption"/>, <see
        ///   cref="ZipEntry.Password"/>, and so on, until the first call to
        ///   <c>ZipOutputStream.Write()</c>, or until the next call to
        ///   <c>PutNextEntry()</c>.  If you modify the <c>ZipEntry</c> <em>after</em>
        ///   having called <c>Write()</c>, you may get a runtime exception, or you may
        ///   silently get an invalid zip archive.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <example>
        ///
        ///   This example shows how to create a zip file, using the
        ///   <c>ZipOutputStream</c> class.
        ///
        /// <code>
        /// private void Zipup()
        /// {
        ///     using (FileStream fs raw = File.Open(_outputFileName, FileMode.Create, FileAccess.ReadWrite ))
        ///     {
        ///         using (var output= new ZipOutputStream(fs))
        ///         {
        ///             output.Password = "VerySecret!";
        ///             output.Encryption = EncryptionAlgorithm.WinZipAes256;
        ///             output.PutNextEntry("entry1.txt");
        ///             byte[] buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #1.");
        ///             output.Write(buffer,0,buffer.Length);
        ///             output.PutNextEntry("entry2.txt");  // this will be zero length
        ///             output.PutNextEntry("entry3.txt");
        ///             buffer= System.Text.Encoding.ASCII.GetBytes("This is the content for entry #3.");
        ///             output.Write(buffer,0,buffer.Length);
        ///         }
        ///     }
        /// }
        /// </code>
        /// </example>
        ///
        /// <param name="entryName">
        ///   The name of the entry to be added, including any path to be used
        ///   within the zip file.
        /// </param>
        ///
        /// <returns>
        ///   The ZipEntry created.
        /// </returns>
        ///
        public ZipEntry PutNextEntry(String entryName)
        {
            if (String.IsNullOrEmpty(entryName))
                throw new ArgumentNullException("entryName");

            if (_disposed)
            {
                _exceptionPending = true;
                throw new System.InvalidOperationException("The stream has been closed.");
            }

            _FinishCurrentEntry();
            _currentEntry = ZipEntry.CreateForZipOutputStream(entryName);
            _currentEntry._container = new ZipContainer(this);
            _currentEntry._BitField |= 0x0008;  // workitem 8932
            _currentEntry.SetEntryTimes(DateTime.Now, DateTime.Now, DateTime.Now);
            _currentEntry.CompressionLevel = this.CompressionLevel;
            _currentEntry.CompressionMethod = this.CompressionMethod;
            _currentEntry.Password = _password; // workitem 13909
            _currentEntry.Encryption = this.Encryption;
            // workitem 12634
            _currentEntry.AlternateEncoding = this.AlternateEncoding;
            _currentEntry.AlternateEncodingUsage = this.AlternateEncodingUsage;

            if (entryName.EndsWith("/"))  _currentEntry.MarkAsDirectory();

            _currentEntry.EmitTimesInWindowsFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Windows) != 0);
            _currentEntry.EmitTimesInUnixFormatWhenSaving = ((_timestamp & ZipEntryTimestamp.Unix) != 0);
            InsureUniqueEntry(_currentEntry);
            _needToWriteEntryHeader = true;

            return _currentEntry;
        }



        private void _InitiateCurrentEntry(bool finishing)
        {
            // If finishing==true, this means we're initiating the entry at the time of
            // Close() or PutNextEntry().  If this happens, it means no data was written
            // for the entry - Write() was never called.  (The usual case us to call
            // _InitiateCurrentEntry(bool) from within Write().)  If finishing==true,
            // the entry could be either a zero-byte file or a directory.

            _entriesWritten.Add(_currentEntry.FileName,_currentEntry);
            _entryCount++; // could use _entriesWritten.Count, but I don't want to incur
            // the cost.

            if (_entryCount > 65534 && _zip64 == Zip64Option.Never)
            {
                _exceptionPending = true;
                throw new System.InvalidOperationException("Too many entries. Consider setting ZipOutputStream.EnableZip64.");
            }

            // Write out the header.
            //
            // If finishing, and encryption is in use, then we don't want to emit the
            // normal encryption header.  Signal that with a cycle=99 to turn off
            // encryption for zero-byte entries or directories.
            //
            // If finishing, then we know the stream length is zero.  Else, unknown
            // stream length.  Passing stream length == 0 allows an optimization so as
            // not to setup an encryption or deflation stream, when stream length is
            // zero.

            _currentEntry.WriteHeader(_outputStream, finishing ? 99 : 0);
            _currentEntry.StoreRelativeOffset();

            if (!_currentEntry.IsDirectory)
            {
                _currentEntry.WriteSecurityMetadata(_outputStream);
                _currentEntry.PrepOutputStream(_outputStream,
                                               finishing ? 0 : -1,
                                               out _outputCounter,
                                               out _encryptor,
                                               out _deflater,
                                               out _entryOutputStream);
            }
            _needToWriteEntryHeader = false;
        }



        private void _FinishCurrentEntry()
        {
            if (_currentEntry != null)
            {
                if (_needToWriteEntryHeader)
                    _InitiateCurrentEntry(true); // an empty entry - no writes

                _currentEntry.FinishOutputStream(_outputStream, _outputCounter, _encryptor, _deflater, _entryOutputStream);
                _currentEntry.PostProcessOutput(_outputStream);
                // workitem 12964
                if (_currentEntry.OutputUsedZip64!=null)
                    _anyEntriesUsedZip64 |= _currentEntry.OutputUsedZip64.Value;

                // reset all the streams
                _outputCounter = null; _encryptor = _deflater = null; _entryOutputStream = null;
            }
        }



        /// <summary>
        /// Dispose the stream
        /// </summary>
        ///
        /// <remarks>
        /// <para>
        ///   This method writes the Zip Central directory, then closes the stream.  The
        ///   application must call Dispose() (or Close) in order to produce a valid zip file.
        /// </para>
        ///
        /// <para>
        ///   Typically the application will call <c>Dispose()</c> implicitly, via a <c>using</c>
        ///   statement in C#, or a <c>Using</c> statement in VB.
        /// </para>
        ///
        /// </remarks>
        ///
        /// <param name="disposing">set this to true, always.</param>
        protected override void Dispose(bool disposing)
        {
            if (_disposed) return;

            if (disposing) // not called from finalizer
            {
                // handle pending exceptions
                if (!_exceptionPending)
                {
                    _FinishCurrentEntry();
                    _directoryNeededZip64 = ZipOutput.WriteCentralDirectoryStructure(_outputStream,
                                                                                     _entriesWritten.Values,
                                                                                     1, // _numberOfSegmentsForMostRecentSave,
                                                                                     _zip64,
                                                                                     Comment,
                                                                                     new ZipContainer(this));
                    Stream wrappedStream = null;
                    CountingStream cs = _outputStream as CountingStream;
                    if (cs != null)
                    {
                        wrappedStream = cs.WrappedStream;
#if NETCF
                    cs.Close();
#else
                        cs.Dispose();
#endif
                    }
                    else
                    {
                        wrappedStream = _outputStream;
                    }

                    if (!_leaveUnderlyingStreamOpen)
                    {
#if NETCF
                    wrappedStream.Close();
#else
                        wrappedStream.Dispose();
#endif
                    }
                    _outputStream = null;
                }
            }
            _disposed = true;
        }



        /// <summary>
        /// Always returns false.
        /// </summary>
        public override bool CanRead { get { return false; } }

        /// <summary>
        /// Always returns false.
        /// </summary>
        public override bool CanSeek { get { return false; } }

        /// <summary>
        /// Always returns true.
        /// </summary>
        public override bool CanWrite { get { return true; } }

        /// <summary>
        /// Always returns a NotSupportedException.
        /// </summary>
        public override long Length { get { throw new NotSupportedException(); } }

        /// <summary>
        /// Setting this property always returns a NotSupportedException. Getting it
        /// returns the value of the Position on the underlying stream.
        /// </summary>
        public override long Position
        {
            get { return _outputStream.Position; }
            set { throw new NotSupportedException(); }
        }

        /// <summary>
        /// This is a no-op.
        /// </summary>
        public override void Flush() { }

        /// <summary>
        /// This method always throws a NotSupportedException.
        /// </summary>
        /// <param name="buffer">ignored</param>
        /// <param name="offset">ignored</param>
        /// <param name="count">ignored</param>
        /// <returns>nothing</returns>
        public override int Read(byte[] buffer, int offset, int count)
        {
            throw new NotSupportedException("Read");
        }

        /// <summary>
        /// This method always throws a NotSupportedException.
        /// </summary>
        /// <param name="offset">ignored</param>
        /// <param name="origin">ignored</param>
        /// <returns>nothing</returns>
        public override long Seek(long offset, SeekOrigin origin)
        {
            throw new NotSupportedException("Seek");
        }

        /// <summary>
        /// This method always throws a NotSupportedException.
        /// </summary>
        /// <param name="value">ignored</param>
        public override void SetLength(long value)
        {
            throw new NotSupportedException();
        }


        private EncryptionAlgorithm _encryption;
        private ZipEntryTimestamp _timestamp;
        internal String _password;
        private String _comment;
        private Stream _outputStream;
        private ZipEntry _currentEntry;
        internal Zip64Option _zip64;
        private Dictionary<String, ZipEntry> _entriesWritten;
        private int _entryCount;
        private ZipOption _alternateEncodingUsage = ZipOption.Never;
        private System.Text.Encoding _alternateEncoding
            = System.Text.Encoding.GetEncoding("IBM437"); // default = IBM437

        private bool _leaveUnderlyingStreamOpen;
        private bool _disposed;
        private bool _exceptionPending; // **see note below
        private bool _anyEntriesUsedZip64, _directoryNeededZip64;
        private CountingStream _outputCounter;
        private Stream _encryptor;
        private Stream _deflater;
        private Ionic.Crc.CrcCalculatorStream _entryOutputStream;
        private bool _needToWriteEntryHeader;
        private string _name;
        private bool _DontIgnoreCase;
#if !NETCF
        internal Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater;
        private long _ParallelDeflateThreshold;
        private int _maxBufferPairs = 16;
#endif

        // **Note regarding exceptions:

        // When ZipOutputStream is employed within a using clause, which
        // is the typical scenario, and an exception is thrown within
        // the scope of the using, Close()/Dispose() is invoked
        // implicitly before processing the initial exception.  In that
        // case, _exceptionPending is true, and we don't want to try to
        // write anything in the Close/Dispose logic.  Doing so can
        // cause additional exceptions that mask the original one. So,
        // the _exceptionPending flag is used to track that, and to
        // allow the original exception to be propagated to the
        // application without extra "noise."

    }



    internal class ZipContainer
    {
        private ZipFile _zf;
        private ZipOutputStream _zos;
        private ZipInputStream _zis;

        public ZipContainer(Object o)
        {
            _zf = (o as ZipFile);
            _zos = (o as ZipOutputStream);
            _zis = (o as ZipInputStream);
        }

        public ZipFile ZipFile
        {
            get { return _zf; }
        }

        public ZipOutputStream ZipOutputStream
        {
            get { return _zos; }
        }

        public string Name
        {
            get
            {
                if (_zf != null) return _zf.Name;
                if (_zis != null) throw new NotSupportedException();
                return _zos.Name;
            }
        }

        public string Password
        {
            get
            {
                if (_zf != null) return _zf._Password;
                if (_zis != null) return _zis._Password;
                return _zos._password;
            }
        }

        public Zip64Option Zip64
        {
            get
            {
                if (_zf != null) return _zf._zip64;
                if (_zis != null) throw new NotSupportedException();
                return _zos._zip64;
            }
        }

        public int BufferSize
        {
            get
            {
                if (_zf != null) return _zf.BufferSize;
                if (_zis != null) throw new NotSupportedException();
                return 0;
            }
        }

#if !NETCF
        public Ionic.Zlib.ParallelDeflateOutputStream ParallelDeflater
        {
            get
            {
                if (_zf != null) return _zf.ParallelDeflater;
                if (_zis != null) return null;
                return _zos.ParallelDeflater;
            }
            set
            {
                if (_zf != null) _zf.ParallelDeflater = value;
                else if (_zos != null) _zos.ParallelDeflater = value;
            }
        }

        public long ParallelDeflateThreshold
        {
            get
            {
                if (_zf != null) return _zf.ParallelDeflateThreshold;
                return _zos.ParallelDeflateThreshold;
            }
        }
        public int ParallelDeflateMaxBufferPairs
        {
            get
            {
                if (_zf != null) return _zf.ParallelDeflateMaxBufferPairs;
                return _zos.ParallelDeflateMaxBufferPairs;
            }
        }
#endif

        public int CodecBufferSize
        {
            get
            {
                if (_zf != null) return _zf.CodecBufferSize;
                if (_zis != null) return _zis.CodecBufferSize;
                return _zos.CodecBufferSize;
            }
        }

        public Ionic.Zlib.CompressionStrategy Strategy
        {
            get
            {
                if (_zf != null) return _zf.Strategy;
                return _zos.Strategy;
            }
        }

        public Zip64Option UseZip64WhenSaving
        {
            get
            {
                if (_zf != null) return _zf.UseZip64WhenSaving;
                return _zos.EnableZip64;
            }
        }

        public System.Text.Encoding AlternateEncoding
        {
            get
            {
                if (_zf != null) return _zf.AlternateEncoding;
                if (_zos!=null) return _zos.AlternateEncoding;
                return null;
            }
        }
        public System.Text.Encoding DefaultEncoding
        {
            get
            {
                if (_zf != null) return ZipFile.DefaultEncoding;
                if (_zos!=null) return ZipOutputStream.DefaultEncoding;
                return null;
            }
        }
        public ZipOption AlternateEncodingUsage
        {
            get
            {
                if (_zf != null) return _zf.AlternateEncodingUsage;
                if (_zos!=null) return _zos.AlternateEncodingUsage;
                return ZipOption.Never; // n/a
            }
        }

        public Stream ReadStream
        {
            get
            {
                if (_zf != null) return _zf.ReadStream;
                return _zis.ReadStream;
            }
        }
    }

}