#ifndef _EPROCOMPGZIP_PLUG_
#define _EPROCOMPGZIP_PLUG_
/*<<<***************************************************************************
The MIT License (MIT)

Copyright(C) 2013 - 2015 Marc Mirti Mancinelli, Christophe Lardau and Philippe Pamart (xdcube.org)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

!! IMPORTANT !!
This plugin/software uses and integrates the following sources which are 
obviously not covered/excluded from this license: 
- Independent JPEG Group's software library 6b used for jpeg support 
	(folder jpeg-6b, see README.txt for license)
- Glenn Randers- Pehrson's libpng-1.2.8
	(folder libpng-128, see LICENSE.txt)
- Jean-loup Gailly/Mark Adler's zlib-1.2.3 for png support and gzip compression
	(folder zlib-123, see README.txt for license)

For this source, please refer to their own license.
////////////////////////////////////////////////////////////////////////////////
#Title: xdcCompGzip
#Project: xdcPicStd
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#include <xbCompressItf.h>
#include "zlib-123/zlib.h"

#define eproGZIP_BLOCKSIZE		8192
#define eproGZIP_BLOCKSIZE_MIN	2048
#define eproGZIP_BLOCKSIZE_MAX	65536
#define eproGZIP_BLOCKSIZE_MID	16384
#define eproGZIP_BLOCKSIZE_HIGH	eproGZIP_BLOCKSIZE_MAX
/*
level: The compression level must be Z_DEFAULT_COMPRESSION, or between 0 and 9: 1 gives best speed, 9 gives best compression, 0 gives no compression at all (the input data is simply copied a block at a time). 
windowBits: 8 - 15, 15= best compression and max mem usage, default: 15
memLevel: 1 - 9, 9=max mem usage and best speed, default: 8
*/
#pragma pack(push,1)

struct gz_Header
{
	uchar	magic[2];
	uchar	compressionMethod;
	uchar	flags;
	ulong	modifTime;
	uchar	extraFlags;
	uchar	os;
	gz_Header()
	{
		xbRdbgAssert(sizeof(gz_Header)==10);
	}
};

#pragma pack(pop)

class eproGZIP:public xbHotPlugObject<xbObject>
{
public:
	eproGZIP()
	{
		m_cp = CP_UTF8;
		m_bStarted = false;
		m_cByteReal = 0;
		m_cByteComp = 0;
	}

	ulong getCodePage() const;
	virtual void Close(bool bFreeMem = true);

	virtual ulong GetMemoryUsage() const
	{
		return 0;
	}
	virtual bool GetIOstat(ulong *pcByteReal,ulong *pcByteComp) const
	{
		if (pcByteReal)
			*pcByteReal = m_cByteReal;
		if (pcByteComp)
			*pcByteComp = m_cByteComp;
		return m_cByteReal != 0 || m_cByteComp != 0;
	}
	virtual void ResetIOstat()
	{
		m_cByteReal = 0;
		m_cByteComp = 0;
	}
	virtual bool EnumFile(int no, xbString *pPath, ulong *pLength,ulong cp = CP_ACP) const;
	virtual int GetFileCount() const;
	virtual bool GetFileParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const;
	virtual bool EnumFileParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const;
	virtual bool GetHeaderParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const;
	virtual bool EnumHeaderParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const;

	virtual ulong GetCaps() const; //cf xbCOMPRESSCAPS_

protected:
	virtual void resetMemory()
	{
	}

	virtual void onDestruction(bool /*bInDestroy*/) 
	{
		eProtectTrace(Close());
	}


	//main status
	bool							m_bStarted;

	//decompressor / compressor settings
	mutable ulong					m_cp;

	//compressor/decompressor stats
	ulong					m_cByteReal;
	ulong					m_cByteComp;
};

class eproGZIPcompress:public eproGZIP
{
public:
	eproGZIPcompress()
	{
		m_bProgressCb = false;
		m_bZstreamInit = false;
	}

