/**
@filename convert_interface.hpp
@brief converter interface and simple logic defined...
@note
<h2>convert block 概念</h2>

ストリーム上に以下のような構造がある
first header (generic_convert_first_header固定 ファイルサイズなどを示している）
second header(コンバーターによってサイズや仕様は違う）
data block   (コンバートしたデータの部分）

これをconvert blockする。
*/

#ifndef DKUTIL_DETAIL_CONVERT_INTERFACE_HPP
#define DKUTIL_DETAIL_CONVERT_INTERFACE_HPP

#include <dkutil/define.hpp>
#include <dkutil/config.hpp>
#include <dkutil/shared_buffer.hpp>
#include <dkutil/detail/serialize_interface.hpp>

namespace dkutil{

class IStream;


///IStreamConvertインターフェイスを継承したコンバータの識別子
enum edk_IStreamConvertType{
	edkICS_null_object = 0,

	edkICS_compression_zlib_encode = 32,
	edkICS_compression_zlib_decode,
	edkICS_compression_lzss_encode,
	edkICS_compression_lzss_decode,
	edkICS_compression_lzw_encode,
	edkICS_compression_lzw_decode,
	edkICS_compression_runlength_packbits_encode,
	edkICS_compression_runlength_packbits_decode,

	edkICS_cryptograph_arcfour = 128,
	edkICS_cryptograph_hc256 = 129,
	edkICS_cryptograph_snow2,
	///独自のコンバーター識別子はこれの後に定義してね＾＾
	edkICS_end = USHRT_MAX * 2,
};

///std::runtime_errorからの派生
DKUTIL_DEFINE_DERIVE_STANDARD_RUNTIME_ERROR(convert_error);

struct generic_convert_first_header{
	uint32 mSignature;
	uint32 mOriginSize;
	uint32 mConvertedSize;
	void serialize(ISerializeStream *p){
		*p << mSignature;
		*p << mOriginSize;
		*p << mConvertedSize;
	}
	size_t serialize_size()const{
		return sizeof(uint32) * 3;
	}
	void init(){
		mSignature = mOriginSize = mConvertedSize = 0;	
	}
};
struct null_convert_second_header{
	void serialize(ISerializeStream *p){
	}
	size_t serialize_size()const{
		return 0;
	}
};

class IStreamCipherConvert{
public:
	BOOST_STATIC_CONSTANT(int,null_object = edkICS_null_object);
	virtual ~IStreamCipherConvert(){}
	virtual void convert(uint8 *buff,size_t size){}
	virtual int type(){return null_object;}
};

class CNullStreamCipherConvert : public IStreamCipherConvert{
	virtual void convert(uint8 *buff,size_t size){return;}

};


///分割して処理する時に使うインターフェイス factoryからこのインターフェイスで生成される
class IStreamConvert{
public:
	BOOST_STATIC_CONSTANT(int,null_object = edkICS_null_object);
	//BOOST_STATIC_CONSTANT(int,convert_succeeded = edk_SUCCEEDED);
	//BOOST_STATIC_CONSTANT(int,convert_failed = edk_FAILED);
	typedef generic_convert_first_header first_header;
	BOOST_STATIC_CONSTANT(size_t,default_inner_buffer_size = 1024 * 64);
	struct convert_result{
		size_t converted_size;
	};
	IStreamConvert(){}
	virtual ~IStreamConvert(){}
	virtual bool begin() = 0;
	virtual bool convert(uint8 *dest,size_t dsize,const uint8 *src,size_t ssize,
		convert_result *p) = 0;
	
		
		

	
	virtual bool convert(uint8 *ptr,size_t size,convert_result *p){
		size_t cs = getConvertOutputSize(size);
		scoped_buffer_byte sb(cs);
		if(false==sb.isValid()){
			return false;
		}
		bool r = convert(sb.get(),cs,ptr,size,p);
		if(p->converted_size > size)
			return false;

		memcpy(ptr,sb.get(),p->converted_size);
		return true;
	}
	

