/**
@filename dcf.hpp
@brief dKingyo Cryption File形式のファイルを操作する
@note
dKingyo Cryption File形式のファイルを操作する
dkutil_c/dkcDCF.hをC++にてWrapしたもの。
詳しくはdkcDCF.hの定義を見て欲しい。

*/
#ifndef DKUTIL_ARCHIVE_DCF_HPP
#define DKUTIL_ARCHIVE_DCF_HPP

#include <dkutil/dkutil_c.hpp>
#include <dkutil/cryptograph/snow2.hpp>
#include <dkutil/filesystem/dkcStream.hpp>
#include <dkutil/dktl/adapters.hpp>

namespace dkutil{

	class CdKingyoCryptionFile{
		DKC_DCF *mp;
		typedef sptr<CSNOW2ConvertStream> convert_ptr;
		convert_ptr msnow2;
		CdkcStream ms;
		void check_obj()const{
			dkcmNOT_ASSERT(NULL==mp);
		}
	public:
		CdKingyoCryptionFile(){
			mp = NULL;
		}
		~CdKingyoCryptionFile(){
			clear();
		}
		bool reset(path_string_ref filename,UINT flag,const CPasswordOption &x){
			clear();
			msnow2.reset(new CSNOW2ConvertStream(x));
			
			if(msnow2.isNull()) return false;
			if(false==msnow2->begin()) return false;
			if(false==ms.reset64(filename.c_str(),flag)) return false;

			mp = dkcAllocDCF_Version01_SNOW2(msnow2->getObj(),ms.getObj());
			if(!mp) return false;

			return true;
		}
		bool init(){
			check_obj();
			dkcDCFInit(mp);
		}
		bool clear(){
			msnow2.clear();
			ms.clear();
			return DKUTIL_SUCCEEDED_BOOL(dkcFreeDCF(&mp));
		}
		bool read(void *data,size_t size,size_t *readsize)
		{
			check_obj();
			int r = dkcDCFRead(mp,data,size,readsize);
			
			if(DKUTIL_FAILED(r) && (r != edk_EndProcess))
			{
				return false;
			}

			/*if(edk_EndProcess==r){
				r = dkcDCFReadFinal(p);
				if(DKUTIL_FAILED(r)) return false;
			}*/
			return true;
		}
		bool eof()const{
			check_obj();
			return ms.eof();
		}
		bool write(const void *data,size_t size,size_t *writesize){
			check_obj();
			return DKUTIL_SUCCEEDED_BOOL(dkcDCFWrite(mp,data,size,writesize));
		}
		bool final(){
			int r;
			check_obj();
			if(ms.is_read_mode()){
				r = dkcDCFReadFinal(mp);
			}else{
				r = dkcDCFWriteFinal(mp);
			}
			return DKUTIL_SUCCEEDED_BOOL(r);
		}
		bool all_read(void *data,size_t size){
			check_obj();
			int r = dkcDCFAllRead(mp,data,size);
			return DKUTIL_SUCCEEDED_BOOL(r);
		}

		static bool password_check(path_string_ref filename,const CPasswordOption &x){
			BOOL r;
			if(x.isPasswordBinary){
				r = dkcDCFPasswordCheck(filename.c_str(),x.mPasswordBin.get(),x.mPasswordBin.size());
			}else{
				r = dkcDCFPasswordCheck(filename.c_str(),x.mPassword.c_str(),x.mPassword.size());
			}
			return TRUE==r;
		}
		static bool get_filesize(path_string_ref filename,uint64 *pv){
			int r = dkcDCFFileSizeExpect(filename.c_str(),pv);
			return DKUTIL_SUCCEEDED_BOOL(r);
		}
		static bool get_filesize(path_string_ref filename,DWORD *high,DWORD *low)
		{
			uint64 v = 0;
			bool r = get_filesize(filename,&v);
			dkcULONGLONGToTwoDWORD(high,low,v);
			return r;
		}
		
		
	};

	inline bool save_dcf(const void *data,size_t size,path_string_ref filename,const CPasswordOption &x)
	{
		CdKingyoCryptionFile dcf;
		size_t ws;
		if(false==dcf.reset(filename,write_mode | binary_mode,x))
			return false;

		if(false==dcf.write(data,size,&ws))
			return false;
		if(ws != size)
			return false;

		if(false==dcf.final())
			return false;

		return true;
	}
	inline bool load_dcf(void *data,size_t size,path_string_ref filename,const CPasswordOption &x)
	{
		CdKingyoCryptionFile dcf;
		//size_t ws;
		if(false==dcf.reset(filename,read_mode | binary_mode,x))
			return false;

		if(false==dcf.all_read(data,size))
			return false;
		
		if(false==dcf.final())//無くても良い。
			return false;

		return true;
	}

	template<typename POD8=uint8>
	class scoped_dcf_buffer_base :	public scoped_file_buffer_base<POD8>
	{
	protected:
		CdKingyoCryptionFile mDCF;
	public:
		typedef typename scoped_file_buffer_base<POD8> base_type;
		typedef typename scoped_dcf_buffer_base<POD8> self_type;
		scoped_dcf_buffer_base(path_string_ref filename,const CPasswordOption &password,
			size_t admit_size = 0)
		{
			reset(filename,password,admit_size);
		}
		scoped_dcf_buffer_base(){}
		~scoped_dcf_buffer_base(){}
		/**
		@param filename[in] ファイル名
		@param password[in] パスワード
		@param admit_size[in] 許容量を指定 0だと0xFFFFFFFFbyteまで許容する
		@return trueで成功
		*/
		///ファイルを読みこむ
		bool reset(path_string_ref filename,const CPasswordOption &password,
			size_t admit_size = 0,bool isText=false)
		{
			{
				bool r;
				if(filename.empty()){
					return false;
				}
				DWORD high,low;
				r = mDCF.get_filesize(filename,&high,&low);
				DKUTIL_RETURN_FALSE(r);
				r = mDCF.reset(filename,read_mode,password);
				DKUTIL_RETURN_FALSE(r);

				if(0 != admit_size){
					if(low > admit_size)
					{//許容量を超えた
						return false;
					}
				}
				//こんなものは読めない！
				if(high != 0){
					return false;
				}
				if(isText){//'\0'用。
					r = base_type::base_type::reset(low+1);
				}else{
					r = base_type::base_type::reset(low);
				}
				DKUTIL_RETURN_FALSE(r);

				r = mDCF.all_read(get(),size());
				if(false==r) goto Error;

				if(isText){
					get()[low]='\0';
				}

				
				mFileSize = low;
			}
			
			return true;
		Error:
			clear();
			return false;
		}

	};
	typedef scoped_dcf_buffer_base<BYTE> scoped_dcf_buffer_byte;
	typedef scoped_dcf_buffer_base<char> scoped_dcf_buffer_char;
	class scoped_dcf_text_buffer : public scoped_dcf_buffer_char{
	public:
		typedef scoped_dcf_buffer_char base_type;
		typedef scoped_dcf_text_buffer self_type;
		scoped_dcf_text_buffer(){}
		bool reset(path_string_ref filename,const CPasswordOption &password,
			size_t admit_size = 0)
		{
			return base_type::reset(filename,password,admit_size,true);
		}

	};


}//end of dkutil namespace



#endif