
#ifndef DKUTIL_COMPRESSION_LZSS_HPP
#define DKUTIL_COMPRESSION_LZSS_HPP

#include <dkutil_c/dkcLZSS.h>
#include <dkutil/compression/interface.hpp>

namespace dkutil{

class CLZSS : public IArrayProcess{
	DKC_LZSS *mP;
	DKC_LZSS_HEADER mH;
	int mResult;
	///シグネチャ
	ULONG mSig;
	bool check_sig(DKC_LZSS_HEADER *p)const{
		return p->mSignature == mSig;
	}
public:
	///@see reset()
	CLZSS(ULONG signature=dkcdLZSS_SIGNATURE){
		mP = dkcAllocLZSS();
		DKUTIL_STRUCTURE_INIT(mH);
		mSig = signature;
	}
	~CLZSS(){
		dkcFreeLZSS(&mP);
	}
	///@param signature[in] LZSSで圧縮されていることを証明するID（デフォルトでは dkcdLZSS_SIGNATURE )
	bool reset(ULONG signature=dkcdLZSS_SIGNATURE){
		DKUTIL_STRUCTURE_INIT(mH);
		dkcFreeLZSS(&mP);
		mP = dkcAllocLZSS();
		//setResult(edk_FAILED);
		setResult(edk_SUCCEEDED);
		setSignature(signature);
		return (NULL != mP);
	}

#if 0
	/*!
	@throw 致命的なエラーの場合、std::runtime_errorを投げる
	*/
	template<class BUFFER_T>
	bool encode(const BYTE *src,size_t srcsize,BUFFER_T &buff)
	{
		//バッファの先頭にヘッダファイルを入れる。
		if(buff.size() < srcsize + sizeof(mH)){
			buff.resize(srcsize + sizeof(mH));
		}
		int r = encode(src,srcsize,buff.get(),buff.size());

		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	template<class BUFFER_T>
	bool decode(const BYTE *src,size_t srcsize,BUFFER_T &buff){
		int r = edk_FAILED;
		//バッファの先頭にヘッダファイルがある。
		DKC_LZSS_HEADER *hp = (DKC_LZSS_HEADER *)src;

		dkcmNOT_ASSERT(check_sig(hp)==FALSE);
		if(check_sig(hp)==FALSE){
			return r;
		}
		if(hp->mOriginSize > buff.size())
		{//バッファが足らなかったら。
			//リサイズ
			buff.resize(hp->mOriginSize);
		}		
		
		return DKUTIL_SUCCEEDED_BOOL(decode(src,srcsize,buff.get(),buff.size()));
	}
#endif
	///@return 元のサイズ
	size_t origin_size()const{
		return mH.mOriginSize;
	}

	///@return デコード後のサイズを取得
	virtual size_t decoded_size(const BYTE *src,size_t size)const{
		DKC_LZSS_HEADER *hp = (DKC_LZSS_HEADER *)src;
		if(check_sig(hp)==FALSE){
			return 0;
		}
		return hp->mOriginSize;
	}
	
	///@see class IArrayProcess
	virtual size_t getEncodedSize()const{
		return mH.mCompressedSize + sizeof(mH);//header 分
	}
	
	///@see class IArrayProcess
	virtual size_t getEncodeOutputSize(size_t size)const{
		return size + sizeof(mH);
	}
	/**
	@param pData[in] デコードする予定のデータ
	@param size[in] デコードする予定のサイズ
	@throw アクセス不可な所へアクセスしようとすると std::runtime_errorをぷっぱなす。
	*/
	///デコード時の出力予定のサイズ　
	virtual size_t getDecodeOutputSize(const void *pData,size_t size)const{
		//バッファの先頭にヘッダファイルがある。
		DKC_LZSS_HEADER *hp = (DKC_LZSS_HEADER *)pData;

		//dkcmNOT_ASSERT(check_sig(hp)==FALSE);
		if(check_sig(hp)==FALSE){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CLZSS::getDecodeOutputSize() signature error"));
			return 0;
		}
		return hp->mOriginSize;
	}
	///デコード後有効なデータのサイズ
	virtual size_t getDecodeValidOutputSize(const void *pData,size_t size)const{
		return getDecodeOutputSize(pData,size);
	}
	int decode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		int r = edk_FAILED;
		//バッファの先頭にヘッダファイルがある。
		DKC_LZSS_HEADER *hp = (DKC_LZSS_HEADER *)src;

		//dkcmNOT_ASSERT(check_sig(hp)==FALSE);
		if(check_sig(hp)==FALSE){
			return r;
		}
		if(hp->mOriginSize > destsize){
			return r;
			//buff.resize(hp->mOriginSize);
		}

		r = dkcLZSSDecode(mP,hp,dest,destsize,
			//先頭を抜かす。
			src + sizeof(DKC_LZSS_HEADER),/*srcsize*/size - sizeof(DKC_LZSS_HEADER),
			mSig
		);

		//結果を入れる。
		mResult = r;

		return mResult;

	}
	int encode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		BYTE *bp = dest + sizeof(mH);
		size_t bsize = destsize - sizeof(mH);
		//結果を入れる。
		mResult = dkcLZSSEncode(mP,&mH,bp,bsize,src,size,size,mSig);
		if(DKUTIL_FAILED(mResult)){
			return mResult;
		}

		DKC_LZSS_HEADER *hp = (DKC_LZSS_HEADER *)dest;
		
		*hp = mH;

		return mResult;
	}	
	
	
};
		

}//end of dkutil namespace



#endif