
#ifndef DKUTIL_DETAIL_STREAM_INTERFACE_HPP
#define DKUTIL_DETAIL_STREAM_INTERFACE_HPP

#include <dkutil/define.hpp>
#include <dkutil/config.hpp>
#include <dkutil/scoped_buffer.hpp>
#include <dkutil/detail/serialize_interface.hpp>
#include <dkutil/detail/stream_functor.hpp>
#include <dkutil/boost/parm_string.hpp>

namespace dkutil{

enum{
	//シークオプション
	///先頭から
	seek_begin = SEEK_SET,
	///今の位置から
	seek_current = SEEK_CUR,
	///最後の位置から
	seek_end = SEEK_END,
};

///seek()のオフセット引数に使う変数
typedef LONGLONG seek_arg_type;


///streamの動作のインターフェース
struct IStreamAction{
	virtual bool action(uint8 *p,size_t size) = 0;
};

///ストリームの処理中の時に割り込み処理する時使う
struct stream_process_interrupt_arg{
	///処理する予定のすべてのサイズ
	uint64 mAllSize;
	///今までの処理し終わったサイズ
	uint64 mFinishedSize;
	///今処理しているストリームのデータのテンポラリバッファへのポインタ
	const void *mStreamData;
	///今処理し終わったサイズ
	size_t mNowFinishedSize;
	stream_process_interrupt_arg(uint64 allsize = 0,uint64 finished_size = 0,
		const void *sd = NULL,size_t now_finished_size = 0) : mAllSize(allsize),
		mFinishedSize(finished_size) , mStreamData(sd),
		mNowFinishedSize(now_finished_size)
	{
	}
};


///ストリーム処理中の時に割り込み処理する時に使うfunctorのbase

struct stream_process_interrupt_functor_base{
	/**
	@param arg[in] stream_process_interrupt_arg型の情報をが入っているポインタ
	@param user_ptr[in] 自分で処理を拡張する時に使うポインタ
	*/
	bool operator()(const stream_process_interrupt_arg *arg,void *user_ptr)
	{
		return true;
	}
};
/*
struct IStreamProcessInterrupt{
	virtual bool 

};
*/
template<class POLICY>
class stream_interface {
public:
	typedef POLICY policy_type;
	typedef typename policy_type::size_type size_type;
	typedef uint32 size_type32;
	typedef uint64 size_type64;
	typedef stream_interface<POLICY> self_type;

	stream_interface(){}
	~stream_interface(){}


	size_type read(void *buf,size_type size){
		return mP.read(buf,size);
	}
	
	
	size_type write(const void *buf,size_type size){
		
		return mP.write(buf,size);
	}
	///for string
	/*size_t write(const char *buf){
		return mP.write(buf,strlen(buf));
	}*/
	size_type write(parm_string str){
		return mP.write(str.c_str(),str.size());
	}

	//size_t write(
	size_type ref(void *buf,size_type size){
		return mP.ref(buf,size);
	}
	bool eof()const{
		return mP.eof();
	}
	bool error()const{
		
		return mP.error();
	}
#if 0
	bool seek(long offset, int origin ){
		return mP.seek(offset,origin);
	}
	
	bool seek(size_type offset){
		return mP.seek(offset);
	}
	
	size_type tell()const{
		return mP.tell();
	}
#else
	bool seek(seek_arg_type offset, int origin ){
		return mP.seek(offset,origin);
	}
	
	bool seek(seek_arg_type offset){
		return mP.seek(offset);
	}
	
