
#ifndef DKUTIL_DETAIL_SERIALIZE_INTERFACE_HPP
#define DKUTIL_DETAIL_SERIALIZE_INTERFACE_HPP

#include <dkutil/macro.hpp>
#include <dkutil/config.hpp>
#include <dkutil/scoped_buffer.hpp>
#include <dkutil/synchronized.hpp>
#include <dkutil/dktl/adapters.hpp>
#include <dkutil/boost/shared_ptr.hpp>

#include <stdio.h>

namespace dkutil{


///serialize時にミスった時に投げられるもの
struct serialize_error : public std::runtime_error{
	serialize_error (const char *p) : std::runtime_error(p){}
	serialize_error (const std::string &p) : std::runtime_error(p){}
};
/**
@note
仕様：
- エンディアン系の指定ができる。
- WIN32系ではデフォルトではLittleEndian
- serialize系がsetConvertStream()で指定したコンバーターの影響を受ける
*/
class ISerializeStream{	
	bool mLittleEndianMode;
	bool mIsRead;
public:

	virtual void store(void *pd,size_t size) = 0;
protected:


	//virtual void store8(uint8 &x) = 0;
	inline void store16(uint16 &x){
		uint16 *p = &x;
		store(p,2);
	}
	virtual void store32(uint32 &x){
		uint32 *p = &x;
		store(p,4);
	}
	virtual void store64(uint64 &x){
		uint64 *p = &x;
		store(p,8);
	}
	
	
	
	/*
	@param pd[in][out] 書きこみデータへのポインタまたは読込先へのバッファへのポインタ
	書きこみになるか読みこみになるかはモードによって異なる。
	@param num[in] serialize8だと1byte単位 serialize16だと2byte単位 ... の pdの数
	@note 
	仕様：
	numにはpdにアクセスして良いbyte単位のサイズではなく、
	関数の名前の語尾についているbit数の個数を渡すようにする。
	*/
	
	/*
	virtual void serialize8(void *pd,size_t num) = 0;
	virtual void serialize16(void *,size_t num) = 0;
	virtual void serialize32(void *,size_t num) = 0;
	virtual void serialize64(void *,size_t num) = 0;
	*/

	
	void serialize_helper_exception(bool error){

		if(error){
			DKUTIL_THROW_OR_NOTICE(serialize_error("serialize :operator <<(...) error exception"));
		}else{
			DKUTIL_THROW_OR_NOTICE(serialize_error("serialize :operator <<(...) operation exception"));
		}
	}

#if 0
	/**
	serialize群は素直にtemplateして楽したいんだけど、
	エンディアン系で特殊化とかしなくちゃならんからがんばって分岐処理使いまくっている
	まぁ、sizeof(T)でswitch()分けしちゃってもいいんだけど・・・
	*/
	
	template<typename T>
	void serialize_helper(T &c)
	{
		
		register T t = c;//コピー
		bool r;//戻り値

		//メイン処理
		if(is_read_mode())
		{
					//エラー処理
			if(error()){
				serialize_helper_exception(true);
				return;
			}
			
			/*size_t readsize;
			//r = read(&t,sizeof(t),&readsize);
			r = read(&t,sizeof(t),&readsize);

			if(readsize != sizeof(t) && eof()==false){
				serialize_helper_exception(error());
				return;
			}*/
			responsible_read(&t,sizeof(t));

		}else{
			//r = write(&t,sizeof(t));
			responsible_write(&t,sizeof(t));
		}
		

		//Update
		c = t;
		return;
		
	}
#endif
	inline bool isSwap()const{
		return (ISerializeStream::isLittleEndian() != getLittleEndianMode());
	}
	virtual void serialize8(void *pd,size_t num){
		store(pd,num);
	}
	virtual void serialize16(void *pd,size_t num){
		
		bool swap = isSwap();
		USHORT us;
		bool is_r = is_read_mode();
		for(size_t i=0;i<num;i++){
			if(is_r){
				store16(us);
				USHORT *usp =(USHORT *)pd;
				if(swap){
					us = dkcmREVERSE_ENDIAN16(us);
					
				}
				usp[i] = us;
				
			}
			else
			{
				if(swap){
					us = (USHORT)*(USHORT *)pd + i;
					us = dkcmREVERSE_ENDIAN16(us);
				}else{
					us = (USHORT)*(USHORT *)pd + i;
				}
				store16(us);
			}
			
		}
		
		//change_endian_swap<USHORT> f;
		//f(pd,num,isSwap(),is_read_mode());
	}




