
#ifndef DKUTIL_ARCHIVE_INTERFACE_HPP
#define DKUTIL_ARCHIVE_INTERFACE_HPP

//#include "header.h"

#include <dkutil/memory/interface.hpp>
#include <dkutil/filesystem/interface.hpp>
#include <dkutil/filesystem/path_string.hpp>
#include <dkutil/compression/interface.hpp>
#include <dkutil/cryptograph/interface.hpp>
#include <dkutil/smart_ptrs.hpp>
#include <dkutil/memory/circuler_stream.hpp>
#include <dkutil/progress.hpp>


namespace dkutil{

#define DKUTIL_ARCHIVE_DEFAULT_WORK_BUFFER_SIZE (1024 * 64) //64kb

	enum e_dKingyoArchiveHeaderSignatures
	{
		///ただアーカイブを作成するだけ　暗号化も圧縮もなし archive/basic.hpp
		eda_Basic = 1,
		///ヘッダのファイルリスト部を暗号化するアーカイブ作成 archive/pack.hpp
		eda_Pack = 2,
		//eda_PackCryptOnly,
		//eda_PackCompressOnly,
		
		///プロフェッショナルユースに答える？フラグ
		eda_Professional = 12,
		///無駄に処理に時間をかけて国家機密並？のアーカイブを作成するフラグ
		eda_SuperSecret = 13,
		
		///ゲームプログラミングに使えるゲーム用パック機構
		eda_PackForGame = 24,
		///ゲームプログラミング用パック機構圧縮タイプ
		eda_PackForGameCompress = 25,
		
		///未来に向けてアーカイブを作成 詳しくは archive/future.hpp
		eda_Future = 32,
		
		///この後に独自定義を書くこと
		eda_UserDefinitionBegin = USHRT_MAX,
		
		
	};
	struct dKingyoArchiveHeader{
		uint32 a_signature;
		uint32 b_version;
		void serialize(ISerializeStream *p){
			*p << a_signature;
			*p << b_version;
		}
	};
	/*enum dKingyoArchiveOption{
		eda_AllCryption
	}*/
	

	struct dKingyoArchiveKey : public shared_buffer
	{
		typedef shared_buffer base_type;
		typedef dKingyoArchiveKey self_type;
		dKingyoArchiveKey(parm_string x)
		{
			base_type::copy_string(x);
		}
		dKingyoArchiveKey(const void *pd,size_t size){
			base_type::copy((const uint8 *)pd,size);
		}
		dKingyoArchiveKey(){}
		friend bool operator<(const self_type &x,const self_type &y){
			if(x.size() < y.size()) return true;
			if(x.size() > y.size()) return false;
			int t = memcmp(x.get(),y.get(),x.size());
			return (t < 0);
		}
		friend bool operator==(const self_type &x,const self_type &y){
			if(x.size() == y.size()){
				 return 0==memcmp(x.get(),y.get(),x.size());
			}
			return false;
			
		}
	};
	enum extract_option{
		archive_extract_hashname,
		archive_extract_keyname,
		///キー名
		archive_extract_directory_keyname,
	};

	/*
	///暗号フラグ
	enum e_dKingyoArchiveGenerateCryptFlags{
		eda_CipherPlane = 0,
		eda_CipherARCFOUR = 4,
		eda_CipherRIJNDEAL = 8,

		eda_CipherBLOWFISH = 32,
		eda_CipherHC256 = 64,
		eda_CipherSNOW20 = 128,
	

	};
	///圧縮フラグ
	enum e_dKingyoArchiveGenerateCompressFlag{
		eda_CompressPlane = 0,
		eda_CompressLZSS = 1,
		eda_CompressLZW = 2,
		eda_CompressBLOCKSORTED_RLE = 16,
	}
	*/
	struct dKingyoArchiveOption{
		dKingyoArchiveOption(size_t wbs = DKUTIL_ARCHIVE_DEFAULT_WORK_BUFFER_SIZE) :
			workbuffer_size(wbs){}
		///ファイル名
		std::string mFilename;
		///ハッシュ値を生成するときのsalt
		shared_buffer mSalt;
		
