//
// System.Security.Cryptography HashAlgorithm Class implementation
//
// Authors:
//   Matthew S. Ford (Matthew.S.Ford@Rose-Hulman.Edu)
//   Sebastien Pouliot (spouliot@motus.com)
//
// Copyright 2001 by Matthew S. Ford.
// Portions (C) 2002 Motus Technologies Inc. (http://www.motus.com)
//
// Comment: Adapted to the Project from Mono CVS as Sebastien Pouliot suggested to enable
// support of Npgsql MD5 authentication in platforms which don't have support for MD5 algorithm.
//


using System;
using System.IO;


namespace Npgsql
{


    // Comment: Removed the ICryptoTransform implementation as this interface may be not supported by
    // all platforms.

    internal abstract class HashAlgorithm : IDisposable
    {
        protected byte[] HashValue; // Caches the hash after it is calculated.  Accessed through the Hash property.
        protected int HashSizeValue; // The size of the hash in bits.
        protected int State;  // nonzero when in use;  zero when not in use
        private bool disposed;

        /// <summary>
        /// Called from constructor of derived class.
        /// </summary>
        protected HashAlgorithm ()
        {
            disposed = false;
        }

        /// <summary>
        /// Finalizer for HashAlgorithm
        /// </summary>
        ~HashAlgorithm ()
        {
            Dispose(false);
        }

        /// <summary>
        /// Get whether or not the hash can transform multiple blocks at a time.
        /// Note: MUST be overriden if descendant can transform multiple block
        /// on a single call!
        /// </summary>
        public virtual bool CanTransformMultipleBlocks {
            get
            {
                return true;
            }
        }

        public virtual bool CanReuseTransform {
            get
            {
                return true;
            }
        }

        public void Clear()
        {
            // same as System.IDisposable.Dispose() which is documented
            Dispose (true);
        }

        /// <summary>
        /// Computes the entire hash of all the bytes in the byte array.
        /// </summary>
        public byte[] ComputeHash (byte[] input)
        {
            return ComputeHash (input, 0, input.Length);
        }

        public byte[] ComputeHash (byte[] buffer, int offset, int count)
        {
            if (disposed)
                throw new ObjectDisposedException ("HashAlgorithm");

            HashCore (buffer, offset, count);
            HashValue = HashFinal ();
            Initialize ();

            return HashValue;
        }

        public byte[] ComputeHash (Stream inputStream)
        {
            // don't read stream unless object is ready to use
            if (disposed)
                throw new ObjectDisposedException ("HashAlgorithm");

            int l = (int) (inputStream.Length - inputStream.Position);
            byte[] buffer = new byte [l];
            inputStream.Read (buffer, 0, l);

            return ComputeHash (buffer, 0, l);
        }

        // Commented out because it uses the CryptoConfig which can't be available in all platforms

        /// <summary>
        /// Creates the default implementation of the default hash algorithm (SHA1).
        /// </summary>
        /*public static HashAlgorithm Create ()
        {
        	return Create ("System.Security.Cryptography.HashAlgorithm");
        }*/

        /// <summary>
        /// Creates a specific implementation of the general hash idea.
        /// </summary>
        /// <param name="hashName">Specifies which derived class to create.</param>
        /*public static HashAlgorithm Create (string hashName)
        {
        	return (HashAlgorithm) CryptoConfig.CreateFromName (hashName);
        }*/



        // Changed Exception type because it uses the CryptographicUnexpectedOperationException
        // which can't be available in all platforms.
        /// <summary>
        /// Gets the previously computed hash.
        /// </summary>
        public virtual byte[] Hash {
            get
            {
                if (HashValue == null)
                    throw new Exception("HashValue is null");
                return HashValue;
            }
        }

        /// <summary>
        /// When overridden in a derived class, drives the hashing function.
        /// </summary>
        /// <param name="rgb"></param>
        /// <param name="start"></param>
        /// <param name="size"></param>
        protected abstract void HashCore (byte[] rgb, int start, int size);

        /// <summary>
        /// When overridden in a derived class, this pads and hashes whatever data might be left in the buffers and then returns the hash created.
        /// </summary>
        protected abstract byte[] HashFinal ();

        /// <summary>
        /// Returns the size in bits of the hash.
        /// </summary>
        public virtual int HashSize {
            get
            {
                return HashSizeValue;
            }
        }

        /// <summary>
        /// When overridden in a derived class, initializes the object to prepare for hashing.
        /// </summary>
        public abstract void Initialize ();

        protected virtual void Dispose (bool disposing)
        {
            disposed = true;
        }

        /// <summary>
        /// Must be overriden if not 1
        /// </summary>
        public virtual int InputBlockSize {
            get
            {
                return 1;
            }
        }

        /// <summary>
        /// Must be overriden if not 1
        /// </summary>
        public virtual int OutputBlockSize {
            get
            {
                return 1;
            }
        }

        void IDisposable.Dispose ()
        {
            Dispose (true);
            GC.SuppressFinalize (this);  // Finalization is now unnecessary
        }

        /// <summary>
        /// Used for stream chaining.  Computes hash as data passes through it.
        /// </summary>
        /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param>
        /// <param name="inputOffset">The offset into the input buffer to start reading at.</param>
        /// <param name="inputCount">The number of bytes to be copied.</param>
        /// <param name="outputBuffer">The buffer to write the copied data to.</param>
        /// <param name="outputOffset">At what point in the outputBuffer to write the data at.</param>
        public int TransformBlock (byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset)
        {
            Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, outputOffset, inputCount);
            HashCore (inputBuffer, inputOffset, inputCount);

            return inputCount;
        }

        /// <summary>
        /// Used for stream chaining.  Computes hash as data passes through it.  Finishes off the hash.
        /// </summary>
        /// <param name="inputBuffer">The buffer from which to grab the data to be copied.</param>
        /// <param name="inputOffset">The offset into the input buffer to start reading at.</param>
        /// <param name="inputCount">The number of bytes to be copied.</param>
        public byte[] TransformFinalBlock (byte[] inputBuffer, int inputOffset, int inputCount)
        {
            byte[] outputBuffer = new byte[inputCount];

            Buffer.BlockCopy (inputBuffer, inputOffset, outputBuffer, 0, inputCount);

            HashCore (inputBuffer, inputOffset, inputCount);
            HashValue = HashFinal ();
            Initialize ();

            return outputBuffer;
        }
    }

}
