
#ifndef DKUTIL_COMPRESSION_RLE_PACKBITS_HPP
#define DKUTIL_COMPRESSION_RLE_PACKBITS_HPP

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

namespace dkutil{

/**
Pack bits型Run Length Encode(Decode)をするクラス

*/
class CRLE_PackBits : public IArrayProcess{
public:
	struct header_type{
		ULONG mSignature;
		DKC_RLE_PACKBITS_HEADER mMain;
	};
private:
	header_type mH;
	size_t mEncodedSize;
	///RLEのカウント数
	int mCount;

	bool check_sig(header_type *p)const{
		bool i1 = p->mSignature == getSignature();
		bool i2 = p->mMain.mCount == mCount;
		return i1 && i2;
	}
public:
	typedef header_type header_type;

	BOOST_STATIC_CONSTANT(size_t,header_size = sizeof(header_type));

	///@see reset()
	CRLE_PackBits (BYTE Count=3,ULONG signature=edk_RLE_PACKBITS_SIGNATURE){
		memset(&mH,0,sizeof(mH));
		reset(Count,signature);
	}
	virtual ~CRLE_PackBits (){

	}
	///@param signature[in] RLEで圧縮されていることを証明するID（デフォルトでは dkcdRLE_SIGNATURE )
	bool reset(BYTE Count,ULONG signature=edk_RLE_PACKBITS_SIGNATURE){
		setResult(edk_FAILED);
		setSignature(signature);
		
		if(Count < 2){//filtering (この処理を削除しない事！）
			Count = 2;
		}
		mCount = Count;



		mEncodedSize = 0;


		return true;
	}

	int decode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		const BYTE *sp = src + header_size;
		size_t ssize = size - header_size;
		int r = edk_FAILED;
		//バッファの先頭にヘッダファイルがある。
		header_type *hp = (header_type*)src;

		dkcmNOT_ASSERT(check_sig(hp)==FALSE);
		if(check_sig(hp)==FALSE){
			return r;
		}
		if(destsize < ssize){
			goto End;
		}
		
		r = dkcRLEPackBitsDecode(&(hp->mMain),dest,destsize,sp,ssize);
		if(DKUTIL_FAILED(r)){
			goto End;
		}
	
	End:
		//結果を入れる。
		setResult(r);

		return r;

	}

	int encode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		BYTE *bp = dest + header_size;
		size_t bsize = destsize - header_size;
		int r = edk_FAILED;
		header_type info;
		
		if(size > bsize){
			goto End;
		}
		r = dkcRLEPackBitsEncode(&(info.mMain),bp,bsize,src,size,
			(BYTE)mCount//mCount &= 255
		);

		if(DKUTIL_FAILED(r)){
			goto End;
		}

		//update
		header_type *hp;
		hp = (header_type *)dest;
		
		info.mSignature = getSignature();
		*hp = info;

		mEncodedSize = info.mMain.mCompressedSize;

	End:
		setResult(r);
		
		return r;
	}	
	
	
	virtual size_t getEncodedSize()const{
		return mEncodedSize  + header_size;
	}
	
	///@see IArrayProcess::getEncodeOutputSize()
	virtual size_t getEncodeOutputSize(size_t size)const{
		return size * 2 + header_size;
	}
	/**
	@param pHeader[in] デコードする予定のデータ
	@param size[in] デコードする予定のサイズ
	@throw アクセス不可な所へアクセスしようとすると std::runtime_errorをぷっぱなす。
	*/
	///デコード時の出力予定のサイズ　
	virtual size_t getDecodeOutputSize(const void *pHeader,size_t size)const{
		if(size < header_size){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CBLockSort::getDecodeOutputSize() size argument exception"));
			return 0;
		}
		header_type *p = (header_type *)pHeader;
		size_t s = p->mMain.mOriginSize;
		return s;
	}
	virtual size_t getDecodeValidOutputSize(const void *pData,size_t size)const{
		return getDecodeOutputSize(pData,size);
	}
	
	
};
		

}//end of dkutil namespace



#endif