	seek_arg_type tell()const{
		return mP.tell();
	}
	static uint64 limit(){
		return policy_type::limit_size;
	}
#endif
	size_type size(){
		return mP.size();
	}
	///指定したサイズ分を必ず読み込む
	void responsible_read(void *buf,size_t size){
		//sizeのテンポラリ
		size_t tsize = size;
		//読み込んだサイズのテンポラリ
		size_t t;
		//既に終わったサイズのテンポラリ
		size_t tf = 0;

		while(1){
			t = read(buf,tsize);
			tf += t;
			if(t == size){
				break;
			}else if(tf == size){
				break;
			}
			if(eof()){
				throw filesystem_error("responsible_read eof");
			}
			if(error()){
				throw filesystem_error("responsible_read error");
			}
			tsize = tsize - t;
			buf = (char *)buf + t;

		}

		return;
	}
	///指定したサイズ分必ず書き込む
	void responsible_write(const void *buf,size_t size){

		size_t tsize = size;

		size_t t = 0;

		size_t tf = 0;
		while(1){
			t = write(buf,tsize);
			tf += t;
			if(t == size){
				break;
			}else if(tf == size){
				break;
			}
			if(eof()){
				throw filesystem_error("responsible_write eof");
			}
			if(error()){
				throw filesystem_error("responsible_write error");
			}
			tsize = tsize - t;
			buf = (char *)buf + t;

		}
		//return size;
	}

	class all_read_functor{
	private:
		BYTE *mp;
		//size_type msize;
		size_type mrest;
	public:
		all_read_functor(BYTE *ptr,size_type size){
			mp = ptr;
			mrest = size;
		}
		void operator()(const BYTE *buff,size_type size){
			if(mrest < size){
				DKUTIL_THROW_OR_NOTICE(std::out_of_range("all_read_functor buffer over flow"));
			}
			memcpy(mp,buff,size);
			mp += size;
			mrest -= size;
		}

	};


	///今の位置から最後まで読み込む @return 実際読み込んだサイズ
	uint64 all_read(void *buf,size_type size,size_t innerbuffsize = 1024 * 512){
		all_read_functor func((BYTE *)buf,size);
		div_read_each_functor<self_type,all_read_functor> div_read_each;
		div_read_each(this,func,innerbuffsize);
		return div_read_each.get_readsize();
	}
	class text_read_functor{
	private:
		BYTE *mp;
		//size_type msize;
		size_type mrest;
		size_t mreadsize;
	public:
		text_read_functor(BYTE *ptr,size_type size){
			mp = ptr;
			mrest = size;
			mreadsize = 0;
		}
		void operator()(const BYTE *buff,size_type size){
			if(mrest < size){
				DKUTIL_THROW_OR_NOTICE(std::out_of_range("text_read_functor buffer over flow"));
			}
			memcpy(mp,buff,size);
			mp += size;
			mrest -= size;
			mreadsize += size;
		}
		size_t readsize()const{
			return mreadsize;
		}

	};

	///textを最後まで読み込む @return 読み込んだテキストのサイズ
	size_t text_all_read(void *buf,size_type size,size_t innerbuffsize = 1024 * 512){
		text_read_functor func((BYTE *)buf,size);
		div_read_each_functor<self_type,text_read_functor> div_read_each;
		div_read_each.referenced_functor(this,func,innerbuffsize);
		return func.readsize();
	}


protected:
	policy_type mP;
	policy_type &getPolicy()const{
		return mP;
	}
		
		
};



namespace policy{

class empty_stream_policy{
public:
	typedef size_t size_type;
	typedef void *handle_type;

	size_type tell()const{
		return 0;
	}