	virtual void serialize32(void *pd,size_t num){
		
		bool swap = isSwap();
		uint32 us;
		bool is_r = is_read_mode();
		ULONG *ulp = (ULONG *)pd;
		for(size_t i=0;i<num;i++){
			if(is_r){
				store32(us);
				if(swap){
					us = dkcmREVERSE_ENDIAN32(us);
				}
				ulp[i] = us;
			}else{//write mode
				if(swap){
					us = (ULONG)*(ULONG *)pd + i;
					us = dkcmREVERSE_ENDIAN32(us);
				}else{
					us = (ULONG)*(ULONG *)pd + i;
				}
				store32(us);
			}
		}
		
		//change_endian_swap<ULONG> f;
		//f(pd,num,isSwap(),is_read_mode());
	}
	virtual void serialize64(void *pd,size_t num){
		
		bool swap = isSwap();
		ULONGLONG us;
		bool is_r = is_read_mode();
		ULONGLONG *ullp = (ULONGLONG *)pd;
		for(size_t i=0;i<num;i++){
			if(is_r){
				store64(us);
				if(swap){
					us = dkcmREVERSE_ENDIAN64( us );
				}
				ullp[i] = us;

			}else{//write_mode

				if(swap){
					us = (ULONGLONG)*(ULONGLONG *)pd + i;
					us = dkcmREVERSE_ENDIAN64( us );
				}else{
					us = (ULONGLONG)*(ULONGLONG *)pd + i;
				}
				store64(us);
			}
		}
	

	}

	
	//virtual ISerializeStream& serialize96(void *) = 0;
public:

#if WIN32
	///little endianにするならTRUE
	ISerializeStream(bool isRead,bool f=true){
		reset(f,isRead);
	}
#else
	ISerializeStream(bool isRead,bool f=false){
		reset(f,isRead);
	}
#endif
	virtual ~ISerializeStream(){}
	
	bool reset(bool isLittle,bool isRead){
		mLittleEndianMode = isLittle;
		mIsRead = isRead;
		return true;
	}

	bool is_read_mode()const{
		return mIsRead;
	}
	void serialize(void *p,size_t size){

		serialize8(p,size);
	}
	/*void setLittleEndianMode(bool flag){
		mLittleEndianMode = flag;
	}*/
	///@return true == little endian false == big endian
	bool getLittleEndianMode()const{
		return mLittleEndianMode;
	}
	bool getReadMode()const{
		return mIsRead;
	}
	static inline bool isLittleEndian(){
		return TRUE==dkcIsLittleEndian();
	}

	//virtual ISerializeStream& operator << (ISerializeStream &) = 0;
	ISerializeStream& operator << (char &c){
		
		serialize8(&c,1);
		return *this;
	}
	ISerializeStream& operator << (short &s){
	
		serialize16(&s,1);
		return *this;
	}
	ISerializeStream& operator << (int &n){
		
		serialize32(&n,1);
		return *this;
	}
	ISerializeStream& operator << (long &l){
#	ifdef WIN32
		
		serialize32(&l,1);
#	else
#		error unknown size...
#	endif
		return *this;
	}
	ISerializeStream& operator << (LONGLONG &ll){
		
		serialize64(&ll,1);
		return *this;
	}
	ISerializeStream& operator << (BYTE &uc){
	
		serialize8(&uc,1);
		return *this;
	}
	ISerializeStream& operator << (USHORT &s){
		
		serialize16(&s,1);
		return *this;
	}
	ISerializeStream& operator << (UINT &n){
		
		serialize32(&n,1);
		return *this;
	}
	ISerializeStream& operator << (ULONG &n){
	
		serialize32(&n,1);
		return *this;
	}
	ISerializeStream& operator << (ULONGLONG &ll){
	
		serialize64(&ll,1);
		return *this;
	}
	ISerializeStream& operator << (bool &b){
		
		serialize8(&b,1);
		return *this;
	}
	//浮動小数点系列はsame size 問題があるので禁止！！ヽ(`Д´)ノムキィ
	//ISerializeStream& operator << (float &) = 0;
	
	ISerializeStream& operator << (double &d){
		dkcmNOT_ASSERT(sizeof(double) != 8);
	
		serialize64(&d,1);
		return *this;
	}
	ISerializeStream& operator << (long double &a){
		dkcmNOT_ASSERT(sizeof(long double) != 8);

		serialize64(&a,1);
		return *this;
	}
	inline void serialize_string(std::string &x){
		synchronized swimming;
		static 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("ISerializeStream serialize_string size == 0 error");
			serialize8(sb.get(),t);
			
			if(sb.get()[t - 1] != '\0'){
				for(size_t 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;
			
			
			/*if(mPConvert->type() != IStreamConvert::null_object)
			{
				scoped_buffer sb;
				sb.reset(IStreamConvert::default_inner_buffer_size);
				size_t ln = x.size() / sb.size();
				size_t restsize = x.size() % sb.size();
				size_t i,offset;
				offset = 0;
				for(i=0;i<ln;i++){
					memcpy(sb.get(),x.data() + offset,sb.size());	
					serialize8((void *)sb.get(),sb.size());
					offset += sb.size();
				}
				if(restsize){
					memcpy(sb.get(),x.data() + offset,restsize);
					
					serialize8((void *)sb.get(),restsize);
				}
			}else{
				serialize8((void *)x.data(),x.size());
			}*/
			serialize8((void *)x.data(),x.size());
			//char y0[2]={'\0','\0'};
		
			//serialize8((void *)y0,1);
		}
	}

	ISerializeStream& operator << (std::string &s){
		serialize_string(s);
		return *this;
	}

	//ISerializeStream& serialize_float(
};


}//end of dkutil namespace



#endif