	virtual void SetProgressCb(IxbCompressProgress *pProgressCb);
	virtual xbString GetCompressLabel(bool bShort = true) const;
	virtual bool GetCompressLevelRange(int *pMin, int *pMax, int *pDflt) const;
	virtual bool GetCompressBlockRange(int *pMin, int *pMax, int *pDflt) const;
	virtual bool EnumCompressSetting(int no, xbString *pName, xbString *pLabel, int *pMin, int *pMax, int *pDflt) const;

	virtual void SetCompressLevel(int compressLevel);
	virtual int GetCompressLevel() const;
	virtual void SetCompressBlock(int compressBlock);
	virtual int GetCompressBlock() const;
	virtual bool SetCompressSetting(LPCSTR pszName,int val,xbOpMode opm =xbOp_Required);
	virtual int GetCompressSetting(LPCSTR pszName,bool *pbExist = NULL) const;
	virtual void ResetCompressSettings(int compressLevel = xbCompressDefault);

	virtual ulong GetMemoryUsage() const
	{
		return eproGZIP::GetMemoryUsage()
			+(1 << (m_compressWindowBits+2)) + (1 << (m_compressMemLevel+9))
			+m_cByteBlock
			;
	}
	virtual void Close(bool bFreeMem = true);

	virtual void SetCodePage(ulong cp);

protected:
	virtual void resetMemory()
	{
		zStreamEnd();
		m_compData.Empty();
		eproGZIP::resetMemory();
	}
	virtual void zStreamEnd()
	{
		if(!m_bZstreamInit)
			return;
		deflateEnd(&m_zStream);
		m_bZstreamInit = false;
	}

	//main status
	xbRefPtr<IxbCompressProgress>	m_prProgressCb;
	bool							m_bProgressCb;

	//compressor settings
	int						m_compressLevel;
	int						m_compressMemLevel;
	int						m_compressWindowBits;
	ulong					m_cByteBlock;

	//compressor buffers
	bool					m_bZstreamInit;
	z_stream				m_zStream;
	xbData					m_compData;
	ulong					m_crc;
};

class __declspec(uuid("{F4900AD1-FC25-4162-9A2B-D468B5C4CD16}")) 
	eproGZIPcompressPush:public eproGZIPcompress,public IxbCompressPush
{
public:
	eproGZIPcompressPush()
	{
		m_bNotifCb = false;
		ResetCompressSettings();
	}
	virtual ~eproGZIPcompressPush()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbCompressPush);
		xbImplInterface(IxbOutputStream);
		xbImplInterface(eproGZIPcompressPush);
		return eproGZIPcompress::GetInterface(riid,ppInterf);
	}

