//
// Copyright (c) 2006-2009 Microsoft Corporation.  All rights reserved.
//
//
// Implements the CRC algorithm, which is used in zip files.  The zip format calls for
// the zipfile to contain a CRC for the unencrypted byte stream of each file.
//
// It is based on example source code published at
//    http://www.vbaccelerator.com/home/net/code/libraries/CRC32/Crc32_zip_CRC32_CRC32_cs.asp
//
// This implementation adds a tweak of that code for use within zip creation.  While
// computing the CRC we also compress the byte stream, in the same read loop. This
// avoids the need to read through the uncompressed stream twice - once to compute CRC
// and another time to compress.
//
// Thu, 30 Mar 2006  13:58
// 
using System;
namespace ThirdParty.Ionic.Zlib
{
    /// 
    /// Calculates a 32bit Cyclic Redundancy Checksum (CRC) using the
    /// same polynomial used by Zip. This type is used internally by DotNetZip; it is generally not used directly
    /// by applications wishing to create, read, or manipulate zip archive files.
    /// 
    internal class CRC32
    {
        /// 
        /// indicates the total number of bytes read on the CRC stream.
        /// This is used when writing the ZipDirEntry when compressing files.
        /// 
        public Int64 TotalBytesRead
        {
            get
            {
                return _TotalBytesRead;
            }
        }
        /// 
        /// Indicates the current CRC for all blocks slurped in.
        /// 
        public Int32 Crc32Result
        {
            get
            {
                // return one's complement of the running result
                return unchecked((Int32)(~_RunningCrc32Result));
            }
        }
        /// 
        /// Returns the CRC32 for the specified stream.
        /// 
        /// The stream over which to calculate the CRC32
        /// the CRC32 calculation
        public Int32 GetCrc32(System.IO.Stream input)
        {
            return GetCrc32AndCopy(input, null);
        }
        /// 
        /// Returns the CRC32 for the specified stream, and writes the input into the output stream.
        /// 
        /// The stream over which to calculate the CRC32
        /// The stream into which to deflate the input
        /// the CRC32 calculation
        public Int32 GetCrc32AndCopy(System.IO.Stream input, System.IO.Stream output)
        {
            unchecked
            {
                //UInt32 crc32Result;
                //crc32Result = 0xFFFFFFFF;
                byte[] buffer = new byte[BUFFER_SIZE];
                int readSize = BUFFER_SIZE;
                _TotalBytesRead = 0;
                int count = input.Read(buffer, 0, readSize);
                if (output != null) output.Write(buffer, 0, count);
                _TotalBytesRead += count;
                while (count > 0)
                {
                    //for (int i = 0; i < count; i++)
                    //{
                    //    _RunningCrc32Result = ((_RunningCrc32Result) >> 8) ^ crc32Table[(buffer[i]) ^ ((_RunningCrc32Result) & 0x000000FF)];
                    //}
                    SlurpBlock(buffer, 0, count);
                    count = input.Read(buffer, 0, readSize);
                    if (output != null) output.Write(buffer, 0, count);
                    _TotalBytesRead += count;
                }
                return (Int32)(~_RunningCrc32Result);
            }
        }
        /// 
        /// Get the CRC32 for the given (word,byte) combo. 
        /// This is a computation defined by PKzip.
        /// 
        /// The word to start with.
        /// The byte to combine it with.
        /// The CRC-ized result.
        public Int32 ComputeCrc32(Int32 W, byte B)
        {
            return _InternalComputeCrc32((UInt32)W, B);
        }
        internal Int32 _InternalComputeCrc32(UInt32 W, byte B)
        {
            return (Int32)(crc32Table[(W ^ B) & 0xFF] ^ (W >> 8));
        }
        /// 
        /// Update the value for the running CRC32 using the given block of bytes.
        /// This is useful when using the CRC32() class in a Stream.
        /// 
        /// block of bytes to slurp
        /// starting point in the block
        /// how many bytes within the block to slurp
        public void SlurpBlock(byte[] block, int offset, int count)
        {
            for (int i = 0; i < count; i++)
            {
                int x = offset + i;
                _RunningCrc32Result = ((_RunningCrc32Result) >> 8) ^ crc32Table[(block[x]) ^ ((_RunningCrc32Result) & 0x000000FF)];
            }
            _TotalBytesRead += count;
        }
        // pre-initialize the crc table for speed of lookup.
        static CRC32()
        {
            unchecked
            {
                // This is the official polynomial used by CRC32 in PKZip.
                // Often the polynomial is shown reversed as 0x04C11DB7.
                UInt32 dwPolynomial = 0xEDB88320;
                UInt32 i, j;
                crc32Table = new UInt32[256];
                UInt32 dwCrc;
                for (i = 0; i < 256; i++)
                {
                    dwCrc = i;
                    for (j = 8; j > 0; j--)
                    {
                        if ((dwCrc & 1) == 1)
                        {
                            dwCrc = (dwCrc >> 1) ^ dwPolynomial;
                        }
                        else
                        {
                            dwCrc >>= 1;
                        }
                    }
                    crc32Table[i] = dwCrc;
                }
            }
        }
        // private member vars
        private Int64 _TotalBytesRead;
        private static UInt32[] crc32Table;
        private const int BUFFER_SIZE = 8192;
        private UInt32 _RunningCrc32Result = 0xFFFFFFFF;
    }
    /// 
    /// A Stream that calculates a CRC32 (a checksum) on all bytes read, 
    /// or on all bytes written.
    /// 
    ///
    /// 
    /// 
    /// This class can be used to verify the CRC of a ZipEntry when reading from a stream, 
    /// or to calculate a CRC when writing to a stream.  The stream should be used to either 
    /// read, or write, but not both.  If you intermix reads and writes, the results are
    /// not defined. 
    /// 
    /// This class is intended primarily for use internally by the DotNetZip library.
    /// 
    public class CrcCalculatorStream : System.IO.Stream
    {
        private System.IO.Stream _InnerStream;
        private CRC32 _Crc32;
        private Int64 _length = 0;
        /// 
        /// Gets the total number of bytes run through the CRC32 calculator.
        /// 
        ///
        /// 
        /// This is either the total number of bytes read, or the total number
        /// of bytes written, depending on the direction of this stream.
        /// 
        public Int64 TotalBytesSlurped
        {
            get { return _Crc32.TotalBytesRead; }
        }
        /// 
        /// The constructor.
        /// 
        /// The underlying stream
        public CrcCalculatorStream(System.IO.Stream stream)
            : base()
        {
            _InnerStream = stream;
            _Crc32 = new CRC32();
        }
        /// 
        /// The constructor.
        /// 
        /// The underlying stream
        /// The length of the stream to slurp
        public CrcCalculatorStream(System.IO.Stream stream, Int64 length)
            : base()
        {
            _InnerStream = stream;
            _Crc32 = new CRC32();
            _length = length;
        }
        /// 
        /// Provides the current CRC for all blocks slurped in.
        /// 
        public Int32 Crc32
        {
            get { return _Crc32.Crc32Result; }
        }
        /// 
        /// Read from the stream
        /// 
        /// the buffer to read
        /// the offset at which to start
        /// the number of bytes to read
        /// the number of bytes actually read
        public override int Read(byte[] buffer, int offset, int count)
        {
            int bytesToRead = count;
            // Need to limit the # of bytes returned, if the stream is intended to have a definite length.
            // This is especially useful when returning a stream for the uncompressed data directly to the 
            // application.  The app won't necessarily read only the UncompressedSize number of bytes.  
            // For example wrapping the stream returned from OpenReader() into a StreadReader() and
            // calling ReadToEnd() on it, We can "over-read" the zip data and get a corrupt string.  
            // The length limits that, prevents that problem. 
            if (_length != 0)
            {
                if (_Crc32.TotalBytesRead >= _length) return 0; // EOF
                Int64 bytesRemaining = _length - _Crc32.TotalBytesRead;
                if (bytesRemaining < count) bytesToRead = (int)bytesRemaining;
            }
            int n = _InnerStream.Read(buffer, offset, bytesToRead);
            if (n > 0) _Crc32.SlurpBlock(buffer, offset, n);
            return n;
        }
        /// 
        /// Write to the stream. 
        /// 
        /// the buffer from which to write
        /// the offset at which to start writing
        /// the number of bytes to write
        public override void Write(byte[] buffer, int offset, int count)
        {
            if (count > 0) _Crc32.SlurpBlock(buffer, offset, count);
            _InnerStream.Write(buffer, offset, count);
        }
        /// 
        /// Indicates whether the stream supports reading. 
        /// 
        public override bool CanRead
        {
            get { return _InnerStream.CanRead; }
        }
        /// 
        /// Indicates whether the stream supports seeking. 
        /// 
        public override bool CanSeek
        {
            get { return _InnerStream.CanSeek; }
        }
        /// 
        /// Indicates whether the stream supports writing. 
        /// 
        public override bool CanWrite
        {
            get { return _InnerStream.CanWrite; }
        }
        /// 
        /// Flush the stream.
        /// 
        public override void Flush()
        {
            _InnerStream.Flush();
        }
        /// 
        /// Not implemented.
        /// 
        public override long Length
        {
            get
            {
                if (_length == 0) throw new NotImplementedException();
                else return _length;
            }
        }
        /// 
        /// Not implemented.
        /// 
        public override long Position
        {
            get { return _Crc32.TotalBytesRead; }
            set { throw new NotImplementedException(); }
        }
        /// 
        /// Not implemented.
        /// 
        /// N/A
        /// N/A
        /// N/A
        public override long Seek(long offset, System.IO.SeekOrigin origin)
        {
            throw new NotImplementedException();
        }
        /// 
        /// Not implemented.
        /// 
        /// N/A
        public override void SetLength(long value)
        {
            throw new NotImplementedException();
        }
    }
}