
#ifndef DKUTIL_MEMORY_INTERFACE_HPP
#define DKUTIL_MEMORY_INTERFACE_HPP

#include <dkutil/detail/stream_interface.hpp>
#include <dkutil/macro.hpp>
#include <dkutil/registration.hpp>
#include <dkutil/filesystem/interface.hpp>

namespace dkutil{



///メモリストリーム用インターフェイス
template<class POLICY>
class memorystream_interface : public stream_interface<POLICY>{
public:
	typedef POLICY policy_type;
	typedef typename policy_type::size_type size_type;
	typedef memorystream_interface<POLICY> self_type;
	typedef stream_interface<POLICY> base_type;

	memorystream_interface(){}
	~memorystream_interface(){}

	void reserve(size_type size){
		mP.reserve(size);
	}
	void resize(size_type size){
		mP.resize(size);
	}


};


///配列を弄繰り回すクラスのインターフェイス
class IArrayProcess{

private:
	///戻り値
	int mResult;
	///識別子
	ULONG mSig;

protected:
	
	void setResult(int r){
		mResult = r;
	}
	void setSignature(ULONG a){
		mSig = a;
	}
public:
	typedef IArrayProcess self_type ;

	
	virtual ~IArrayProcess(){}
	///複合化する。@return edkResultのどれか
	virtual int decode(const BYTE *src,size_t size,BYTE *dest,size_t destsize) = 0;
	///暗号化する。@return edkResultのどれか
	virtual int encode(const BYTE *src,size_t size,BYTE *dest,size_t destsize) = 0;

	//decodeしたらのサイズ
	//virtual size_t decoded_size(const BYTE *src,size_t size)const = 0;
	
	///エンコード後のサイズ
	virtual size_t getEncodedSize()const = 0;
	
	///エンコード時の出力予定のサイズ (普通より大きめの値を返されます。) 
	virtual size_t getEncodeOutputSize(size_t size)const = 0;
	/**
	@param pData[in] デコードする予定のデータ
	@param size[in] デコードする予定のサイズ
	@throw アクセス不可な所へアクセスしようとすると std::runtime_errorをぷっぱなす。
	*/
	///デコード時の出力予定のサイズ　
	virtual size_t getDecodeOutputSize(const void *pData,size_t size)const = 0;

	///デコード後の有効なデータのサイズを返す
	virtual size_t getDecodeValidOutputSize(const void *Data,size_t size)const = 0;
	///シグネチャ
	ULONG getSignature()const{
		return mSig;
	}

	///@return encode decodeの戻り値を返す
	int getResult()const{
		return mResult;
	}

	///encode または decodeが成功したらTRUE
	bool isSucceeded()const{
		return DKUTIL_SUCCEEDED_BOOL(mResult);
	}
	///@return バッファがオーバーフローしたようならTRUE
	bool is_bufferoverflow()const{
		return edk_BufferOverFlow==mResult;
	}
	///@return 圧縮する、暗号化する、等の価値が無かったらTRUE
	bool is_valueless()const{
		return edk_NoValueToProcess==mResult;
	}

	/*!
	@throw 致命的なエラーの場合、std::runtime_errorを投げる
	*/
	template<class BUFFER_T>
	bool encode(const BYTE *src,size_t srcsize,BUFFER_T &buff)
	{
		size_t size = getEncodeOutputSize(srcsize);
		//バッファの先頭にヘッダファイルを入れる。
		if(buff.size() < size){
			//buff.resize(size);
			buff.reserve(size);
		}
		int r = encode(src,srcsize,buff.get(),buff.size());

		return DKUTIL_SUCCEEDED_BOOL(r);
	}
	template<class BUFFER_T>
	bool decode(const BYTE *src,size_t srcsize,BUFFER_T &buff){
		int r = edk_FAILED;
		size_t size = getDecodeOutputSize(src,srcsize);
		/*if(0==size){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("
		}*/

		if(size > buff.size())
		{//バッファが足らなかったら。
			//リサイズ
			//buff.resize(size);
			buff.reserve(size);
		}		
		
		return DKUTIL_SUCCEEDED_BOOL(decode(src,srcsize,buff.get(),buff.size()));
	}
	/*
	int encode(const BYTE *src,size_t size,IStream *dest_s){
		if(dest_s->type() != 	memory_stream_type){
			return edk_ArgumentException;
		}
		return encode(src,size,(unsigned char *)dest_s->data(),dest_s->size());
	}

	int decode(const BYTE *src,size_t size,IStream *dest_s){
		if(dest_s->type() != memory_stream_type){
			return edk_ArgumentException;
		}
		return decode(src,size,(unsigned char *)dest_s->data(),dest_s->size());
	}
	*/
	int encode(const BYTE *src,size_t size,IMemoryStream *dest_s){
	
		return encode(src,size,(unsigned char *)dest_s->data(),dest_s->size());
	}

