
#ifndef DKUTIL_DETAIL_CONVERT_SERIALIZE_INTERFACE_HPP
#define DKUTIL_DETAIL_CONVERT_SERIALIZE_INTERFACE_HPP


#include <dkutil/detail/serialize_interface.hpp>
#include <dkutil/detail/convert_interface.hpp>
namespace dkutil{


class IConvertSerializeStream : public ISerializeStream{
public:
	typedef IConvertSerializeStream self_type;
	typedef ISerializeStream base_type;
	typedef boost::shared_ptr<IStreamCipherConvert> convert_ptr;
	//typedef convert_interface<IStreamConvert,self_type> abstruct_convert_interface;
protected:
	//abstruct_convert_interface mPConvert;
	boost::shared_ptr<IStreamCipherConvert> mPConvert;
	size_t mSerializeWorkBufferSize;
public:
	ISerializeStream& operator << (char &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,1);
		}else{
			mPConvert->convert((uint8 *)&a,1);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (short &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,2);
		}else{
			mPConvert->convert((uint8 *)&a,2);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (int &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,4);
		}else{
			mPConvert->convert((uint8 *)&a,4);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (long &a){
#	ifdef WIN32
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,4);
		}else{
			mPConvert->convert((uint8 *)&a,4);
			base_type::operator<<(a);
		}
		return *this;
#	else
#		error unknown size...
#	endif
	}
	ISerializeStream& operator << (LONGLONG &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,8);
		}else{
			mPConvert->convert((uint8 *)&a,8);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (BYTE &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,1);
		}else{
			mPConvert->convert((uint8 *)&a,1);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (USHORT &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,2);
		}else{
			mPConvert->convert((uint8 *)&a,2);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (UINT &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,4);
		}else{
			mPConvert->convert((uint8 *)&a,4);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (ULONG &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,4);
		}else{
			mPConvert->convert((uint8 *)&a,4);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (ULONGLONG &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,8);
		}else{
			mPConvert->convert((uint8 *)&a,8);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (bool &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,1);
		}else{
			mPConvert->convert((uint8 *)&a,1);
			base_type::operator<<(a);
		}
		return *this;
	}
	//浮動小数点系列はsame size 問題があるので禁止！！ヽ(`Д´)ノムキィ
	//ISerializeStream& operator << (float &) = 0;
	
	ISerializeStream& operator << (double &a){
			
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,8);
		}else{
			mPConvert->convert((uint8 *)&a,8);
			base_type::operator<<(a);
		}
		return *this;
	}
	ISerializeStream& operator << (long double &a){
		
		if(is_read_mode()){
			base_type::operator<<(a);
			mPConvert->convert((uint8 *)&a,8);
		}else{
			mPConvert->convert((uint8 *)&a,8);
			base_type::operator<<(a);
		}
		return *this;
	}

	inline void serialize_string_inner(std::string &x,uint32 t)
	{
		if(mPConvert->type() != IStreamConvert::null_object)
		{
			scoped_buffer sb;
			sb.reset(mSerializeWorkBufferSize);
			size_t ln,restsize;

			if(is_read_mode()){
				ln = t / sb.size();
				restsize = t % sb.size();
			}else{
				ln = x.size() / sb.size();
				restsize = x.size() % sb.size();
			}
			size_t i,offset;
			offset = 0;
			for(i=0;i<ln;i++){
				if(is_read_mode())
				{
					serialize8((void *)sb.get(),sb.size());
					mPConvert->convert((uint8 *)sb.get(),sb.size());
					size_t j;
					for(j=0;j<sb.size();j++)
						x += sb[j];
					//offset += sb.size();
				}else{
					memcpy(sb.get(),x.data() + offset,sb.size());	
					mPConvert->convert((uint8 *)sb.get(),sb.size());
					serialize8((void *)sb.get(),sb.size());
					offset += sb.size();
				}
			}
			if(restsize){
				if(is_read_mode())
				{
					serialize8((void *)sb.get(),restsize);
					mPConvert->convert((uint8 *)sb.get(),restsize);
					size_t j;
					for(j=0;j<restsize;j++)
						x += sb[j];
					//memcpy(sb.get(),x.data() + offset,restsize);
				}else{
					memcpy(sb.get(),x.data() + offset,restsize);
					mPConvert->convert((uint8 *)sb.get(),sb.size());
					serialize8((void *)sb.get(),restsize);
				}
			}
		}else{
			serialize8((void *)x.data(),x.size());
		}

	}
	inline void serialize_string(std::string &x){
		//synchronized swimming;
		//scoped_buffer sb;
		uint32 t;
		
		if(is_read_mode()){
			//todo 分割読み込み synchronizedをはずす。
			*this << t;
			//mPConvert->convert((uint8 *)&t,sizeof(uint32));
			//sb.if_resize(t);
			x.reserve(t);
			if(0==t) throw serialize_error("IConvertSerializeStream serialize_string size == 0 error");
			serialize_string_inner(x,t);
			/*serialize8(sb.get(),t);
			mPConvert->convert((uint8 *)sb.get(),t);
			if(sb.get()[t - 1] != '\0'){
				size_t i;
				//for(i=0;i<=t;i++){	x += (char)sb[i];}
				for(i=0;i<t;i++){	x += (char)sb[i];}
			}else{
				x += sb.get();
			}*/
		}else{
			t = x.size();
			//t = x.size() + 1;
			//mPConvert->convert((uint8 *)&t,sizeof(uint32));
			*this << t;
			serialize_string_inner(x,t);
			
		
			
			//char y0[2]={'\0','\0'};
			//mPConvert->convert((uint8 *)y0,1);
			//serialize8((void *)y0,1);
		}
	}
		
	ISerializeStream& operator << (std::string &s){
		serialize_string(s);
		return *this;
	}
	void serialize(void *p,size_t size){
		if(is_read_mode())
		{
			base_type::serialize(p,size);
			mPConvert->convert(static_cast<uint8 *>(p),size);
		}else{
			mPConvert->convert(static_cast<uint8 *>(p),size);
			base_type::serialize(p,size);
		}
	}
#if WIN32
	///little endianにするならTRUE
	IConvertSerializeStream(bool isRead,bool f=true) : base_type(isRead,f){
		setSerializeWorkBufferSize(0);
	}
#else
	IConvertSerializeStream(bool isRead,bool f=false)  : base_type(isRead,f){
		setSerializeWorkBufferSize(0);
	}
#endif
	virtual ~IConvertSerializeStream(){}
	///IStreamConvertを継承したコンバータークラスを入れる
	void setConvertStream(convert_ptr p)
	{
		mPConvert = p;
	}
	bool reset(bool isLittle,bool isRead){
		mPConvert.reset( new CNullStreamCipherConvert );
		return base_type::reset(isLittle,isRead);
	}
	///serialize_string()のdeserializeが遅い時は搭載メモリとの兼ね合いも考えてサイズを大きく設定してください。
	///最近のPCだと1MB指定(1024 * 1024)しても大丈夫なはず
	void setSerializeWorkBufferSize(size_t size){
		if(0 == size){
			size = IStreamConvert::default_inner_buffer_size;
		}
		mSerializeWorkBufferSize = size;
	}
};

}//end of dkutil namespace



#endif