//
// © 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
{
	/// 
	/// Implements the common functionality needed for all s
	/// 
	public abstract class CodecBase : Codec, IDisposable
	{
        #region Data members
        /// 
        /// Instance of the internal zlib buffer structure that is
        /// passed to all functions in the zlib dll
        /// 
        internal ZStream _ztream = new ZStream();
        /// 
        /// True if the object instance has been disposed, false otherwise
        /// 
        protected bool _isDisposed = false;
        /// 
        /// The size of the internal buffers
        /// 
        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
        /// 
        /// Initializes a new instance of the CodeBase class.
        /// 
		public CodecBase()
		{
            try
            {
                _hInput = GCHandle.Alloc(_inBuffer, GCHandleType.Pinned);
                _hOutput = GCHandle.Alloc(_outBuffer, GCHandleType.Pinned);
            }
            catch (Exception)
            {
                CleanUp(false);
                throw;
            }
        }
        #region Codec Members
        /// 
        /// Occurs when more processed data are available.
        /// 
        public event DataAvailableHandler DataAvailable;
        /// 
        /// Fires the  event
        /// 
        protected void OnDataAvailable()
        {
            if (_ztream.total_out > 0)
            {
                if (DataAvailable != null)
                    DataAvailable( _outBuffer, 0, (int)_ztream.total_out);
                resetOutput();
            }
        }
        /// 
        /// Adds more data to the codec to be processed.
        /// 
        /// Byte array containing the data to be added to the codec
        /// Adding data may, or may not, raise the DataAvailable event
        public void Add(byte[] data)
        {
            Add(data,0,data.Length);
        }
        /// 
        /// Adds more data to the codec to be processed.
        /// 
        /// Byte array containing the data to be added to the codec
        /// The index of the first byte to add from data
        /// The number of bytes to add
        /// Adding data may, or may not, raise the DataAvailable event
        /// This must be implemented by a derived class
        public abstract void Add(byte[] data, int offset, int count);
        /// 
        /// Finishes up any pending data that needs to be processed and handled.
        /// 
        /// This must be implemented by a derived class
        public abstract void Finish();
        /// 
        /// Gets the checksum of the data that has been added so far
        /// 
        public uint Checksum { get { return _checksum; } }
        #endregion
        #region Destructor & IDisposable stuff
        /// 
        /// Destroys this instance
        /// 
        ~CodecBase()
        {
            CleanUp(false);
        }
        /// 
        /// Releases any unmanaged resources and calls the  method of the derived class
        /// 
        public void Dispose()
        {
            CleanUp(true);
        }
        /// 
        /// Performs any codec specific cleanup
        /// 
        /// This must be implemented by a derived class
        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
        /// 
        /// Copies a number of bytes to the internal codec buffer - ready for proccesing
        /// 
        /// The byte array that contains the data to copy
        /// The index of the first byte to copy
        /// The number of bytes to copy from data
        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;
        }
        /// 
        /// Resets the internal output buffers to a known state - ready for processing
        /// 
        protected void resetOutput()
        {
            _ztream.total_out = 0;
            _ztream.avail_out = kBufferSize;
            _ztream.next_out = _hOutput.AddrOfPinnedObject();
        }
        /// 
        /// Updates the running checksum property
        /// 
        /// The new checksum value
        protected void setChecksum(uint newSum)
        {
            _checksum = newSum;
        }
        #endregion
    }
}