//IxbCompress
	virtual xbString GetCompressLabel(bool bShort = true) const
	{
		return eproGZIPcompress::GetCompressLabel(bShort);
	}
	virtual bool GetCompressLevelRange(int *pMin, int *pMax, int *pDflt) const
	{
		return eproGZIPcompress::GetCompressLevelRange(pMin,pMax,pDflt);
	}
	virtual bool GetCompressBlockRange(int *pMin, int *pMax, int *pDflt) const
	{
		return eproGZIPcompress::GetCompressBlockRange(pMin,pMax,pDflt);
	}
	virtual bool EnumCompressSetting(int no, xbString *pName, xbString *pLabel, int *pMin, int *pMax, int *pDflt) const
	{
		return eproGZIPcompress::EnumCompressSetting(no,pName,pLabel,pMin,pMax,pDflt);
	}
	virtual ulong GetCaps() const
	{
		return eproGZIPcompress::GetCaps();
	}

	virtual void SetCompressLevel(int compressLevel)
	{
		eproGZIPcompress::SetCompressLevel(compressLevel);
	}

	virtual int GetCompressLevel() const
	{
		return eproGZIPcompress::GetCompressLevel();
	}
	virtual void SetCompressBlock(int compressBlock)
	{
		eproGZIPcompress::SetCompressBlock(compressBlock);
	}
	virtual int GetCompressBlock() const
	{
		return eproGZIPcompress::GetCompressBlock();
	}
	virtual bool SetCompressSetting(LPCSTR pszName,int val,xbOpMode opm =xbOp_Required)
	{
		return eproGZIPcompress::SetCompressSetting(pszName,val,opm);
	}
	virtual int GetCompressSetting(LPCSTR pszName,bool *pbExist = NULL) const
	{
		return eproGZIPcompress::GetCompressSetting(pszName,pbExist);
	}
	virtual void ResetCompressSettings(int compressLevel = xbCompressDefault)
	{
		eproGZIPcompress::ResetCompressSettings(compressLevel);
	}

	virtual ulong GetMemoryUsage() const
	{
		return eproGZIPcompress::GetMemoryUsage();
	}
	virtual bool GetIOstat(ulong *pcByteReal,ulong *pcByteComp) const
	{
		return eproGZIPcompress::GetIOstat(pcByteReal,pcByteComp);
	}
	virtual void ResetIOstat()
	{
		eproGZIPcompress::ResetIOstat();
	}

	virtual void SetCodePage(ulong cp)
	{
		eproGZIPcompress::SetCodePage(cp);
	}
	virtual ulong GetCodePage() const
	{
		return getCodePage();
	}

	virtual void New(IxbOutputStream *pStm);
	virtual void SetOutputStream(IxbOutputStream *pStm);
	virtual void SetProgressCb(IxbCompressProgress *pProgressCb)
	{
		eproGZIPcompress::SetProgressCb(pProgressCb);
	}
	virtual void SetNotifCb(IxbCompressNotif *pNotifCb);
	virtual bool GetOutputStream(xbRefPtr<IxbOutputStream> *pprStm,xbOpMode opm =xbOp_Required);
	virtual void CompressFlush();
	virtual void Close(bool bFreeMem = true);
	virtual bool IsCompressStarted() const
	{
		return m_bStarted;
	}

	virtual bool NewFile(const xbString &path,ulong length = -1,ulong cp = CP_ACP,xbOpMode addopm = xbOp_Required); //return false if file is already in list, in this case seek only 
	virtual bool AddFileParam(const xbString &name, const xbString &val,ulong cp = CP_ACP,xbOpMode addopm = xbOp_Required);

	virtual bool AddHeaderParam(const xbString &name, const xbString &val,ulong cp = CP_ACP,xbOpMode addopm = xbOp_Required);

	virtual bool EnumFile(int no, xbString *pPath, ulong *pLength,ulong cp = CP_ACP) const
	{
		return eproGZIPcompress::EnumFile(no,pPath,pLength,cp);
	}
	virtual int GetFileCount() const
	{
		return eproGZIPcompress::GetFileCount();
	}
	virtual bool GetHeaderParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return eproGZIPcompress::GetHeaderParam(name,pVal,cp,opm);
	}
	virtual bool EnumHeaderParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return eproGZIPcompress::EnumHeaderParam(no,pName,pVal,cp);
	}
	virtual bool GetFileParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return eproGZIPcompress::GetFileParam(name,pVal,cp,opm);
	}
	virtual bool EnumFileParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return eproGZIPcompress::EnumFileParam(no,pName,pVal,cp);
	}

//IxbOutputStream
	virtual bool			CanWrite() const;
	virtual ulong			Write(const void *pData,ulong DataLg,bool DataLg_Required = true);
	virtual ulong			WriteStream(IxbInputStream *pstm);
	virtual ulong			WriteStream(IxbInputStream *pstm,ulong DataLg,bool DataLg_Required = true);

	virtual ulong			WriteString(const char *pStr,ulong lgStr,bool bAddLf = false,IxbStreamMode *pMode = NULL);
	virtual ulong			WriteStringW(const wchar *pStr,ulong lgStr,bool bAddLf = false,IxbStreamMode *pMode = NULL);


protected:
	virtual void onDestruction(bool bInDestroy) 
	{
		eProtectTrace(m_prStm.Empty());
		eProtectTrace(zStreamEnd());
		eproGZIPcompress::onDestruction(bInDestroy);
	}
	bool initNotifCb();
	bool initProgressCb();

	// data stream functions
	void onCompressStreamStart();
	void flush(bool bEnd = false,bool bClose = false);
	void dataCompressWrite(const void *_pData,ulong DataLg);
	void dataCompressFlush(bool bEnd,bool bClose);

	//main status
	xbRefPtr<IxbOutputStream>		m_prStm;
	xbRefPtr<IxbCompressNotif>		m_prNotifCb;
	bool							m_bNotifCb;
};


