#ifndef __xbCOMPRESSLZSS_
#define __xbCOMPRESSLZSS_
/*<<<***************************************************************************
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.
////////////////////////////////////////////////////////////////////////////////
#Title: xbCompressLZSS
#Project: xdcBase
////////////////////////////////////////////////////////////////////////////////
#Author: Marc MIRTI MANCINELLI
#History:
<15/09/2013: creation.
12/02/2014: this notice inserted.
***************************************************************************>>>*/

#ifndef _xbPTR_
#	include "xbPtr.h"
#endif

#ifndef _xbSTREAM_
#	include "xbStream.h"
#endif

#ifndef _xbCOMPRESSITF_
#	include "xbCompressItf.h"
#endif

#ifndef _xbCONFIG_
#	include "xbConfig.h"
#endif

//
// lzss_compress.h
// (c)2002-2003 Jonathan Bennett (jon@hiddensoft.com)
//
// STANDALONE CLASS
//
// This is an implementation of LZSS compression
//
// Note, this is a "as simple to follow as possible" piece of code, there are many
// more efficient and quicker (and cryptic) methods but this was done as a learning
// exercise.
//

//
// LICENSE
// This code is free to use, but please do me the decency of giving me credit :)
//

// The start of the compression stream will contain "other" items apart from data.
// The data is compressed in blocks, the compressed then uncompressed size of the block
// is output followed by the compressed block itself.  This is repeated until a compressed
// block size of 0 is reached
//
// Algorithm ID							4 bytes
// Algorithm mode						4 bytes (1 ULONG)
// Algorithm uncompressed block size	4 bytes (1 ULONG)
// ... (data according mode)
//
//* long block mode case : 
//		Compressed Block1 Size/4bytes	4 bytes (1 ULONG)
//		[Uncompressed Block1 Size		4 bytes (1 ULONG)] if compressed block size & 0x80000000
//		Compressed Block1 Data			nn bytes (where nn=compressed block size)
//		...
//		Compressed Blockn Size/4bytes	4 bytes (1 ULONG)
//		[Uncompressed Blockn Size		4 bytes (1 ULONG)]
//		Compressed Blockn Data			nn bytes (where nn=compressed block size)
//		...
//		00 00 00 00						4 bytes (1 ULONG)
//* short block mode case : 
//		Compressed Block1 Size/2bytes	2 bytes (1 USHORT) (2 bytes aligned)
//		[Uncompressed Block1 Size		2 bytes (1 USHORT)] if compressed block size & 0x8000
//		Compressed Block1 Data			nn bytes (where nn=compressed block size)
//		...
//		Compressed Blockn Size/2bytes	2 bytes (1 USHORT)
//		[Uncompressed Blockn Size		2 bytes (1 USHORT)]
//		Compressed Blockn Data			nn bytes (where nn=compressed block size)
//		...
//		00 00							2 bytes (1 USHORT)
//

// Alg ID
#define xbLZSS_ALGID			"EPLZ"

// Alg mode
#define	xbLZSS_ALGMODE_SHORTBLOCK		0x00000010
#define	xbLZSS_ALGMODE_WINDOWCOEFF		0x0000000F
#define xbLZSS_ALGMODE_HEADERPARAM		0x80000000 //support param list for header
#define xbLZSS_ALGMODE_FILELIST			0x40000000 //support multiple file in one stream
#define xbLZSS_ALGMODE_DEBUGMARKER		0x20000000 //debug markers included in stream

#define	xbLZSS_ALGMODE_SUPPORTED		(xbLZSS_ALGMODE_SHORTBLOCK|xbLZSS_ALGMODE_WINDOWCOEFF|xbLZSS_ALGMODE_HEADERPARAM|xbLZSS_ALGMODE_FILELIST|xbLZSS_ALGMODE_DEBUGMARKER)
#define	xbLZSS_ALGMODE_VALID(mode)		((mode & ~xbLZSS_ALGMODE_SUPPORTED) == 0)

#define	xbLZSS_ALGMODE_WINDOWCOEFF_DFLT		3
#define	xbLZSS_ALGMODE_WINDOWCOEFF_MIN		xbLZSS_ALGMODE_WINDOWCOEFF_START
#define	xbLZSS_ALGMODE_WINDOWCOEFF_MAX		(xbLZSS_ALGMODE_WINDOWCOEFF_START+xbLZSS_ALGMODE_WINDOWCOEFF)
#define	xbLZSS_ALGMODE_WINDOWCOEFF_START	1

