/**
@filename future.hpp
@brief 数十年後から数百年後に解凍されるかもしれないアーカイブを作成
@note
未来に向けてのタイムカプセル的な機能。
しかしならが、解凍される保証はまったくない。
決して大切なファイルをこの機構を使ってアーカイブ化してはならない。
量子コンピューターが今のコンピューター並に実用化されれば解凍できるかもしれない。
</PRE>
*/
#ifndef DKUTIL_ARCHIVE_FUTURE_HPP
#define DKUTIL_ARCHIVE_FUTURE_HPP

#include <dkutil/archive/interface.hpp>
#include <dkutil/archive/reserve.hpp>
#include <dkutil/filesystem/xml_save.hpp>
#include <dkutil/signature/signature_buffer.hpp>
#include <dkutil/signature/in_a_mass.hpp>
#include <dkutil/filesystem/dkcStream.hpp>

namespace dkutil{

	/**
	仕様：little endian base
	*/
	class CFutureArchiveGenerate : public CArchiveGenerateReserve{
	public:
		BOOST_STATIC_CONSTANT(uint32,signature = eda_Future);
		typedef CArchiveGenerateReserve base_type;
		typedef base_type::size_type size_type;
		typedef CFutureArchiveGenerate self_type;

		static uint32 getSignature(){
			return signature;
		}
		
		typedef CdkcStream fs_type;
		///データの並び構造
		struct data_list{
			std::string a_key;
			///ファイルのサイズ
			uint64 b_filesize;
			///データのMD5値
			signature_buffer_str<CMD5> c_md5;
			///データのSHA1値 以下同様に
			signature_buffer_str<CSHA1> d_sha160;
			signature_buffer_str<CSHA256> e_sha256;
			signature_buffer_str<CSHA384> f_sha384;
			signature_buffer_str<CSHA512> g_sha512;
			

			
			void serialize(ISerializeStream *p)
			{
				*p << a_key;
				*p << b_filesize;
				c_md5.serialize(p);
				d_sha160.serialize(p);
				e_sha256.serialize(p);
				f_sha384.serialize(p);
				g_sha512.serialize(p);

			}
		};
		struct to_signatures
		{
			CSignatureCalculateInAMass mC;
			to_signatures(){
				mC.regist(new CMD5);
				mC.regist(new CSHA1);
				mC.regist(new CSHA256);
				mC.regist(new CSHA384);
				mC.regist(new CSHA512);
			}
			~to_signatures(){
			}
		
			bool for_memory(const uint8 *buff,size_t size,data_list &dest)
			{
				return set_sig("\0",buff,size,dest,false);	
			}
			bool for_file(parm_string filename,data_list &dest){
				return set_sig(filename,NULL,0,dest,true);
			}
			bool set_sig(parm_string filename,const uint8 *buff,size_t size,
				data_list &dest,bool isFile)
			{
				
				typedef CSignatureCalculateInAMass::output_container ot;
				ot c;
				if(isFile){
					mC.fileToSignatures(filename,c,string_signature);
				}else{
					mC.memoryToSignatures(buff,size,c,string_signature);
				}

				ot::iterator it = c.begin();
				
					
				if(DKUTIL_FAILED(
					dkc_memcpy(dest.c_md5.get(),dest.c_md5.size(),(*it).get(),(*it).size())
					))
				{
					return false;
				}
				it++;
				if(DKUTIL_FAILED(
					dkc_memcpy(dest.d_sha160.get(),dest.d_sha160.size(),(*it).get(),(*it).size())
					))
				{
					return false;
				}
				it++;
				if(DKUTIL_FAILED(
					dkc_memcpy(dest.e_sha256.get(),dest.e_sha256.size(),(*it).get(),(*it).size())
					))
				{
					return false;
				}
				it++;
				if(DKUTIL_FAILED(
					dkc_memcpy(dest.f_sha384.get(),dest.f_sha384.size(),(*it).get(),(*it).size())
					))
				{
					return false;
				}
				it++;
				if(DKUTIL_FAILED(
					dkc_memcpy(dest.g_sha512.get(),dest.g_sha512.size(),(*it).get(),(*it).size())
					))
				{
					return false;
				}
				it++;
				dkcmASSERT(it == c.end());
				return true;
			}
		};
	protected:
		