class eproGZIPdecompress:public eproGZIP
{
public:
	eproGZIPdecompress()
	{
		m_bProgressCb = false;
		m_bZstreamInit = false;
	}

	virtual void SetProgressCb(IxbDecompressProgress *pProgressCb);
	virtual xbString GetCompressLabel(bool bShort = true) const;
	virtual ulong GetMemoryUsage() const
	{
		return eproGZIP::GetMemoryUsage()
			+(1 << MAX_WBITS) + 1440*2*sizeof(int)
			+m_cByteBlock;
	}
	virtual void Close(bool bFreeMem = true);

protected:
	virtual void resetMemory()
	{
		zStreamEnd();
		m_decompData.Empty();
		eproGZIP::resetMemory();
	}
	virtual void zStreamEnd()
	{
		if(!m_bZstreamInit)
			return;
		inflateEnd(&m_zStream);
		m_bZstreamInit = false;
	}

	//main status
	xbRefPtr<IxbDecompressProgress>	m_prProgressCb;
	bool							m_bProgressCb;
	ulong							m_cByteBlock;

	//compressor buffers
	bool					m_bZstreamInit;
	z_stream				m_zStream;
	xbData					m_decompData;
	ulong					m_crc;
};

class __declspec(uuid("{0846468B-18FB-4d56-85D5-264577AB2817}")) 
	eproGZIPdecompressPush:public eproGZIPdecompress,public IxbDecompressPush
{
public:
	eproGZIPdecompressPush()
	{
		m_readStep = readNone;
	}
	virtual ~eproGZIPdecompressPush()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbDecompressPush);
		xbImplInterface(IxbOutputStream);
		xbImplInterface(eproGZIPdecompressPush);
		return eproGZIPdecompress::GetInterface(riid,ppInterf);
	}

//IxbCompress
	virtual void New();
	virtual void SetOutputStream(IxbOutputStream *pFileStm);

	virtual xbString GetCompressLabel(bool bShort = true) const
	{
		return eproGZIPdecompress::GetCompressLabel(bShort);
	}
	virtual ulong GetCaps() const
	{
		return eproGZIPdecompress::GetCaps();
	}

	virtual bool GetIOstat(ulong *pcByteReal,ulong *pcByteComp) const
	{
		return eproGZIPdecompress::GetIOstat(pcByteReal,pcByteComp);
	}
	virtual void ResetIOstat()
	{
		eproGZIPdecompress::ResetIOstat();
	}

	virtual ulong GetCodePage() const
	{
		return getCodePage();
	}

	virtual void SetProgressCb(IxbDecompressProgress *pProgressCb)
	{
		eproGZIPdecompress::SetProgressCb(pProgressCb);
	}
	virtual void SetNotifCb(IxbDecompressNotif *pNotifCb);
	virtual bool GetOutputStream(xbRefPtr<IxbOutputStream> *pprStm,xbOpMode opm =xbOp_Required);
	virtual void Close(bool bFreeMem = true);
	virtual void DecompressFlush();
	virtual bool IsDecompressStarted() const
	{
		return m_bStarted;
	}

	virtual bool EnumFile(int no, xbString *pPath, ulong *pLength,ulong cp = CP_ACP) const
	{
		return eproGZIPdecompress::EnumFile(no,pPath,pLength,cp);
	}
	virtual int GetFileCount() const
	{
		return eproGZIPdecompress::GetFileCount();
	}
	virtual bool GetHeaderParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return eproGZIPdecompress::GetHeaderParam(name,pVal,cp,opm);
	}
	virtual bool EnumHeaderParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return eproGZIPdecompress::EnumHeaderParam(no,pName,pVal,cp);
	}
	virtual bool GetFileParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return eproGZIPdecompress::GetFileParam(name,pVal,cp,opm);
	}
	virtual bool EnumFileParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return eproGZIPdecompress::EnumFileParam(no,pName,pVal,cp);
	}