inline uint64 marker(const char *pMarker)
{
	uint64 ret=0;
	const uchar *ptr = (const uchar *)pMarker;
	while(*ptr)
	{
		ret >>= 8;
		ret |= uint64(*ptr) << 56;
		ptr++;
	}
	return ret;
}											//  01234567

#define xbLZSSMARKER_BLOCKSTART			marker("EPLZ-STA")
#define xbLZSSMARKER_BLOCKEND			marker("EPLZ-END")

// Block sizes
// Data is compressed in blocks of size below.
// Worst case output for the block is all literals with flags = 9bits per byte (not actually true anymore but close enough!)
#define xbLZSS_BLOCKSIZE		16*1024 //old: 1024 * 1024
#define xbLZSS_BLOCKSIZE_MIN	1024
#define xbLZSS_BLOCKSIZE_MAX	0x7FFFFFF0
#define xbLZSS_BLOCKSIZE_HIGH	(1024*1024*8)
#define xbLZSS_BLOCKSIZE_MID	xbLZSS_BLOCKSIZE_SETTING_SHORTMAX // must be < xbLZSS_BLOCKSIZE_SETTING_SHORTMAX

#define xbLZSS_BLOCKSIZE_SETTING_SHORTMAX	0x7FFC
#define xbLZSS_BLOCKSIZE_SHORTMAX			(0x7FFE * 2)
#define xbLZSS_COMPBLOCKSIZE(blocksize)	((blocksize*10)/8+sizeof(xbLZSS_Header)+sizeof(xbLZSS_LongBlockHeader)+(sizeof(ulong)*2))	// LZSS hdr + block hdr + block + end * 2

struct xbLZSS_Header
{
	char	m_algid[4];
	ulong	m_mode;
	ulong	m_cByteBlockReal;
	xbLZSS_Header()
	{
		memcpy(m_algid,xbLZSS_ALGID,4);
		m_mode = 0;
		m_cByteBlockReal = 0;
	}
}; //12 bytes

struct xbLZSS_LongBlockHeader
{
	ulong	m_cByteComp; //Compressed Block Size
	ulong	m_cByteReal; //Uncompressed Block Size
};

struct xbLZSS_ShortBlockHeader
{
	ushort	m_cByteComp; //Compressed Block Size
	ushort	m_cByteReal; //Uncompressed Block Size
};

// Define static huffman tree structures
#define xbLZSS_HUFF_DELAY					8192	// Trees are regenerated after this many
													// codings
#define xbLZSS_HUFF_MAXCODEBITS				16	// Maximum number of bits for a huffman code

#define	xbLZSS_HUFF_LITERAL_FREQMOD			1	// Frequency modifier for old values
#define xbLZSS_HUFF_MATCHLEN				256	// Match lens are elements 256 and above in the literal alphabet

#define	xbLZSS_HUFF_OFFSET_FREQMOD			1	// Frequency modifier for old values

#define xbLZSS_MINMATCHLEN		3				// Minimum match length

#ifdef _origVALUE
#	define xbLZSS_HUFF_LITERAL_ALPHABETSIZE	292	// Number of characters to represent literals 
												//  + match lengths (36 match len codes, 0-1023)
#	define xbLZSS_HUFF_OFFSET_ALPHABETSIZE	30	// Number of characters to represent offset codes (30 codes <= 0-32767)

#	define xbLZSS_MAXWINDOWLENGTH			(32767 + xbLZSS_MINMATCHLEN)	// Sliding window size

// Match lens (now are closely related to huffman coding, so be careful)
#	define xbLZSS_MAXMATCHLEN				(1023 + xbLZSS_MINMATCHLEN)	// Maximum match 
												//length (1024 lengths over 36 codes) 
												//for full compression
#else
#	define xbLZSS_HUFF_LITERAL_ALPHABETSIZE	304	// Number of characters to represent literals 
												//  + match lengths (36 match len codes, 0-1023)
#	define xbLZSS_HUFF_OFFSET_ALPHABETSIZE	32	// Number of characters to represent offset codes (30 codes <= 0-32767)