	size_type read(void *buf,size_type size){
		return 0;
	}
	size_type write(const void *buf,size_type size){
		
		return 0;
	}
	bool eof()const{
		return false;
	}
	bool error()const{
		
		return false;
	}
	bool seek(long offset, int origin ){
		return 0;
	}
	size_type size(){
		return 0;
	}
	void resize(size_type){}
	void reserve(size_type){}
};



}//end of policy

/**
<PRE>
OS 	コード表記 16進表記 10進表記 
UNIX 	<LF> 	    0A 	     10 
Windows <CR><LF>    0D 0A    13 10 
Macintosh <CR> 	    0D 	     13 
</PRE>
*/
template<class STREAM_T>
class stream_convert_line_feed_code_writer_win32{
public:
	STREAM_T::size_type operator()(STREAM_T *p,parm_string str){
		const char *a = str.c_str();
		size_t c=0;
		char crlf[3]={0x0D,0x0A,'\0'};
		while(*a != '\0')
		{
			if(*a == '\n')
			{
				c += p->write(crlf);
			}else{
				c += p->write(a,1);
			}
			a++;
		}
		return c;
	}

};


/**
ストリームがエラーした時にブン投げられるもの
*/

/*
class stream_error : public std::runtime_error{
public:
	stream_error(const char *str) : std::runtime_error(str){}
	stream_error(const std::string &str) : std::runtime_error(str){}
};
*/
DKUTIL_DEFINE_DERIVE_STANDARD_RUNTIME_ERROR(stream_error);


///一回しか書き込みが出来ないストリーム
class IOneTimeWriteStream{
public:
	virtual ~IOneTimeWriteStream(){}
	/*!
	@param buff[in] 書き込むデータへのポインタ
	@param size[in] buffのサイズ
	@return falseならエラー。（stream内部のバッファが足りない等)
	*/
	///書き込む
	virtual bool write(const void *buff,size_t size,size_t *write_size) = 0;
	
	virtual size_t write(const void *buff,size_t size){
		size_t writesize;
		if(false==write(buff,size,&writesize)){
			//DKUTIL_THROW_OR_NOTICE(stream_error("read false"));
		}
		return writesize;
	}
	///責任を持って書きこむ @see write() @return 責任を持って書き込めなかったらfalse
	inline bool responsible_write(const void *buff,size_t size){
		char *pBuffer = (char *)buff;
		size_t Rest = size;
		size_t write_ed;

		while ( Rest > 0 )
		{

			bool r = write( pBuffer, Rest, &write_ed );
			if(r==false){
				goto End;
			}

			// write_した分進める
			Rest -= write_ed;
			pBuffer += write_ed;
		}
	End:
		write_ed = (size - Rest);
		return write_ed == size;
	}

	///@return ストリームが使用可能ならtrueを返す
	virtual bool good()const = 0;


};

///一回しか読み込みが出来ないストリーム
class IOneTimeReadStream{
public:
	virtual ~IOneTimeReadStream(){}
		/*!
	@param buff[out] バッファー
	@param size[in] バッファのサイズ
	@param readsize[in] 実際に読み込んだサイズ
	@return falseならエラー（バッファが足りない 等）
	*/
	///読み込む
	virtual bool read(void *buff,size_t size,size_t *readsize) = 0;
	virtual size_t read(void *buff,size_t size){
		size_t readsize;
		if(false==read(buff,size,&readsize)){
			//DKUTIL_THROW_OR_NOTICE(stream_error("read false"));
		}
		return readsize;
	}
	

	///責任を持って読みこむ @see read()
	inline bool responsible_read(void *buff,size_t size)
	{
		char *pBuffer = (char *)buff;
		size_t Rest = size;
		size_t readed;
		

		while ( Rest > 0 )
		{

			bool r = read( pBuffer, Rest, &readed );
			if(r==false){
				goto End;
			}

			// readした分進める
			Rest -= readed;
			pBuffer += readed;
		}
	End:
		readed = (size - Rest);
		return readed == size;
	}


};
/**
超重大な欠陥がない限り、絶対にインターフェイスの実装を変えない。
（内部の関数の順番からなにからって事です。
@todo
まだ不完全なのでインターフェイスを変える可能性は多いにあり。
@note
仕様：
- Stream系クラスのインターフェイスとしての始祖である。

*/
class IStream : public IOneTimeReadStream,public IOneTimeWriteStream{
public:
	typedef IStream self_type;
	typedef size_t size_type;

	
	virtual ~IStream(){}