	int decode(const BYTE *src,size_t size,IMemoryStream *dest_s){
		
		return decode(src,size,(unsigned char *)dest_s->data(),dest_s->size());
	}
};

/**
@note
dkutilでの抽象クラスはPOD以外の型を使うのは禁じていたはずなのだが･･･
*/
///ArrayProcessでencode decodeしたデータを読み書きするインターフェイス
class IArrayProcessFileToMemory : public IArrayProcess{
public:
	typedef IArrayProcess base_type;
	typedef IArrayProcessFileToMemory self_type;

	virtual ~ IArrayProcessFileToMemory(){}

	//virtual size_t getDecodeOutputSize(IFileStream *ps) = 0;
	///encodeしたデータをファイルから読みこむ関数
	virtual bool read(IFileStream *,IStream *) = 0;

	/*
	@param ps[in][out] IFileStreamのインターフェイスを持ったオブジェクトへのポインタ
	@param src[in] データ
	@param srcsize[in] srcのデータ
	@return trueなら成功
	*/
	///@throw IFileStreamのwriteが失敗するとそれに準じたものが投げられる可能性がある。
	bool encode_write(IFileStream *ps,const BYTE *src,size_t srcsize)
	{
		size_t size = getEncodeOutputSize(srcsize);
		scoped_buffer_byte buff(size);

		encode(src,srcsize,buff.get(),buff.size());

		if(false==isSucceeded()){
			return false;
		}
		bool r = ps->responsible_write(buff.get(),size);
		return r;
	}

};


namespace detail{


///処理した道筋を返すためのインターフェイス
template<class T>
class IProcessedRoute : public T{
public:
	typedef IProcessedRoute<T> self_type;
	typedef T base_type;

	virtual ~IProcessedRoute(){}
	virtual int getProcessedRoute()const = 0;

};


}//end of detail namespace





/*class CProcessRegist : public registration<IProcessedRoute>{
};*/
/**
IArrayProcessをまとめるもの
@todo
encode decodeが同じだからまとめたいナァ。
*/
class CArrayProcessDiverge : 
	public detail::IProcessedRoute<IArrayProcess>
{
public:
	typedef CArrayProcessDiverge self_type;
	typedef detail::IProcessedRoute<IArrayProcess> base_type;

	//typedef typename policy::IArrayProcess interface_type;
	typedef registration<IArrayProcess> container_type;
	typedef container_type::priority_type priority_type;
	typedef container_type::element_sptr_type element_sptr_type;
	
	CArrayProcessDiverge(int signature){
		mSig = signature;
		mProcessedID = std::numeric_limits<priority_type>::digits;
	}
	virtual ~CArrayProcessDiverge(){}
	
	bool regist(priority_type prio,element_sptr_type el){
		return mC.regist(prio,el);
	}
	
	virtual int decode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		if(mC.empty()){
			return edk_LogicError;
		}
		container_type::iterator it = mC.begin();
		
		int result;
		for(;it != mC.end();it++){
			result = it->second->decode(src,size,dest,destsize);
			if(DKUTIL_FAILED(result))
			{
				continue;
			}
			mProcessedID = it->first;
			break;
		}
		return result;
	}
	///暗号化する。@return edkResultのどれか
	virtual int encode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		if(mC.empty()){
			return edk_LogicError;
		}
		container_type::iterator it = mC.begin();
		//priority_type id = 0;
		int result;
		for(;it != mC.end();it++){
			result = it->second->encode(src,size,dest,destsize);
			if(DKUTIL_FAILED(result))
			{
				continue;
			}
			mProcessedID = it->first;
			break;
		}
		return result;
	}
	//decodeしたらのサイズ
	//virtual size_t decoded_size(const BYTE *src,size_t size)const = 0;
	
	///エンコード後のサイズ
	virtual size_t getEncodedSize()const{
		return mC.find_by_key(mProcessedID)->getEncodedSize();
		//return mC.find(mProcessedID)->getEncodedSize();
	}
	
	///出力予定のサイズ
	virtual size_t getEncodeOutputSize(size_t size)const{
		return mC.find_by_key(mProcessedID)->getEncodeOutputSize(size);
		//return mC.find(mProcessedID)->getOutputSize();
	}

	///シグネチャ
	virtual int getSignature()const{
		return mSig;
	}
	virtual int getProcessedRoute()const{
		return mProcessedID;
	}
private:
	///signature保管庫
	int mSig;
	///処理したインターフェイスへのID
	priority_type mProcessedID;
	///インターフェイス登録所
	container_type mC;
};



}//end of dkutil namespace



#endif