#	define xbLZSS_MAXWINDOWLENGTH \
	((1<<(((xbLZSS_HUFF_OFFSET_ALPHABETSIZE - 1 - 4)>>1)+1+2)) + xbLZSS_MINMATCHLEN - 1)
					// Sliding window size

// Match lens (now are closely related to huffman coding, so be careful)
#	define xbLZSS_MAXMATCHLEN \
	((1<<(((xbLZSS_HUFF_LITERAL_ALPHABETSIZE - 1 - xbLZSS_HUFF_MATCHLEN - 8)>>2)+1+3)) + xbLZSS_MINMATCHLEN - 1)
					// Maximum match 
					//length (1024 lengths over 36 codes) 
					//for full compression
#endif

struct xbHuffmanNode
{
	ulong			nFrequency;						// Frequency value
	xbHuffmanNode	*pParent;
	xbHuffmanNode	*pChildLeft;					// Left child node or NULL
	xbHuffmanNode	*pChildRight;					// Right child node or NULL
	bool			bSearchMe;
	uchar			cValue;
}; //20 - 24 bytes ?

struct xbHuffmanOutput
{
	uint	nNumBits;						// Code length 
	uchar	szOutput[xbLZSS_HUFF_MAXCODEBITS];
}; //20 bytes

xbEXPORT void	xbHuffmanZero(xbHuffmanNode *HuffTree, uint nAlphabetSize);	// Clears the frequencies in the huffman tree
xbEXPORT void	xbHuffmanGenerate(xbHuffmanNode *HuffTree, xbHuffmanOutput *HuffOutput, uint nAlphabetSize, uint nFreqMod);

// Define our hash and hash table structure
#define xbLZSS_HASHORDER		3				// Order-3 hashing
#define xbLZSS_HASHTABLE_SIZE	65536			// Our hash function gives values 0-65535
#define	xbLZSS_HASHPRUNEDELAY	128				// Hash table is pruned every n loops (~n*2 bytes)
//#define xbLZSS_HASHMEMLIMIT		1280 * 1024		// Limits the number of hash entries to this mem level
												// In addition uses 
												// HASHMEMLIMIT / sizeof(LZSS_Hash)
												// for allocation heap, 1310720/16=81920 for hash pointers
												// Total memory = ~2MB

struct xbLZSS_Hash
{
	ulong		stmPos;					// Data position in stream
	const uchar	*pMemPos;				// Data position in memory
	xbLZSS_Hash *pNext;					// Next entry in linked list or NULL
	xbLZSS_Hash *pPrev;
}; //16 bytes

//Header and files parameters
struct xbEXPORT xbLZSS_Param
{
	xbLZSS_Param()
	{
		status = 0;
	}
	xbLZSS_Param(const xbString &_name,const xbString &_value)
		:name(_name),value(_value)
	{
		status = 0;
	}
	xbString				name;
	xbString				value;
	ulong					status;
};

//File
struct xbEXPORT xbLZSS_File
{
	xbLZSS_File()
	{
		length = -1;
		status = 0;
	}
	xbLZSS_File(const xbString &_path,ulong _length)
		:path(_path)
	{
		length = _length;
		status = 0;
	}
	xbString				path;
	ulong					length;
	ulong					status;
	xbArray<xbLZSS_Param>	params;
};
xbEXPORT int xbLZSSfile_find(const xbLZSS_File &file1,const void *pFile2);
xbEXPORT int xbLZSSparam_find(const xbLZSS_Param &param1,const void *pParam2);


class xbEXPORT xbLZSS:public xbObject
{
public:
	xbLZSS()
	{
		m_cByteReal = 0;
		m_cByteComp = 0;
		m_cp = CP_UTF8;
		m_bStarted = false;
		m_bWriteTmpExt = false;
		m_pTmpDataStart = NULL;
		m_pCompDataStart = NULL;
		m_pHuffmanLiteralTree = NULL;
		m_bShortBlock = false;
		m_bDebugMarker = false;
		m_windowCoeff = xbLZSS_ALGMODE_WINDOWCOEFF_DFLT;
		m_curFile = 0;
		m_cByteFile = -1;
		m_bAcceptHeaderParam = m_bAcceptFileList = m_bAcceptFileParam = true;
	}

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