//IxbOutputStream
	virtual bool			CanWrite() const;
	virtual ulong			Write(const void *pData,ulong DataLg,bool DataLg_Required = true);
	virtual ulong			WriteStream(IxbInputStream *pstm);
	virtual ulong			WriteStream(IxbInputStream *pstm,ulong DataLg,bool DataLg_Required = true);

	virtual ulong			WriteString(const char *pStr,ulong lgStr,bool bAddLf = false,IxbStreamMode *pMode = NULL);
	virtual ulong			WriteStringW(const wchar *pStr,ulong lgStr,bool bAddLf = false,IxbStreamMode *pMode = NULL);


	virtual ulong GetMemoryUsage() const
	{
		return eproGZIPdecompress::GetMemoryUsage();
	}

protected:
	virtual void resetMemory()
	{
		eproGZIPdecompress::resetMemory();
	}
	virtual void onDestruction(bool bInDestroy) 
	{
		eProtectTrace(zStreamEnd());
		eProtectTrace(m_prStm.Empty());
		eproGZIPdecompress::onDestruction(bInDestroy);
	}

	bool initNotifCb();
	bool initProgressCb();

	// data stream functions
	void flush(bool bEnd = false,bool bClose = false);
	void dataDecompressWrite(const void *_pData,ulong DataLg);
	void dataDecompressFlush(bool bEnd,bool bClose);
	virtual void onDecompressStreamStart();

	xbRefPtr<IxbOutputStream>		m_prStm;
	xbRefPtr<IxbDecompressNotif>	m_prNotifCb;
	bool							m_bNotifCb;

	enum readStep
	{
		readNone,
		readHeader,
		readExtraFieldLength,
		readExtraFieldData,
		readOrigName,
		readComment,
		readHeaderCrc,
		readStream,
		readStreamCrc,
		readEnd
	};

	readStep						m_readStep;
	gz_Header						m_gzHeader;
	xbData							m_tmpData;
	ushort							m_dataToSkip;

	int stopCharPos(const uchar *pData,int DataLg)
	{
		int pos = 0;
		while(pos++ < DataLg)
		{
			if (*(pData++) == 0)
				return pos;
		}
		return -1;
	}
};


class __declspec(uuid("{506755AC-ECD5-472c-B6FC-93BB8056F453}")) 
	eproGZIPplug:public xbHotPlugObject<xbObject>,public IxbCompressPlug
{
public:
	eproGZIPplug()
	{
	}
	virtual ~eproGZIPplug()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();
	xbHotPlugCreateDcl(eproGZIPplug,IxbCompressPlug);

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbCompressPlug);
		xbImplInterface(eproGZIPplug);
		return xbObject::GetInterface(riid,ppInterf);
	}

//IxbCompressPlug
	virtual bool CanWorkWithType(LPCSTR pszType, IxbInputStream *pStm) const;

	virtual bool Decompress(IxbInputStream *pStm, ulong caps, xbRefPtr<IxbDecompressPull> *pprUncompStm,xbOpMode opm =xbOp_Required);
	virtual bool Decompress(IxbOutputStream *pStm, ulong caps, xbRefPtr<IxbDecompressPush> *pprUncompStm,xbOpMode opm =xbOp_Required);
	virtual bool Compress(IxbOutputStream *pStm, ulong caps, xbRefPtr<IxbCompressPush> *pprCompStm,xbOpMode opm =xbOp_Required);
	virtual bool Compress(IxbInputStream *pStm, ulong caps, xbRefPtr<IxbCompressPull> *pprCompStm,xbOpMode opm =xbOp_Required);

	virtual void askClose(LPCSTR pReason);
	virtual xbString getCloseReason() const;

	virtual xbString GetCompressLabel(bool bShort = true) const;
	virtual bool GetCompressLevelRange(int *pMin, int *pMax, int *pDflt) const;
	virtual bool GetCompressBlockRange(int *pMin, int *pMax, int *pDflt) const;
	virtual bool EnumCompressSetting(int no, xbString *pName, xbString *pLabel, int *pMin, int *pMax, int *pDflt) const;
	virtual ulong GetCaps() const;

	void throwEnd(const xbString &strEnd,const xbString &strCtx) const;
protected:
	xbString m_closeReason;
};


#endif

