//

// © 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;



namespace DotZLib

{

	/// <summary>

	/// Implements the common functionality needed for all <see cref="Codec"/>s

	/// </summary>

	public abstract class CodecBase : Codec, IDisposable

	{



        #region Data members



        /// <summary>

        /// Instance of the internal zlib buffer structure that is 

        /// passed to all functions in the zlib dll

        /// </summary>

        internal ZStream _ztream = new ZStream();



        /// <summary>

        /// True if the object instance has been disposed, false otherwise

        /// </summary>

        protected bool _isDisposed = false;



        /// <summary>

        /// The size of the internal buffers

        /// </summary>

        protected const int kBufferSize = 16384;



        private byte[] _outBuffer = new byte[kBufferSize];

        private byte[] _inBuffer = new byte[kBufferSize];



        private GCHandle _hInput;

        private GCHandle _hOutput;



        private uint _checksum = 0;



        #endregion



        /// <summary>

        /// Initializes a new instance of the <c>CodeBase</c> class. 

        /// </summary>

		public CodecBase()

		{

            try

            {

                _hInput = GCHandle.Alloc(_inBuffer, GCHandleType.Pinned);

                _hOutput = GCHandle.Alloc(_outBuffer, GCHandleType.Pinned);

            }

            catch (Exception)

            {

                CleanUp(false);

                throw;

            }

        }





        #region Codec Members



        /// <summary>

        /// Occurs when more processed data are available.

        /// </summary>

        public event DataAvailableHandler DataAvailable;



        /// <summary>

        /// Fires the <see cref="DataAvailable"/> event

        /// </summary>

        protected void OnDataAvailable()

        {

            if (_ztream.total_out > 0)

            {

                if (DataAvailable != null)

                    DataAvailable( _outBuffer, 0, (int)_ztream.total_out); 

                resetOutput();

            }

        }



        /// <summary>

        /// Adds more data to the codec to be processed.

        /// </summary>

        /// <param name="data">Byte array containing the data to be added to the codec</param>

        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>

        public void Add(byte[] data)

        {

            Add(data,0,data.Length);

        }



        /// <summary>

        /// Adds more data to the codec to be processed.

        /// </summary>

        /// <param name="data">Byte array containing the data to be added to the codec</param>

        /// <param name="offset">The index of the first byte to add from <c>data</c></param>

        /// <param name="count">The number of bytes to add</param>

        /// <remarks>Adding data may, or may not, raise the <c>DataAvailable</c> event</remarks>

        /// <remarks>This must be implemented by a derived class</remarks>

        public abstract void Add(byte[] data, int offset, int count);



        /// <summary>

        /// Finishes up any pending data that needs to be processed and handled.

        /// </summary>

        /// <remarks>This must be implemented by a derived class</remarks>

        public abstract void Finish();



        /// <summary>

        /// Gets the checksum of the data that has been added so far

        /// </summary>

        public uint Checksum { get { return _checksum; } }



        #endregion



        #region Destructor & IDisposable stuff



        /// <summary>

        /// Destroys this instance

        /// </summary>

        ~CodecBase()

        {

            CleanUp(false);

        }



        /// <summary>

        /// Releases any unmanaged resources and calls the <see cref="CleanUp()"/> method of the derived class

        /// </summary>

        public void Dispose()

        {

            CleanUp(true);

        }



        /// <summary>

        /// Performs any codec specific cleanup

        /// </summary>

        /// <remarks>This must be implemented by a derived class</remarks>

        protected abstract void CleanUp();



        // performs the release of the handles and calls the dereived CleanUp()

        private void CleanUp(bool isDisposing)

        {

            if (!_isDisposed)

            {

                CleanUp();

                if (_hInput.IsAllocated)

                    _hInput.Free();

                if (_hOutput.IsAllocated)

                    _hOutput.Free();



                _isDisposed = true;

            }

        }





        #endregion



        #region Helper methods



        /// <summary>

        /// Copies a number of bytes to the internal codec buffer - ready for proccesing

        /// </summary>

        /// <param name="data">The byte array that contains the data to copy</param>

        /// <param name="startIndex">The index of the first byte to copy</param>

        /// <param name="count">The number of bytes to copy from <c>data</c></param>

        protected void copyInput(byte[] data, int startIndex, int count)

        {

            Array.Copy(data, startIndex, _inBuffer,0, count);

            _ztream.next_in = _hInput.AddrOfPinnedObject();

            _ztream.total_in = 0;

            _ztream.avail_in = (uint)count;



        }



        /// <summary>

        /// Resets the internal output buffers to a known state - ready for processing

        /// </summary>

        protected void resetOutput()

        {

            _ztream.total_out = 0;

            _ztream.avail_out = kBufferSize;

            _ztream.next_out = _hOutput.AddrOfPinnedObject();

        }



        /// <summary>

        /// Updates the running checksum property

        /// </summary>

        /// <param name="newSum">The new checksum value</param>

        protected void setChecksum(uint newSum)

        {

            _checksum = newSum;

        }

        #endregion



    }

}