	virtual ulong GetMemoryUsage() const
	{
		return m_huffmanLiteralTree.Length
			+ m_huffmanLiteralOutput.Length
			+ m_huffmanOffsetTree.Length
			+ m_huffmanOffsetOutput.Length
			+ m_tmpData.Length
			+ m_compData.Length;
	}
	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:
	bool addFile(const xbString &path,ulong length = -1,ulong cp = CP_ACP,xbOpMode addopm =xbOp_Required,bool bNoAdd = false);
	bool addHeaderParam(const xbString &name,const xbString &val,ulong cp = CP_ACP,xbOpMode addopm =xbOp_Required);
	bool addFileParam(const xbString &name,const xbString &val,ulong cp = CP_ACP,xbOpMode addopm =xbOp_Required);
	virtual void resetMemory()
	{
		m_huffmanLiteralTree.Empty();
		m_huffmanLiteralOutput.Empty();
		m_huffmanOffsetTree.Empty();
		m_huffmanOffsetOutput.Empty();
		m_tmpData.Empty();
		m_compData.Empty();
		m_pTmpDataStart = NULL;
		m_pCompDataStart = NULL;
		m_pHuffmanLiteralTree = NULL;
	}

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

	// huffman functions
	void	huffmanInit(bool bCompress);

	// stream functions
	void dataInit(uint cByteReal,uint cByteComp);

	// temporary stream functions (data waiting compression/decompression)
	void tmpDataResize(uint cByte,bool bKeepPtr);
	void tmpDataWrite(const void *pData,ulong DataLg);
	void tmpDataFlush(bool bEnd = false,bool bClose = false);
	virtual void onTmpDataToComp(bool bEnd,bool bClose) = 0;
	virtual void onTmpDataStreamStart() = 0;
	virtual void onTmpDataWriteTmpExt() {}

	// comp data functions (compressed data waiting stream write)
	void compDataResize(uint cByte,bool bKeepPtr);
	virtual void onCompDataFlush(bool bEnd,bool bClose) = 0;
	inline void compDataWrite(const void *pData,ulong DataLg)
	{
		// Check for possible overrun (compressed buffer is larger than input thankfully)
		if (m_pCompData + DataLg > m_pCompDataEnd)
			xbThrow xbEcompress("LZSS engine : compressed output is larger than buffer","xbLZSScompressPush::compDataWrite()");
		memcpy(m_pCompData,pData,DataLg);
		m_pCompData += DataLg;
	}

	inline void compDataWriteUlong(ulong nb)
	{
		// Check for possible overrun (compressed buffer is larger than input thankfully)
		if (m_pCompData + sizeof(ulong) > m_pCompDataEnd)
			xbThrow xbEcompress("LZSS engine : compressed output is larger than buffer","xbLZSScompressPush::compDataWriteUlong()");
		*((ulong*)m_pCompData) = nb;
		m_pCompData += sizeof(ulong);
	}

	inline void compDataWriteUint64(uint64 nb)
	{
		// Check for possible overrun (compressed buffer is larger than input thankfully)
		if (m_pCompData + sizeof(uint64) > m_pCompDataEnd)
			xbThrow xbEcompress("LZSS engine : compressed output is larger than buffer","xbLZSScompressPush::compDataWriteUlong()");
		*((uint64*)m_pCompData) = nb;
		m_pCompData += sizeof(uint64);
	}

	inline void compDataWriteUshort(ushort nb)
	{
		// Check for possible overrun (compressed buffer is larger than input thankfully)
		if (m_pCompData + sizeof(ushort) > m_pCompDataEnd)
			xbThrow xbEcompress("LZSS engine : compressed output is larger than buffer","xbLZSScompressPush::compDataWriteUlong()");
		*((ushort*)m_pCompData) = nb;
		m_pCompData += sizeof(ushort);
	}

	//main status
	bool							m_bStarted;
	bool							m_bWriteTmpExt;

	//decompressor / compressor settings
	mutable ulong					m_cp;
	bool							m_bShortBlock,m_bDebugMarker;
	ulong							m_windowCoeff;