		///一つの作業領域のバッファのサイズを指定（合計のサイズではない）
		size_t workbuffer_size;
	};
	struct dKingyoArchiveCallbackArg
	{
		///今処理している要素のサイズ（mCurrentSizeから
		size_t mCurrentSize;
		///今処理している要素の処理しきったサイズ（mCurrentBeginから
		size_t mCurrentOffset;
		///今処理している要素の最初のオフセット（Generate時は０）
		size_t mCurrentBegin;
		///今処理している要素のバッファ
		void *mCurrentWorkBuffer;
		///今処理している要素のバッファのサイズ
		size_t mCurrentWorkBufferSize;
		///今処理している要素のキー
		const void *mCurrentKey;
		size_t mCurrentKeySize;

		///今のアーカイブのサイズ
		size_t mArchiveSize;
		
		///今のアーカイブのオフセット
		size_t mArchiveOffset;

		///今処理しているアーカイブの名前
		const char *archive_name;
		/*int state()const{
			mCurrentWork
		*/
	};
	struct IdKingyoArchiveCallback{
		//progress_calculator 
		virtual bool callback(void *baseClass,dKingyoArchiveCallbackArg *peculiar) = 0;
	};
	///この構造体は変わる可能性あり
	struct dKingyoArchiveGenerateOption : public PasswordOption , public dKingyoArchiveOption{
		dKingyoArchiveGenerateOption(){
			mStreamProcessFlag = 0;
			workbuffer_size = 1024 * 64;
			process_callback = NULL;
		}
		/*///ファイル名
		std::string mFilename;
		///パスワード
		shared_buffer mPassword;
		///暗号化するときのinitialize vector
		shared_buffer mIV;*/

		///edk_ProcessSignaturesを組み合わせて入れる
		uint64 mStreamProcessFlag;
		///xmlのdoctypeを入れる
		std::string mDocType;
		/**
		各クラスに定義されているpeculiar_for_callback構造体が第一引数に入ります。
		*/
		///コールバック関数。
		IdKingyoArchiveCallback *process_callback;
		
	};


	struct dKingyoArchiveExtractOption : public PasswordOption , public dKingyoArchiveOption
	{
		dKingyoArchiveExtractOption(){
			//workbuffer_size = 1024 * 64;
		}
	};
	///登録用構造体
	struct dKingyoArchiveReserveElement{
		bool mIsFile;
		std::string mFilename;
		shared_buffer mBuffer;
		shared_buffer mKey;
		typedef boost::shared_ptr<IStreamConvert> convert_ptr;
		convert_ptr mConvert;
		dKingyoArchiveReserveElement(){}
		~dKingyoArchiveReserveElement(){
		}
		///暗号化とか圧縮のストリームを追加する。
		void setConvertStream(const convert_ptr p){
			mConvert = p;
		}
		void setKey(const shared_buffer &x){
			mKey = x;
		}

		void regist_filename(path_string_ref filename){
			shared_buffer buff;
			buff.copy_string(filename);
			regist_filename(filename,buff);
		}
		void regist_filename(path_string_ref filename,const shared_buffer &key){
			mFilename = filename;
			mKey = key;
			mIsFile = true;
		}
		void regist_buffer(const shared_buffer &buff,const shared_buffer &key){
			mBuffer = buff;
			mKey = key;
			mIsFile = false;
		}	
	};
	inline bool operator<(dKingyoArchiveReserveElement const & a, dKingyoArchiveReserveElement const & b) // never throws
	{
		return std::less<shared_buffer>()(a.mKey,b.mKey);
	}



	class IdKingyoArchiveGenerate{
	public:

