//
// © Copyright Henrik Ravn 2004
//
// Use, modification and distribution are subject to the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace DotZLib
{
    #region ChecksumGeneratorBase
    /// 
    /// Implements the common functionality needed for all s
    /// 
    /// 
    public abstract class ChecksumGeneratorBase : ChecksumGenerator
    {
        /// 
        /// The value of the current checksum
        /// 
        protected uint _current;
        /// 
        /// Initializes a new instance of the checksum generator base - the current checksum is
        /// set to zero
        /// 
        public ChecksumGeneratorBase()
        {
            _current = 0;
        }
        /// 
        /// Initializes a new instance of the checksum generator basewith a specified value
        /// 
        /// The value to set the current checksum to
        public ChecksumGeneratorBase(uint initialValue)
        {
            _current = initialValue;
        }
        /// 
        /// Resets the current checksum to zero
        /// 
        public void Reset() { _current = 0; }
        /// 
        /// Gets the current checksum value
        /// 
        public uint Value { get { return _current; } }
        /// 
        /// Updates the current checksum with part of an array of bytes
        /// 
        /// The data to update the checksum with
        /// Where in data to start updating
        /// The number of bytes from data to use
        /// The sum of offset and count is larger than the length of data
        /// data is a null reference
        /// Offset or count is negative.
        /// All the other Update methods are implmeneted in terms of this one.
        /// This is therefore the only method a derived class has to implement
        public abstract void Update(byte[] data, int offset, int count);
        /// 
        /// Updates the current checksum with an array of bytes.
        /// 
        /// The data to update the checksum with
        public void Update(byte[] data)
        {
            Update(data, 0, data.Length);
        }
        /// 
        /// Updates the current checksum with the data from a string
        /// 
        /// The string to update the checksum with
        /// The characters in the string are converted by the UTF-8 encoding
        public void Update(string data)
        {
			Update(Encoding.UTF8.GetBytes(data));
        }
        /// 
        /// Updates the current checksum with the data from a string, using a specific encoding
        /// 
        /// The string to update the checksum with
        /// The encoding to use
        public void Update(string data, Encoding encoding)
        {
            Update(encoding.GetBytes(data));
        }
    }
    #endregion
    #region CRC32
    /// 
    /// Implements a CRC32 checksum generator
    /// 
    public sealed class CRC32Checksum : ChecksumGeneratorBase
    {
        #region DLL imports
        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
        private static extern uint crc32(uint crc, int data, uint length);
        #endregion
        /// 
        /// Initializes a new instance of the CRC32 checksum generator
        /// 
        public CRC32Checksum() : base() {}
        /// 
        /// Initializes a new instance of the CRC32 checksum generator with a specified value
        /// 
        /// The value to set the current checksum to
        public CRC32Checksum(uint initialValue) : base(initialValue) {}
        /// 
        /// Updates the current checksum with part of an array of bytes
        /// 
        /// The data to update the checksum with
        /// Where in data to start updating
        /// The number of bytes from data to use
        /// The sum of offset and count is larger than the length of data
        /// data is a null reference
        /// Offset or count is negative.
        public override void Update(byte[] data, int offset, int count)
        {
            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
            if ((offset+count) > data.Length) throw new ArgumentException();
            GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned);
            try
            {
                _current = crc32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count);
            }
            finally
            {
                hData.Free();
            }
        }
    }
    #endregion
    #region Adler
    /// 
    /// Implements a checksum generator that computes the Adler checksum on data
    /// 
    public sealed class AdlerChecksum : ChecksumGeneratorBase
    {
        #region DLL imports
        [DllImport("ZLIB1.dll", CallingConvention=CallingConvention.Cdecl)]
        private static extern uint adler32(uint adler, int data, uint length);
        #endregion
        /// 
        /// Initializes a new instance of the Adler checksum generator
        /// 
        public AdlerChecksum() : base() {}
        /// 
        /// Initializes a new instance of the Adler checksum generator with a specified value
        /// 
        /// The value to set the current checksum to
        public AdlerChecksum(uint initialValue) : base(initialValue) {}
        /// 
        /// Updates the current checksum with part of an array of bytes
        /// 
        /// The data to update the checksum with
        /// Where in data to start updating
        /// The number of bytes from data to use
        /// The sum of offset and count is larger than the length of data
        /// data is a null reference
        /// Offset or count is negative.
        public override void Update(byte[] data, int offset, int count)
        {
            if (offset < 0 || count < 0) throw new ArgumentOutOfRangeException();
            if ((offset+count) > data.Length) throw new ArgumentException();
            GCHandle hData = GCHandle.Alloc(data, GCHandleType.Pinned);
            try
            {
                _current = adler32(_current, hData.AddrOfPinnedObject().ToInt32()+offset, (uint)count);
            }
            finally
            {
                hData.Free();
            }
        }
    }
    #endregion
}