	// Huffman variables
	xbData					m_huffmanLiteralTree;		// malloc The huffman literal/len tree
	xbHuffmanNode			*m_pHuffmanLiteralTree;		// The huffman literal/len tree
	xbData					m_huffmanLiteralOutput;		// malloc Output buffer for chars
	xbHuffmanOutput			*m_pHuffmanLiteralOutput;	// Output buffer for chars
	ulong					m_nHuffmanLiteralsLeft;				// Number of literals before huffman regenerated
	bool					m_bHuffmanLiteralFullyActive;
	ulong					m_nHuffmanLiteralIncrement;
		
	xbData					m_huffmanOffsetTree;		// malloc The huffman offset tree
	xbHuffmanNode			*m_pHuffmanOffsetTree;		// The huffman offset tree
	xbData					m_huffmanOffsetOutput;		// malloc Output buffer for chars
	xbHuffmanOutput			*m_pHuffmanOffsetOutput;	// Output buffer for chars
	ulong					m_nHuffmanOffsetsLeft;				// Number of literals before huffman regenerated
	bool					m_bHuffmanOffsetFullyActive;
	ulong					m_nHuffmanOffsetIncrement;

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

	//compressor/decompressor buffer
	xbData					m_tmpData;
	uchar					*m_pTmpData,*m_pTmpDataStart,*m_pTmpDataEnd;
	xbData					m_compData;
	uchar					*m_pCompData,*m_pCompDataStart,*m_pCompDataEnd;

	//compressor infos
	xbArray<xbLZSS_File>	m_arrFile;
	xbArray<xbLZSS_Param>	m_arrParam;

	ulong					m_curFile;
	bool					m_bAcceptHeaderParam,m_bAcceptFileList,m_bAcceptFileParam;
};

class xbEXPORT xbLZSScompress:public xbLZSS
{
public:
	xbLZSScompress()
	{
		m_ppHashTable = NULL;
		m_bProgressCb = 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 xbLZSS::GetMemoryUsage()
			+ m_hashTable.Length
			+ m_hashChainCounts.Length
			+ m_hashEntryMemPool.Length
			+ m_hashMemAllocTable.Length;
	}
	virtual void Close(bool bFreeMem = true);

	virtual void SetCodePage(ulong cp);

protected:
	virtual void resetMemory()
	{
		m_ppHashTable = NULL;
		m_hashTable.Empty();
		m_hashChainCounts.Empty();
		m_hashEntryMemPool.Empty();
		m_hashMemAllocTable.Empty();
		xbLZSS::resetMemory();
	}

	// Hash table functions
	void	hashTableInit();				// Make hash table ready for first use
	void	hashTableAdd(const uchar *pData);// Add an entry to the table
	void	hashTablePrune(const uchar *pData,bool bForce = false);
	xbLZSS_Hash* hashEntryAlloc();			// malloc cache
	void	hashEntryFree(xbLZSS_Hash*);	// free cache
	void	hashTableFind(const uchar *pInitialDataPos, uint *pnOffset, uint *pnLen, uint nBestLen);

	// Hash table related variables
	xbData					m_hashTable;				// malloc Hash table 
	xbLZSS_Hash				**m_ppHashTable;			// Hash table 
	xbData					m_hashChainCounts;			// malloc Number of entries in each hash chain
	uint					*m_pHashChainCounts;		// Number of entries in each hash chain
	int						m_nHashTablePurgeCount;
	uint					m_nHashEntriesMax;			// Maximum number of hash entries allowed
	uint					m_nHashEntriesFree;			// Number of free hash positions we have
	xbData					m_hashEntryMemPool;			// malloc Pointer to the single block of memory allocated
	xbLZSS_Hash				*m_pHashEntryMemPool;		// Pointer to the single block of memory allocated
	xbData					m_hashMemAllocTable;		// malloc Our table to keep track of memory allocated
	xbLZSS_Hash				**m_ppHashMemAllocTable;	// Our table to keep track of memory allocated

	bool					m_bLastHash;
	uint					m_nLastHash;

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

	//compressor settings
	int						m_compressLevel;
	bool					m_bLazyEvaluation;
	uint					m_nHashChainLimit;			// The max length of each hash chain

	ulong					m_cByteBlock; //xbLZSS_BLOCKSIZE
	ulong					m_cByteCompBlock; //xbLZSS_COMPBLOCKSIZE

	uchar					*m_pRealDataStart,*m_pRealDataEnd,*m_pRealDataBusyEnd;
	ulong					m_cByteRealBusyEnd,m_cByteRealOffset;
};