		typedef dKingyoArchiveKey key_type;
		typedef dKingyoArchiveReserveElement reserve_type;
		typedef uint32 size_type;

		virtual ~IdKingyoArchiveGenerate(){}
		virtual bool addToArchive(path_string_ref filename) = 0;
		virtual bool addToArchive(const key_type &key,path_string_ref filename) = 0;
		virtual bool addToArchive(const key_type &key,const uint8 *pbuff,size_type size) = 0;
		virtual bool addToArchive(const key_type &key,const shared_buffer &x) = 0;
		virtual bool addToArchive(const reserve_type &x) = 0;
		inline bool addToArchive(const key_type& key,reserve_type::convert_ptr cpt,
			path_string_ref filename)
		{
			reserve_type r;
			r.mFilename = filename;
			r.mIsFile = true;
			r.mKey = key;
			r.mConvert = cpt;
			return addToArchive(r);			
		}
		virtual bool generate(const dKingyoArchiveGenerateOption &x) = 0;
	};
	

	struct dKingyoArchiveExtractElementOption{
		typedef boost::shared_ptr<IStreamConvert> convert_ptr;
		convert_ptr mConverter;
		dKingyoArchiveExtractElementOption(size_t ros = 1024 * 64,size_t wbs = 1024 * 64)
		{
			read_once_size = ros;
			work_buffer_size = wbs;
		}
		///1回のreadArchive()で読み込むサイズ
		size_t read_once_size;
		///内部バッファのサイズ read_once_sizeより大きい事
		size_t work_buffer_size;
	};

	///by archive/extract.hpp
	class CExtractStream;

	//typedef CCirculerMemoryStream CExtractStream;
	class IdKingyoArchiveExtract {
	public:
		typedef dKingyoArchiveKey key_type;
		typedef size_t size_type;
		typedef dKingyoArchiveExtractElementOption extract_option;
		virtual ~IdKingyoArchiveExtract(){}
		///@return 対象のアーカイブファイルらしかったらtrue
		virtual bool isArchive(path_string_ref archive_filename) = 0;
		virtual bool open(const dKingyoArchiveExtractOption &x) = 0;
		virtual bool find(const key_type &key) = 0;
		virtual bool extractToMemory(shared_buffer &dest,const key_type &key) = 0;
		virtual bool extractToFile(path_string_ref filename,const key_type &key) = 0;

		virtual CExtractStream *createExtractStream(
			const extract_option &option,const key_type &key) = 0;
		virtual bool readArchive(CExtractStream *p,size_t *readsize) = 0;
		virtual bool destroyExtractStream(CExtractStream *) = 0;

		virtual bool clear() = 0;
		//inline bool clear(){return close();}
	};


	///ハッシュ値を計算させると共にwrite()する。
	template<class STREAM_T,class HASH_T>
	class write_with_hash_calculate_functor{
	private:
		STREAM_T *mps;
		HASH_T *mSHA;
	public:
		typedef STREAM_T stream_type;
		typedef HASH_T hash_type;
		
		write_with_hash_calculate_functor(stream_type *ps,hash_type *p){
			mps = ps;
			mSHA = p;
			p->init();
		}
		void operator()(const uint8 *buff,size_t size)
		{
			mSHA->load(buff,size);
			mps->write(buff,size);
		}
	};
///ハッシュ値を計算させると共にread()する。
	template<class STREAM_T,class HASH_T>
	class read_with_hash_calculate_functor{
	private:
		STREAM_T *mps;
		HASH_T *mSHA;
	public:
		typedef STREAM_T stream_type;
		typedef HASH_T hash_type;
		
		read_with_hash_calculate_functor(stream_type *ps,hash_type *p){
			mps = ps;
			mSHA = p;
			p->init();
		}
		void operator()(const uint8 *buff,size_t size)
		{
			mSHA->load(buff,size);
			mps->read(buff,size);
		}
	};
}//end of dkutil namespace



#endif