
#ifndef DKUTIL_ARCHIVE_STREAM_TO_CONVERT_BLOCK_HPP
#define DKUTIL_ARCHIVE_STREAM_TO_CONVERT_BLOCK_HPP

#include <dkutil/cryptograph/arcfour.hpp>
#include <dkutil/cryptograph/blowfish.hpp>
#include <dkutil/cryptograph/hc256.hpp>
#include <dkutil/cryptograph/snow2.hpp>
#include <dkutil/output/logger.hpp>
#include <dkutil/boost/shared_ptr.hpp>
#include <dkutil/filesystem/path_string.hpp>
#include <dkutil/detail/convert_interface.hpp>
#include <dkutil/define.hpp>

namespace dkutil{

/**
@param T cryptograph interface
@param FS_T file stream type
@param INTERRUPT_T stream interrupt functor type
*/
///ストリーム内に暗号化したファイルを埋め込みます（ヘッダ付き）
template<class T,class FS_T,class INTERRUPT_T>
class stream_to_convert_block_functor
{
public:
	typedef typename T policy_type;
	///file stream type
	typedef typename FS_T fs_type;
	typedef typename convert_interface<policy_type,fs_type> converter;
	typedef typename stream_to_convert_block_functor<T,FS_T,INTERRUPT_T> self_type;
	typedef typename stream_process_interrupt_functor_base null_interrupt_functor;
	typedef typename INTERRUPT_T interrupt_type;
protected:
	converter mC;
	//template<class SELF_T = self_type,class CONVERT_T = converter>
	class convert_functor{
		typedef self_type SELF_T;
		typedef converter CONVERT_T;
		CONVERT_T *mp;
		SELF_T *msp;
		interrupt_type mit;
		stream_process_interrupt_arg mArg;
		//fs_type *mpfs;
		void *mUserPtr;
	public:
		convert_functor(SELF_T *selfp,CONVERT_T *p,/*fs_type *pfs,*/interrupt_type it,
			uint64 allsize,void *user_ptr) : mArg(allsize)
		{
			mp = p;
			msp = selfp;
			mUserPtr = user_ptr;
			//mpfs = pfs;
			mit = it;
			/*if(pfs->is_read_mode()){
				DKUTIL_THROW_OR_NOTICE(std::runtime_error("convert_functor constructor not write_mode"));
			}*/
		}
		void operator()(const uint8 *p,size_t size){
			
			stream_process_interrupt_arg arg;
			
		
			mp->convert(p,size);
			mArg.mFinishedSize += size;
			//stack上で
			arg = mArg;
			arg.mNowFinishedSize = size;
			arg.mStreamData = (const void *)p;

			//interrupt
			mit(&arg,mUserPtr);
			//write
			//mpfs->write(p,size);
		}
	};
public:

	stream_to_convert_block_functor(){}
	~stream_to_convert_block_functor(){}