class __declspec(uuid("{DFCB1D9A-4EE3-49a0-98A0-E5233D471D3D}")) 
	xbEXPORT xbLZSScompressPush:public xbLZSScompress,public IxbCompressPush
{
public:
	xbLZSScompressPush()
	{
		m_bNotifCb = false;
		ResetCompressSettings();
	}
	virtual ~xbLZSScompressPush()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

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

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

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

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

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

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

	virtual void New(IxbOutputStream *pStm);
	virtual void SetOutputStream(IxbOutputStream *pStm);
	virtual void SetProgressCb(IxbCompressProgress *pProgressCb)
	{
		xbLZSScompress::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 xbLZSScompress::EnumFile(no,pPath,pLength,cp);
	}
	virtual int GetFileCount() const
	{
		return xbLZSScompress::GetFileCount();
	}
	virtual bool GetHeaderParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return xbLZSScompress::GetHeaderParam(name,pVal,cp,opm);
	}
	virtual bool EnumHeaderParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return xbLZSScompress::EnumHeaderParam(no,pName,pVal,cp);
	}
	virtual bool GetFileParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return xbLZSScompress::GetFileParam(name,pVal,cp,opm);
	}
	virtual bool EnumFileParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return xbLZSScompress::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());
		xbLZSScompress::onDestruction(bInDestroy);
	}
	bool initNotifCb();
	bool initProgressCb();

	// data stream functions
	void flush(bool bEnd = false,bool bClose = false);

	// tmp data functions (data waiting compression)
	virtual void onTmpDataStreamStart();
	virtual void onTmpDataWriteTmpExt();
	virtual void onTmpDataToComp(bool bEnd,bool bClose);

	// comp data functions (compressed data waiting stream write)
	virtual void onCompDataFlush(bool bEnd,bool bClose);

	void compDataWriteBits(const uint nValue, uint nNumBits);
	void compDataWriteBitsFlush();
	void compDataWriteHuffman(xbHuffmanOutput *pHuffOutputCode);
	void compDataWriteLiteral(const uint nChar);
	void compDataWriteLen(const uint nLength);
	void compDataWriteOffset(const uint nOffset);
	void compDataFlush();

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

	// Temporary variables used for the bit operations
	ulong	m_nCompressedLong;					// Compressed stream temporary 32bit value
	ushort	m_nCompressedBitsFree;				// Number of bits unused in temporary value
};


class xbEXPORT xbLZSSdecompress:public xbLZSS
{
public:
	xbLZSSdecompress()
	{
		m_bProgressCb = false;
	}

	virtual void SetProgressCb(IxbDecompressProgress *pProgressCb);
	virtual xbString GetCompressLabel(bool bShort = true) const;
	virtual ulong GetMemoryUsage() const
	{
		return xbLZSS::GetMemoryUsage()
/*			+ m_hashTable.Length
			+ m_hashChainCounts.Length
			+ m_hashEntryMemPool.Length
			+ m_hashMemAllocTable.Length
			+ m_tmpData.Length
			+ m_compData.Length*/;
	}
	virtual void Close(bool bFreeMem = true);

protected:
	virtual void resetMemory()
	{
/*		m_hashTable.Empty();
		m_hashChainCounts.Empty();
		m_hashEntryMemPool.Empty();
		m_hashMemAllocTable.Empty();
		m_tmpData.Empty();
		m_compData.Empty();*/
		xbLZSS::resetMemory();
	}

	xbHuffmanNode*	tmpDataReadHuffman(xbHuffmanNode *pHuffTree, uint nAlphabetSize);
	uint			tmpDataReadBits(uint nNumBits);
	uint			tmpDataReadLiteral();
	uint			tmpDataReadLen(uint nCode);
	uint			tmpDataReadOffset();

	//decompression methods
	void decompressBlock();

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

	// Temporary variables used for the bit operations
	ulong					m_nCompressedLong;					// Compressed stream temporary 32bit value
	int						m_nCompressedBitsUsed;				// Number of bits used in temporary value
	const uchar				*m_pTmpBlockPos;

	uchar					*m_pRealDataStart,*m_pRealDataEnd,*m_pRealDataBusyEnd;
	ulong					m_cByteRealBusyEnd,m_cByteRealOffset;

	ulong					m_mode; //header mode
	ulong					m_cByteBlock; //normal real block size
};