		//typedef std::list<data_list> archive_list;
		void setLog(parm_string str){
			//mErrorLog += str.c_str();
			if(mLog){
				*mLog << str.c_str() << std::endl;
			}
		}
		//std::string mErrorLog;
		std::ostream *mLog;
		//std::ostream *mProcessLog;
		void write_xml_elem(xml_save &obj,const char *name,const char *value)
		{
			obj.save_start(name);
			obj.write_attribute("value",value);
			obj.save_end(name);
		}
		void write_xml_data(xml_save &obj,data_list &dl)
		{
			obj.write_attribute("key",dl.a_key.c_str());
			obj.write_attribute_uint64("filesize",dl.b_filesize);
			obj.indent();
				write_xml_elem(obj,"md5",dl.c_md5.get());
				write_xml_elem(obj,"sha1",dl.d_sha160.get());
				write_xml_elem(obj,"sha256",dl.e_sha256.get());
				write_xml_elem(obj,"sha384",dl.f_sha384.get());
				write_xml_elem(obj,"sha512",dl.g_sha512.get());
			/*obj.write_attribute("md5",dl.c_md5.get());
			obj.write_attribute("sha1",dl.d_sha160.get());
			obj.write_attribute("sha256",dl.e_sha256.get());
			obj.write_attribute("sha384",dl.f_sha384.get());
			obj.write_attribute("sha512",dl.g_sha512.get());
			*/
		}
	public:

		
		CFutureArchiveGenerate(std::ostream *p = NULL){
			//reset(filename);
			setOutputStream(p);
		}
		virtual ~CFutureArchiveGenerate(){
			clear();
		}
		void clear(){
			base_type::clear();
			mLog = NULL;
		}

		void setOutputStream(std::ostream *p){
			mLog = p;
		}


		/**
		固める処理
		*/
		virtual bool generate(const dKingyoArchiveGenerateOption &x)
		{

			fs_type FStream;//scope outを狙っている

			xml_save xml;
			if(NULL==x.process_callback)
			{
				setLog("Not Supprted process callback...\n");
			}
			if(false==FStream.reset(x.mFilename.c_str(),write_mode | text_mode))
			{
				return false;
			}
			std::string header_sig = "dkutil_archive_future";
			if(x.mDocType.size())
			{
				if(false==xml.reset(&FStream,header_sig,x.mDocType)){
					return false;
				}
			}else{
				if(false==xml.reset(&FStream,header_sig)){
					return false;
				}
			}
			//if(false==FStream.open())	return false;
			
			
			
			const char *save_sig= "dkutil_archive_future";
			xml.save_start(save_sig);
			xml.write_attribute_uint32("archive_signature",getSignature());


			{
				to_signatures fts;
				const char *filesig = "file";
				container_type::iterator it = mC.begin();
				for(;it != mC.end();it++)
				{
					xml.save_start(filesig);
					const dKingyoArchiveReserveElement &a = (*it);
					data_list dl;
					if(a.mIsFile)
					{
						if(false==FileExist(a.mFilename.c_str())){
							setLog(a.mFilename);
							setLog("が存在しません。\n");
							continue;
						}
						uint64 filesize;
						if(false==GetFileSize(a.mFilename,&filesize)){
							dMB("んなあほな");
							continue;
						}
						if(0==filesize){
							setLog(a.mFilename.c_str());
							setLog("はファイルサイズが0です。\n");
						}

						bool r = to_string_or_data_hex(dl.a_key,a.mKey.get(),a.mKey.size());
						if(false == r){
							setLog(a.mFilename);
							setLog("Keyを上手く文字列に変換できませんでした。\n");
							continue;
						}
						dl.b_filesize = filesize;
						
						
						if(false==fts.for_file(a.mFilename,dl))
						{
							setLog(a.mFilename);
							setLog("のシグネチャを取得できませんでした。\n");
							continue;
						}

						

					}else{//memory buffer
						
						bool r = to_string_or_data_hex(dl.a_key,a.mKey.get(),a.mKey.size());
						if(false == r){
							setLog(a.mFilename);
							setLog("Keyを上手く文字列に変換できませんでした。\n");
							continue;
						}
						
						dl.b_filesize = a.mBuffer.size();

						
						if(false==fts.for_memory(a.mBuffer.get(),a.mBuffer.size(),dl))
						{
							std::string str = toHexString(a.mKey.get(),a.mKey.size());
							setLog("memory : ");
							setLog(str);
							setLog(" / のシグネチャを取得できませんでした。\n");
							continue;
						}


						
					}//end of file or memory 
					write_xml_data(xml,dl);
					xml.save_end(filesig);

				}//end of registed element iterate
			}
			xml.save_end(save_sig);

			return true;

		}

		
		
	};

}//end of dkutil namespace



#endif