
#ifndef DKUTIL_DETAIL_STREAM_FUNCTOR_HPP
#define DKUTIL_DETAIL_STREAM_FUNCTOR_HPP

#include <dkutil/macro.hpp>
#include <dkutil/config.hpp>
#include <dkutil/scoped_buffer.hpp>
#include <dkutil/detail/serialize_interface.hpp>
#include <stdio.h>

namespace dkutil{


/**
@param STREAM_T streamの型
@param FUNCTOR_ 以下のコードのような形
@code
class f{
	converter *mp;
public:
	f(converter *p) : mp(p){}
	void operator()(const unsigned char *p,size_t size){
		mp->convert(p,size);
	}
};
@endcode
*/
///分割読み込み
template<class STREAM_T,class FUNCTOR_>
struct div_read_each_functor{
	uint64 mReadSize;
	div_read_each_functor(){
		mReadSize = 0;
	}
	uint64 get_readsize()const{
		return mReadSize;
	}
	/**
	@param ps[in][out] streamオブジェクトへのポインタ
	@param func_[in] ロードしたデータを処理する為のファンクター
	//@param ope[in] このファイルのメンバ関数(readかwrite)
	@param innerbuffsize[in] 内部で使われるバッファのサイズ
	*/
	bool operator()(STREAM_T *ps,FUNCTOR_ func_,size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		for(;false==ps->eof();)
		{
			if(true==ps->error() ){
				throw filesystem_error("ferror error");
			}
			size_t count = ps->read(t.get(),t.size());
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());
			mReadSize += count;
			func_(t.get(),count);
		}
		return true;
		//return referenced_functor(ps,func_,innerbuffsize);
	}
	bool referenced_functor(STREAM_T *ps,FUNCTOR_ &func_,size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		for(;false==ps->eof();)
		{
			if(true==ps->error() ){
				throw filesystem_error("ferror error");
			}
			size_t count = ps->read(t.get(),t.size());
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());
			mReadSize += count;
			func_(t.get(),count);
		}
		return true;

	}
};

///分割書き込み
template<class STREAM_T,class FUNCTOR_>
struct div_write_each_functor{

	bool operator()(STREAM_T *ps,FUNCTOR_ func_,size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		for(;false==ps->eof();)
		{
			if(true==ps->error() ){
				throw filesystem_error("ferror error");
			}
			size_t count = ps->write(t.get(),t.size());
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());

			func_(t.get(),count);
		}
		return true;
	}
};

///リミット付き分割読み込み
template<class STREAM_T,class FUNCTOR_>
struct div_read_limit_functor{

	bool operator()(STREAM_T *ps,FUNCTOR_ func_,size_t limit_size,
		size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		size_t lc = 0;
		for(;false==ps->eof();)
		{
			if(true==ps->error() ){
				throw filesystem_error("ferror error");
			}
			
			size_t count;
			count = ps->read(t.get(),t.size());
			lc+=count;
			//なんかここのリミット比較条件がバグっているような・・・
			if(lc == limit_size){
				func_(t.get(),count);
				return true;
			}else if(lc > limit_size){//特にココ
				size_t readsize;
				if(t.size() > limit_size){//特にココ
					readsize = limit_size;
				}else{
					readsize = lc - limit_size;
				}
				func_(t.get(),readsize);
				return true;
			}else{
				
				func_(t.get(),count);
			}
			
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());

			
		}
		return true;
	}
};


template<class STREAM_T>
struct responsible_read_functor{

	///指定したサイズ分を必ず読み込む
	void operator()(STREAM_T *ps,void *buf,size_t size){
		//sizeのテンポラリ
		size_t tsize = size;
		//読み込んだサイズのテンポラリ
		size_t t;
		//既に終わったサイズのテンポラリ
		size_t tf = 0;

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

		}

		return;
	}
};


template<class STREAM_T>
struct responsible_write_functor{

	///指定したサイズ分必ず書き込む
	void operator()(STREAM_T *ps,const void *buf,size_t size){

		size_t tsize = size;

		size_t t = 0;

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

		}
		//return size;
	}
};



}//end of dkutil namespace



#endif