///////////////////////////////////////////////////////////////////////////////
/// @file dePluginZLib.cpp
///
/// @brief ZLib compression plugin
///
/// @author Lightning
///
/// This file is the intellectual property of Novus Delta, LLC.. Usage of the
/// contents of this file is subject to the Destiny3D Member License which
/// can be found at http://www.destiny3d.com.  Any other usage is prohibited.
///
/// This file is distributed "AS IS" without warranty of any kind.  Novus
/// Delta, LLC. does not guarantee the fitness of the contents of this file
/// for any particular purpose.
///
/// Copyright (C) 2001-2003 Novus Delta, LLC. All Rights Reserved.
///
/// <hr>
///                                 Change History
/// <hr>
///
/// @date Feb 2002
/// @author Lightning
/// @remarks Creation
///
/// @date Jun 2003
/// @author Lightning
/// @remarks Renamed from deCompressionZLib.cpp to dePluginZLib.cpp
///
///////////////////////////////////////////////////////////////////////////////

#include <windows.h>
#include "dePlugins_priv.hpp"
#include "ZLib/Zlib.h"


//the zlib compression plugin

//ID of twofish encryption
#ifdef DE3D_CPU_LITTLE_ENDIAN
#define ZLibID	0x00004C5A		//ZL\0\0, ZLib, version 0.0
#else
#define ZLibID	0x5A4C0000		//ZL\0\0, ZLib, version 0.0
#endif

dePluginZLib::dePluginZLib()
{
	//setup the defaults
	priv_DataType = BINARY;		//binary data
	priv_CompressAmount = 9;				//max compress
}

dePluginZLib::~dePluginZLib()
{
	//reset everything
	priv_DataType = BINARY;		//binary data
	priv_CompressAmount = 9;				//max compress
}

void* dePluginZLib::GetInterface(IdePlugin::interface_t i)
{
	if (i == IdePlugin::iface_IdePlugin)
		return (IdePlugin*)(this);
	else if (i == IdePlugin::iface_IdePluginZLib)
		return (IdePluginZLib*)(this);
	return NULL;
}

int dePluginZLib::Release()
{
	delete this;
	return 0;
}

deBoolean dePluginZLib::EncodeData(void *Buffer, DWORD Length, void *NewBuffer, DWORD *NewLength)
{
	z_stream	c_stream;

	//if no valid pointers, return deFALSE
	if(!Buffer || !Length || !NewBuffer || !NewLength)
		return deFALSE;

	//verify that *NewLength is at least the size of the needed length
	if(*NewLength < MaxEncodeLength(Length))
		return deFALSE;

	//setup the beginning part of the buffer for the header
	((ZLibHeader *)NewBuffer)->ID = ZLibID;				//id that it is ZLib
	((ZLibHeader *)NewBuffer)->OriginalSize = deByteOrderBE32(Length);	//original length

	memset(&c_stream, 0, sizeof(c_stream));

	//setup the compression structure
	c_stream.avail_out = *NewLength;
    c_stream.zalloc = (alloc_func)0;
    c_stream.zfree = (free_func)0;
    c_stream.opaque = (voidpf)0;
	c_stream.next_in = (BYTE *)Buffer;
	c_stream.avail_in = Length;
	c_stream.next_out = &((BYTE *)NewBuffer)[sizeof(ZLibHeader)];

	//set the type default
	c_stream.data_type = Z_BINARY;

	//if they want ascii, set it
	if(priv_DataType == ASCII)
		c_stream.data_type = Z_ASCII;

	//init the compression
	if (deflateInit(&c_stream, priv_CompressAmount) != Z_OK)
		return deFALSE;

	//compress it
	if (deflate(&c_stream, Z_FINISH) != Z_STREAM_END)
		return deFALSE;

	//clear compress buffers
	if (deflateEnd(&c_stream) != Z_OK)
		return deFALSE;

	//set the compressed size
	*NewLength = c_stream.total_out + sizeof(ZLibHeader);

	//make sure we went thru everything
	if(c_stream.total_in != Length)
		return deFALSE;

	return TRUE;
}

deBoolean dePluginZLib::DecodeData(void *Buffer, DWORD Length, void *NewBuffer, DWORD *NewLength)
{
	z_stream		d_stream;

	//if no valid pointers, return deFALSE
	if(!Buffer || !Length || !NewBuffer || !NewLength)
		return deFALSE;

	//adjust the length to not account for the header
	Length = Length - sizeof(ZLibHeader);

	//verify the header
	if(((ZLibHeader *)Buffer)->ID != ZLibID)
		return deFALSE;

	//verify the new length is large enough
	if(*NewLength < deByteOrderBE32(((ZLibHeader *)Buffer)->OriginalSize))
		return deFALSE;

	//setup the decompression structure
    d_stream.zalloc = (alloc_func)0;
    d_stream.zfree = (free_func)0;
    d_stream.opaque = (voidpf)0;
	d_stream.next_in = &((BYTE *)Buffer)[sizeof(ZLibHeader)];	//skip ahead of our header
	d_stream.next_out = (BYTE *)NewBuffer;
	d_stream.avail_in = Length;
	d_stream.avail_out = *NewLength;

	//default to binary
	d_stream.data_type = Z_BINARY;
	
	//if they want ascii, do it
	if(priv_DataType == ASCII)
		d_stream.data_type = Z_ASCII;

	//init the decompression
	if (inflateInit(&d_stream) != Z_OK)
		return deFALSE;

	//decompress
	if (inflate(&d_stream, Z_FINISH) != Z_STREAM_END)
		return deFALSE;

	//clear decompress buffers
	if (inflateEnd(&d_stream) != Z_OK)
		return deFALSE;

	//verify that the total out is the original size
	if(d_stream.total_out != deByteOrderBE32(((ZLibHeader *)Buffer)->OriginalSize))
		return deFALSE;

	//set the newlength to the original size
	*NewLength = deByteOrderBE32(((ZLibHeader *)Buffer)->OriginalSize);

	//everything went fine, return
	return deTRUE;
}

DWORD dePluginZLib::MaxEncodeLength(DWORD Length)
{
	//return the maximum length for compressed data
	DWORD	NeededLength;

	//figure out the maximum required size for compressed data
	NeededLength = Length + (long)(Length * 0.1f) + 13;

	//add in our header area
	NeededLength = NeededLength + sizeof(ZLibHeader);

	//return it
	return NeededLength;
}

DWORD dePluginZLib::MaxDecodeLength(void *Buffer)
{
	//return the maximum length for decompression

	//if no buffer, return 0
	if(!Buffer)
		return 0;

	//verify the header
	if(((ZLibHeader *)Buffer)->ID != ZLibID)
		return 0;

	//return the size of the original buffer
	return deByteOrderBE32(((ZLibHeader *)Buffer)->OriginalSize);
}

deBoolean dePluginZLib::SetType(DataType Type)
{
	//set the type of data we are compressing
	priv_DataType = Type;
	return deTRUE;
}

deBoolean dePluginZLib::SetCompressAmount(long Amount)
{
	//set the amount to compress the data by, only 0 to 9 allowed
	if(Amount < 0 || Amount > 9)
		return deFALSE;

	//set and return
	priv_CompressAmount = Amount;
	return deTRUE;
}