class __declspec(uuid("{ADD6A776-17C9-470d-8837-2908F13B9A72}")) 
	xbEXPORT xbLZSSdecompressPush:public xbLZSSdecompress,public IxbDecompressPush
{
public:
	xbLZSSdecompressPush()
	{
		m_readStep = readNone;
	}
	virtual ~xbLZSSdecompressPush()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();

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

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

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

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

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

	virtual void SetProgressCb(IxbDecompressProgress *pProgressCb)
	{
		xbLZSSdecompress::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 xbLZSSdecompress::EnumFile(no,pPath,pLength,cp);
	}
	virtual int GetFileCount() const
	{
		return xbLZSSdecompress::GetFileCount();
	}
	virtual bool GetHeaderParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return xbLZSSdecompress::GetHeaderParam(name,pVal,cp,opm);
	}
	virtual bool EnumHeaderParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return xbLZSSdecompress::EnumHeaderParam(no,pName,pVal,cp);
	}
	virtual bool GetFileParam(const xbString &name, xbString *pVal,ulong cp = CP_ACP,xbOpMode opm =xbOp_Required) const
	{
		return xbLZSSdecompress::GetFileParam(name,pVal,cp,opm);
	}
	virtual bool EnumFileParam(int no, xbString *pName, xbString *pVal,ulong cp = CP_ACP) const
	{
		return xbLZSSdecompress::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 xbLZSSdecompress::GetMemoryUsage()
			+ m_outpData.Length;
	}

protected:
	virtual void resetMemory()
	{
		m_pOutpDataStart = NULL;
		m_outpData.Empty();
		xbLZSSdecompress::resetMemory();
	}
	virtual void onDestruction(bool bInDestroy) 
	{
		eProtectTrace(m_prStm.Empty());
		xbLZSSdecompress::onDestruction(bInDestroy);
	}

	bool initNotifCb();
	bool initProgressCb();

	// data stream functions
	void flush(bool bEnd = false,bool bClose = false);

	// tmp data functions (data waiting compression)
	virtual void onTmpDataStreamStart();
	virtual void onTmpDataToComp(bool bEnd,bool bClose);

	// comp data functions (compressed data waiting stream write)
	virtual void onCompDataFlush(bool bEnd,bool bClose);

	// output stream functions (data to write waiting)
	enum outpStep
	{
		outpNone,
		outpStream,
		outpHeader,
		outpHeaderLengths,
		outpHeaderStrings,
		outpHeaderFile,
		outpFile,
		outpFilePath,
		outpFileLengths,
		outpFileStrings,
	};
	void outpDataAskRead(outpStep step,uint cByte);
	void outpDataWrite(const void *pData,ulong DataLg);
	void outpDataFlush(bool bEnd = false,bool bClose = false);
	void onOutpDataReady(bool bEnd,bool bClose);

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

	enum readStep
	{
		readNone,
		readHeader,
		readShortBlockHeader,
		readShortBlockHeaderClose,
		readShortBlockHeaderReal,
		readLongBlockHeader,
		readLongBlockHeaderClose,
		readLongBlockHeaderReal,
		readBlockData,
	};

	readStep						m_readStep;
	xbLZSS_LongBlockHeader			m_readBlock;

	const uchar						*m_pCompDataToWrite;

	//output buffer
	xbData							m_outpData;
	uchar							*m_pOutpData,*m_pOutpDataStart,*m_pOutpDataEnd;
	outpStep						m_outpStep;
	ulong							m_outpCnt;
	xbLZSS_File						m_outpFile;
	xbLZSS_Param					m_outpParam;
};


class __declspec(uuid("{B3D6FC31-E7E5-4c76-AA10-4D22DDEE0265}")) 
	xbEXPORT xbLZSSplug:public xbHotPlugObject<xbObject>,public IxbCompressPlug
{
public:
	xbLZSSplug()
	{
	}
	virtual ~xbLZSSplug()
	{
		eProtectTrace(Destroy());
	}

	xbImplObject();
	xbHotPlugCreateDcl(xbLZSSplug,IxbCompressPlug);

	virtual bool GetInterface(REFIID riid, void ** ppInterf)
	{
		xbImplInterface(IxbCompressPlug);
		xbImplInterface(xbLZSSplug);
		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