	/**
	@note
	policyは事前に使える状態にしていてください。
	fsは事前にwrite()出来る状態にしていてください。
	*/
	bool operator()(fs_type *dest,fs_type *src,size_t srcsize,
		policy_type *policy,
		void *user_ptr = NULL,
		interrupt_type it=interrupt_type(),
		size_t inner_buffer_size = DKUTIL_DEFAULT_INNER_BUFFER_SIZE)
	{
		converter c;
		if(dest->is_write_mode()==false){
			return false;
		}
		if(src->is_read_mode()==false){
			return false;
		}
		
		DKUTIL_RETURN_FALSE(c.reset(policy,dest));
		DKUTIL_RETURN_FALSE(c.begin());
		typedef div_read_each_functor<fs_type,convert_functor> ft;
		ft f;
		

		convert_functor cf(this,&c,it,srcsize,user_ptr);
		DKUTIL_RETURN_FALSE(f(src,cf,inner_buffer_size));

		DKUTIL_RETURN_FALSE(c.end());

		return true;

	}
	bool operator()(
		const PasswordOption &pass,uint32 signature,
		fs_type *dest,fs_type *src,size_t srcsize,
		policy_type *policy,
		void *user_ptr = NULL,
		interrupt_type it=interrupt_type(),
		size_t inner_buffer_size = DKUTIL_DEFAULT_INNER_BUFFER_SIZE)
	{
		policy_type p(pass,signature,inner_buffer_size);
		return operator()(dest,src,srcsize,policy,user_ptr,it,inner_buffer_size);
	}
};

/**
@param pass[in] パスワード
@param signature[in] 識別値（自由に設定してよし）
@param dest[in][out] ストリームオブジェクトへのポインタ
@param srcfile[in] ファイルへのパス
@param user_ptr[in] interrupt_で使うポインタ
@param interrupt_[in] 割り込み処理用ファンクター
@param inner_buffer_size[in] 内部のテンポラリバッファのサイズ
*/
template<class STREAM_T,class INTERRUPT_T>
inline bool ArcfourDecodeStreamConvertBlockToStream(
	const PasswordOption &pass,uint32 signature,
	STREAM_T *dest,STREAM_T *src,size_t srcsize,
	void *user_ptr = NULL,
	INTERRUPT_T interrupt_ = INTERRUPT_T(),
	size_t inner_buffer_size = DKUTIL_PROPER_TEMPORARY_BUFFER_SIZE())
{
	stream_to_convert_block_functor<CArcfourConvertStream,STREAM_T,INTERRUPT_T> a;
	DKUTIL_RETURN_FALSE(
		a(
			pass,signature,dest,src,srcsize,user_ptr,interrupt_,inner_buffer_size
		)
	);
	return true;
}



template<class STREAM_T,class INTERRUPT_T>
inline bool HC256DecodeStreamConvertBlockToStream(
	const PasswordOption &pass,uint32 signature,
	STREAM_T *dest,STREAM_T *src,size_t srcsize,
	void *user_ptr = NULL,
	INTERRUPT_T interrupt_ = INTERRUPT_T(),
	size_t inner_buffer_size = DKUTIL_PROPER_TEMPORARY_BUFFER_SIZE())
{
	stream_to_convert_block_functor<CHC256ConvertStream,STREAM_T,INTERRUPT_T> a;
	DKUTIL_RETURN_FALSE(
		a(
			pass,signature,dest,src,srcsize,user_ptr,interrupt_,inner_buffer_size
		)
	);
	return true;
}

/**
#define ArcfourDecodeFileToStream ArcfourEncodeFileToStream
template<class STREAM_T,class INTERRUPT_T>
inline bool ArcfourDecodeFileToStream(
	const PasswordOption &pass,uint32 signature,
	STREAM_T *dest,path_string_ref srcfile,
	void *user_ptr = NULL,
	INTERRUPT_T interrupt_ = INTERRUPT_T(),
	size_t inner_buffer_size = DKUTIL_PROPER_TEMPORARY_BUFFER_SIZE())
{
	
	stream_to_convert_block_functor<CArcfourConvertStream,STREAM_T,INTERRUPT_T> a;
	DKUTIL_RETURN_FALSE(
		a(
			pass,signature,dest,srcfile,user_ptr,interrupt_,inner_buffer_size
		)
	);
	return true;
}

template<class STREAM_T,class INTERRUPT_T>
inline bool HC256DecodeFileToFile(
	const PasswordOption &pass,
	path_string_ref destfile,path_string_ref srcfile,
	bool overwrite = false,std::ostream *pos = NULL,size_t inner_buffer_size = 1024 * 512)
{
	stream_to_convert_block_functor<CHC256DecodeStream> a;
	a.set_ostream(pos);
	return a.convert(pass,destfile,srcfile,overwrite,inner_buffer_size);
}
*/
}//end of dkutil namespace





#endif