	bool convert(shared_buffer &x,const uint8 *src,size_t size,convert_result *r){
		size_t cs = getConvertOutputSize(size);
		DKUTIL_RETURN_FALSE(x.if_resize(size));
		DKUTIL_RETURN_FALSE(convert(x.get(),x.size(),src,size,r));
		return true;
	}
	bool convert(IStream *out,IStream *in,convert_result *p)
	{
		scoped_buffer_byte sb(mIBSize);
		if(false==sb.isValid()){
			return false;
		}
		for(;false==in->eof();)
		{
			in->read(sb.get(),sb.size());
			if(false==convert(sb.get(),sb.size(),p)){
				return false;
			}
			out->write(sb.get(),sb.size());
		}
		return true;
		//p->converted_size = 
	}
	virtual size_t getConvertOutputSize(size_t size)const = 0;
	virtual bool end(first_header *pfh) = 0;
	///@return SecondHeaderが存在しない場合はfalse
	virtual bool getSecondHeader(void *) = 0;
	///@return enum edk_Result
	//virtual int state()const = 0;
	virtual int type()const  = 0;
protected:
	void setInnerBufferSize(size_t size){
		mIBSize = size;
	}
	size_t getInnerBufferSize()const{
		return mIBSize;
	}
private:
	size_t mIBSize;
};

class CNullStreamConvert : public IStreamConvert{
public:
	CNullStreamConvert(){}
	virtual ~CNullStreamConvert(){}
	virtual bool begin(){return true;}
	virtual bool convert(uint8 *ptr,size_t size,convert_result *p){return true;}
	virtual bool convert(IStream *out,IStream *in,convert_result *p){return true;}
	virtual bool end(generic_convert_first_header *pfh){return true;}
	virtual size_t getConvertOutputSize(size_t size)const{return size;}
	///@return enum edk_Result
	//virtual int state()const{return edk_SUCCEEDED;}
	virtual int type()const{return IStreamConvert::null_object;}
};




template<class POLICY_T,class STREAM_T>
class convert_interface{
public:
	typedef typename POLICY_T policy_type;
	typedef typename STREAM_T stream_type;
	typedef typename generic_convert_first_header first_header;
	typedef typename policy_type::second_header second_header;
	convert_interface(){}
	~convert_interface(){}

	bool reset(policy_type *policy,stream_type *stream)
	{
		mP = policy;
		mS = stream;
		mStreamBegin = mS->tell();
		
		return true;
	}
	bool begin(){
		DKUTIL_RETURN_FALSE(mS->seek(headers_size()));
		DKUTIL_RETURN_FALSE(mP->begin());
		return true;
	}
	size_t headers_size()const{
		return mFH.serialize_size() + mSH.serialize_size();
	}
	bool convert(const void *buff,size_t size){
		size_t cs = mP->getConvertOutputSize(size);
		DKUTIL_RETURN_FALSE(mIB.if_resize(cs));
		IStreamConvert::convert_result r;
		DKUTIL_RETURN_FALSE(mP->convert(mIB.get(),cs,(const uint8 *)buff,size,&r));
		DKUTIL_RETURN_FALSE(mS->write(mIB.get(),r.converted_size));
		return true;
	}
	bool end()
	{
		DKUTIL_RETURN_FALSE(mP->end(&mFH));
		
		DKUTIL_RETURN_FALSE(mS->seek(mStreamBegin,seek_begin));
		mFH.serialize(mS);
		if(mP->getSecondHeader((void *)&mSH))//falseだったらsecond headerは0なので
		{
			mSH.serialize(mS);
		}
		return true;
	}

private:
	first_header mFH;
	second_header mSH;
	shared_buffer mIB;
	seek_arg_type mStreamBegin;
	policy_type *mP;
	stream_type *mS;
};

/*
class IEncoderStream : public IStreamConvert{
public:


};
*/

}//end of dkutil namespace



#endif