
#ifndef DKUTIL_CRYPTOGRAPH_BLOWFISH_HPP
#define DKUTIL_CRYPTOGRAPH_BLOWFISH_HPP

#include <dkutil_c/dkc.h>
#include <dkutil/memory/interface.hpp>

namespace dkutil{

/**
Blowfish暗号を行うクラス
type:ブロック暗号
*/
class CBlowFish : public IArrayProcess{

	size_t mEncodedSize;

	struct CBlowFishHeader{
		///シグネチャ
		ULONG sig;
		///オリジナルのサイズ
		size_t origin_size;
		///エンコード済みのサイズ
		size_t encoded_size;
	};
	DKC_BLOWFISH *mP;
	BOOST_STATIC_CONSTANT(int,header_size = sizeof(CBlowFishHeader));
	
	bool check_sig(CBlowFishHeader *p){
		return (p->sig == getSignature());
	}
	void check_obj__()const{
		if(NULL == mP){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CBlowfish uninitialize error"));
			dkcmFORCE_NOT_ASSERT(NULL != mP);
		}
	
	}
public:
	///@see reset()
	CBlowFish(const BYTE *key = NULL,size_t keysize = 0,ULONG signature=edk_BLOWFISH_SIGNATURE){
		mP = NULL;
		mEncodedSize = 0;
		reset(key,keysize);
		
	}
	void clear(){
		dkcFreeBlowfish(&mP);
	}
	
	~CBlowFish(){
		clear();
	}
	///@param signature[in] BlockSortで圧縮されていることを証明するID（デフォルトでは dkcdBlockSort_SIGNATURE )
	bool reset(const BYTE *key ,size_t keysize,ULONG signature=edk_BLOWFISH_SIGNATURE){
		setSignature(signature);
		setResult(edk_FAILED);
		
		if(NULL==key){
			return false;
		}

		clear();

		BYTE *kp = (BYTE *)malloc(keysize);
		if(NULL==kp){
			return false;
		}
		//キーをコピー
		memcpy(kp,key,keysize);
	

		//オブジェクト取得
		DKC_BLOWFISH *p = dkcAllocBlowfish(kp,keysize);
		bool r = false;
		if(NULL==p){
			goto Error;
		}
		
		mP = p;
		mEncodedSize = 0;
		r = true;
	Error:
		if(kp){free(kp);}
		return r;
	}

	int decode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		const BYTE *sp = src + header_size;
		size_t ssize = size - header_size;
		int r = edk_FAILED;
		//バッファの先頭にヘッダファイルがある。
		CBlowFishHeader *hp = (CBlowFishHeader*)src;
		check_obj__();

		dkcmNOT_ASSERT(check_sig(hp)==FALSE);
		if(check_sig(hp)==FALSE){
			return r;
		}
		if(getDecodeOutputSize(hp,size) > destsize){//destが足りない
			return r;
		}
		if(hp->encoded_size > size){//input bufferが足りない
			return r;
		}
		//くそー、メモリが〜
		scoped_buffer_byte buff(getDecodeOutputSize(hp,size));
		if(false==buff.good()){
			return r;
		}
		memcpy(buff.get(),sp,hp->encoded_size);


		dkcBlowfishDecrypt(
			mP,buff.get(),dest,buff.size()
		);

		r = edk_SUCCEEDED;
		//結果を入れる。
		setResult(r);

		return r;

	}

	int encode(const BYTE *src,size_t size,BYTE *dest,size_t destsize){
		BYTE *bp = dest + header_size;
		size_t bsize = destsize - header_size;
		DWORD outputsize;
		check_obj__();

		if(getEncodeOutputSize(size) > destsize){
			return edk_FAILED;
		}
		//input側の領域が侵されるのはイヤなのでコピーしておく
		memcpy(bp,src,size);


		//bsize分暗号化
		outputsize = dkcBlowfishEncrypt(mP,bp,bp,bsize);
		if(0==outputsize){
			return edk_FAILED;
		}

		//update
		CBlowFishHeader *hp = (CBlowFishHeader *)dest;
		hp->sig = getSignature();
		hp->origin_size = size;
		hp->encoded_size = outputsize;

		mEncodedSize = hp->encoded_size;
		setResult(edk_SUCCEEDED);
		
		return edk_SUCCEEDED;
	}	
	
	
	virtual size_t getEncodedSize()const{
		return mEncodedSize;
	}
	
	///エンコード時の出力予定のサイズ (普通より大きめの値を返されます。) 
	virtual size_t getEncodeOutputSize(size_t size)const{
		return dkcBlowfishGetOutputLength(size) + header_size;
	}
	/**
	@param pHeader[in] デコードする予定のデータ
	@param size[in] デコードする予定のサイズ
	@throw アクセス不可な所へアクセスしようとすると std::runtime_errorをぷっぱなす。
	*/
	///デコード時の出力予定のサイズ　
	virtual size_t getDecodeOutputSize(const void *pHeader,size_t size)const{
		if(size < header_size){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CBlowfish::getDecodeOutputSize() size argument exception"));
			return 0;
		}
		CBlowFishHeader *p = (CBlowFishHeader *)pHeader;
		//size_t s = p->origin_size;
		size_t s = p->encoded_size;
		return s;
	}
	virtual size_t getDecodeValidOutputSize(const void *pHeader,size_t size)const{
		if(size < header_size){
			DKUTIL_THROW_OR_NOTICE(std::runtime_error("CBlowfish::getDecodeOutputSize() size argument exception"));
			return 0;
		}
		CBlowFishHeader *p = (CBlowFishHeader *)pHeader;
		size_t s = p->origin_size;
		//size_t s = p->encoded_size;
		return s;
	}


};



}//end of dkutil namespace



#endif