	/*!
	@param buff[out] バッファー
	@param size[in] バッファのサイズ
	@param readsize[in] 実際に読み込んだサイズ
	@return falseならエラー。
	@note
	read()との違い：
	ref()は内部のオフセットカウントを変更しない。
	*/
	///中身を見る。
	virtual bool ref(void *buff,size_t size,size_t *readsize)
	{
		seek_arg_type t = tell();
		bool r = read(buff,size,readsize);
		if(false==r) return r;
		seek(t,seek_begin);
		return r;
	}
	/*!
	@param offset[in] origin指定から何バイト、シークするか。
	@param origin[in] 
	@note
	動作はfseekと同じです。（爆）
	originも実はfseekと同じ指定子をぶち込んでも動いちゃったりします＾＾；
	*/
	///シークする。
	virtual bool seek(seek_arg_type offset,int origin) = 0;
	///今のバッファの最初からのオフセットを返す。
	virtual seek_arg_type tell()const = 0;

	///このストリームの書き込みリミットを取得する。limit() == tell()だったらeof()?
	virtual seek_arg_type limit()const = 0;
	virtual bool eof()const{
		return tell()>=limit();
	}
		///ストリームのタイプを取得
	virtual int type()const = 0;
	///メンバ関数テンプレートで汎用度UP!!？
	template<class T>
	void push_back(const T &x){
		size_type size = sizeof(x);
		BYTE *t = (BYTE *)&x;
		bool r = responsible_write(t,size);
		if(false==r){
			throw stream_error ("stream_interface::push_back()");//(DKUTIL_MAKESIGNATURE(T));
		}
	}
	template<class T>
	void push_back(const T *buf,size_type size){
		//const BYTE *t = (BYTE *)buf;
		bool r = responsible_write(buf,size);
		if(false==r){
			throw stream_error ("stream_interface::push_back()");//(DKUTIL_MAKESIGNATURE(T));
		}
	}
	///@return little endianだったらtrue
	static bool isLittleEndian(){
		return TRUE==dkcIsLittleEndian();
	}

	//stream_interface* operator=(const stream_interface *ptr){}


};


/*!
超重大な欠陥がない限り、絶対にインターフェイスの実装を変えない。
（内部の関数の順番からなにからって事です。
@todo
まだ不完全なのでインターフェイスを変える可能性は多いにあり。
@note
仕様：
- read write refは　ほぼ、必ず、指定したサイズ分を完了するまで動作する。
- 
*/
class IMemoryStream : public IStream{
public:
	typedef IStream base_type;
	//typedef base_type::seek_arg_type seek_arg_type;
	typedef base_type::size_type size_type;
	typedef IMemoryStream self_type;
	//typedef size_t size_type;
	typedef uint64 size_type64;
	typedef uint32 size_type32;


	
/*
	#ifdef BOOST_NO_INCLASS_MEMBER_INITIALIZATION
	enum{
		///バッファとして使う。
		buffer = edkcStreamInitBuffer,
		///ファイルとして使う。
		file = edkcStreamInitFile,
	};
#else
	const static int buffer = edkcStreamInitBuffer,
	const static int file = edkcStreamInitFile,
#endif
	*/
protected:

public:

	
	virtual ~IMemoryStream(){}

	/*!
	@note
	serialize_interfaceのポインタをぶち込めば、
	シリアライズしたり、デシリアライズしたりしてくれます。
	*/
	///シリアライズする。
	//virtual bool serialize(serialize_interface *) = 0;
	///内部バッファへのポインタを頂く（無効領域を含む。危険。
	///type()での戻り値がmemory_stream_typeの時のみに御使用ください。
	virtual const void *data()const = 0;
	///内部バッファのサイズ。
	virtual size_type size()const = 0;
	///内部バッファをリサイズする。
	virtual void resize(size_type size) = 0;
	///内部バッファをクリアする。
	virtual void clear() = 0;


	///size分シークします。例：/--*-/を/-*--/の方向にシークする感じ。一方方向。
	inline void pop_back(int size){
		if(false==seek(-abs(size),edkcStreamSeekCurrent)){
			throw stream_error("IMemoryStream::pop_back()");
			//DKUTIL_THROW_OR_NOTICE(std::runtime_error("IStream::pop_back()"));
		}
	}

	//stream_interface* operator=(const stream_interface *ptr){}


};


}//end of dkutil namespace



#endif