
#ifndef DKUTIL_FILESYSTEM_INTERFACE_HPP
#define DKUTIL_FILESYSTEM_INTERFACE_HPP

#include <dkutil_c/dkc.h>
#include <dkutil/boost/parm_string.hpp>
#include <dkutil/macro.hpp>
#include <dkutil/detail/stream_interface.hpp>
#include <dkutil/detail/convert_serialize_interface.hpp>
#include <dkutil/detail/object_interface.hpp>
#include <dkutil/filesystem/utility.hpp>
#include <string>
//#include <stdexcept>
#include <cstddef>




namespace dkutil{

///filesystem系ライブラリのreset()のmodeに使う奴等　フラグ用定数群
enum edk_filesystem{
	///読み込みモード
	read_mode = edkcReadMode,
	///書き込みモード
	write_mode = edkcWriteMode,
	///追記モード
	postscript_mode = edkcPostScriptMode,
	///バイナリモード
	binary_mode = edkcBinaryMode,
	///テキストモード
	text_mode = edkcTextMode,
	///serializeする時 little endianで
	serialize_little_endian_mode = 256,
	///serializeする時 big endianで
	serialize_big_endian_mode = 512,
	///streamのタイプ(memoryのストリーム)
	memory_stream_type = 0,
	///streamのタイプ(fileのストリーム)
	file_stream_type = 1,

	

};
/* old
///filesystem系ライブラリのreset()のmodeに使う奴等　フラグ用定数群
enum edk_filesystem{
	///読み込みモード
	read_mode = 1,
	///書き込みモード
	write_mode = 2,
	///追記モード
	postscript_mode = 4,
	///バイナリモード
	binary_mode = 64,
	///テキストモード
	text_mode = 128,
	///serializeする時 little endianで
	serialize_little_endian_mode = 256,
	///serializeする時 big endianで
	serialize_big_endian_mode = 512,
	///streamのタイプ(memoryのストリーム)
	memory_stream_type = 0,
	///streamのタイプ(fileのストリーム)
	file_stream_type = 1,

	

};
*/

///file system系の機能がエラーした時に投げるもの
struct filesystem_error : public stream_error{
	 filesystem_error (const char *p) : stream_error(p){}
	 filesystem_error (const std::string &p) : stream_error(p){}

};
/*
struct filesystem_error : public std::runtime_error{
	 filesystem_error (const char *p) : std::runtime_error(p){}
	 filesystem_error (const std::string &p) : std::runtime_error(p){}

};*/

/*
struct filesystem_error{
	 filesystem_error (const char *p) {}
	 filesystem_error (const std::string &p){}

};
*/



/*
@note
- read(),write(),ref()での戻り値がfalseだったら、error()やeof()で状態を調べる事

*/
///ファイル用ストリーム
class IFileStream : public IMemoryStream /*, public ISerializeStream*/{
	UINT mOpenModeFlag;
protected:
	void setOpenModeFlag(UINT f){
		mOpenModeFlag = f;
	}
	int getOpenModeFlag()const{
		return mOpenModeFlag;
	}


public:

	typedef IFileStream self_type;
	typedef IStream base_type;
	
	typedef base_type::size_type size_type;
	typedef ULONGLONG size_type64;
	IFileStream(UINT flag){
		setOpenModeFlag(flag);
	}

	virtual ~IFileStream(){}
	///再初期化する。
	virtual bool reset(const char *filename,UINT Mode) = 0;
	///ファイルのサイズ
	virtual size_type64 filesize()const{
		size_type64 siz = 0;
		if(false==GetFileSize64(filename(),&siz)){
			DKUTIL_THROW_OR_NOTICE(filesystem_error("IFileStream::filesize() GetFileSize() Error"));
		}
		return siz;
	}
	///ファイル名取得
	virtual const char *filename()const = 0;
	///バッファをフラッシュ((ファイルに完全に書きこむ))する。
	virtual bool flush() = 0;

	virtual bool eof()const = 0;
	virtual bool error()const = 0;


	/*
	廃止：pure virtual function callになる事がある
	///clearのwrapper 
	inline void close(){
		clear();
	}
	*/


	bool is_read_mode()const{
		return 0 != (mOpenModeFlag & read_mode);
	}
	bool is_write_mode()const{
		return 0 != (mOpenModeFlag & write_mode);
	}
	bool is_binary_mode()const{
		return 0 != (mOpenModeFlag & binary_mode);
	}
	bool is_text_mode()const{
		return 0 != (mOpenModeFlag & text_mode);
	}


	bool is_postscript_mode()const{
		return 0 != (mOpenModeFlag & postscript_mode);
	}

	/*
	virtual ISerializeStream& operator << (char &c){
		serialize_helper(c,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (short &s){
		serialize_helper(s,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (int &n){
		serialize_helper(n,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (long &l){
		serialize_helper(l,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (LONGLONG &ll){
		serialize_helper(ll,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (BYTE &uc){
		serialize_helper(uc,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (USHORT &us){
		serialize_helper(us,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (UINT &n){
		serialize_helper(n,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (ULONG &n){
		serialize_helper(n,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (ULONGLONG &ll){
		serialize_helper(ll,default_read_functor(),default_write_functor());
		return *this;
	}
	virtual ISerializeStream& operator << (bool &b){
		serialize_helper(b,default_read_functor(),default_write_functor());
		return *this;
	}
	*/
};


namespace detail{

///flagをfopenで使うmodeに設定
inline bool flag_to_fopen_mode(char *fb,size_t size,UINT flag)
{
	//char fb[size]="";
	if(size < 4){//バッファが足りない･･･。
		return false;
	}
	bool rwflag = (flag & read_mode) && (flag & write_mode);
	bool psflag = ((flag & postscript_mode) != 0);

	if(rwflag && psflag){
		fb[0] = 'a';
		fb[1] = '+';
	}else if(rwflag){
		fb[0] = 'r';
		fb[1] = '+';
	}else if(flag & read_mode){
		fb[0] = 'r';
	}else if(flag & write_mode){
		fb[0] = 'w';
	}else if(flag & postscript_mode){
		fb[0] = 'a';
	}else{
		return false;
	}

	if(flag & binary_mode){
		strncat(fb,"b",size);
	}else if(flag & text_mode){
		strncat(fb,"t",size);
	}else{
		return false;
	}
	return true;
}


}//end of namespace detail




template<class POLICY>
class filestream_interface : public stream_interface<POLICY> , public IConvertSerializeStream{
public:
	//typedef POLICY base_type;
	typedef POLICY policy_type;
	typedef typename policy_type::size_type size_type;
	typedef typename policy_type::handle_type handle_type;

	typedef filestream_interface<POLICY> self_type;
	typedef stream_interface<POLICY> base_type;
	typedef IConvertSerializeStream serialize_type;
private:
	///ファイル名
	std::string mfilename;
	///モード
	ULONG mmode;
	///policy
	//policy_type mP;
	
protected:

	virtual void store(void *pd,size_t size){
		if(is_read_mode()){
			responsible_read(pd,size);
		}else{
			responsible_write(pd,size);
		}
	}
	//virtual void store8(uint8 &x) = 0;
	/*virtual void store16(uint16 &x){

	}
	virtual void store32(uint32 &x) = 0;
	virtual void store64(uint64 &x) = 0;
	*/
public:
	bool is_read_mode()const{
		return (mmode & read_mode) != 0;
	}
	bool is_write_mode()const{
		return !is_read_mode();
	}
	BOOST_STATIC_CONSTANT(int,invalid_handle = policy_type::invalid_handle);



	filestream_interface(parm_string filename,ULONG mode= read_mode | binary_mode) :
	serialize_type(0 != (mode & read_mode))
	{
		
		if(filename.empty()==false){
			mfilename = filename.c_str();
		}
		mmode = mode;
	}
	filestream_interface() :
	serialize_type(false)
	{
		mfilename.clear();
		mmode = 0;
	}

	filestream_interface(const self_type &c){
		
		mfilename = c.filename();//.c_str();
		mmode = c.mode();//.c_str();
		
		
	}

	~filestream_interface(){
		close();
	}
	static inline bool exist(parm_string str){
		return TRUE==dkcFileExist(str.c_str());
	}
	bool reset(parm_string filename,ULONG mode)
	{
		close();
		if(filename.empty())
		{
			//return false;
			DKUTIL_THROW_OR_NOTICE(std::invalid_argument("file_operator::reset() invalid argument"));
		}

		if(mode & read_mode){
			if(false==exist(filename.c_str())){
				return false;
			}
		}

		mfilename=filename.c_str();
		mmode = mode;

		bool isRead = 0 != (mmode & read_mode);
		bool isBig = 0 != (mode & serialize_big_endian_mode);
		bool isLittle = 0 != (mode & serialize_little_endian_mode);

		if(isBig && isLittle){
			DKUTIL_THROW_OR_NOTICE(std::invalid_argument("file_operator::reset() endian order flag with two"));
			return false;
		}
				
		if(isBig){
			serialize_type::reset(false,isRead);
		}else if(isLittle){
			serialize_type::reset(true,isRead);
		}else{//endian independent
			serialize_type::reset(isLittleEndian(),isRead);
		}

		return true;
	}
	const char *filename()const{
		return mfilename.c_str();
	}
	ULONG mode()const{
		//return mmode.c_str();
		return mmode;
	}
	bool open(){
		close();
		if(false==mP.open(mfilename.c_str(),mmode)){
			return false;
		}
		
		return true;
	}
	handle_type handle()const{
		return mP.handle();
	}
	bool flush(){
		return mP.flush();
	}
	uint64 filesize(){
		DWORD high,low;
		if(NULL==filename() )
		{
			DKUTIL_THROW_OR_NOTICE(filesystem_error(
				"filestream_interface::filesize() filename() null"
			));
			return 0;
		}
		if(FALSE==dkcFileSize64(filename(),&high,&low)){
			std::string s = "filestream_interface::filesize() not found ";
			s += filename();
			DKUTIL_THROW_OR_NOTICE(filesystem_error(s));
			return 0;
		}
		uint64 d;
		dkcTwoDWORDToULONGLONG(&d,high,low);
		return d;
	}
	/*
	size_type tell()const{
		return mP.tell();
		}

	size_type read(void *buf,size_type size){
		return mP.read(buf,size);
	}
	size_type write(const void *buf,size_type size){
		
		return mP.write(buf,size);
	}
	bool eof()const{
		return mP.eof();
	}
	bool error()const{
		
		return mP.error();
	}
	bool seek(long offset, int origin ){
		
		return mP.seek(offset,origin);
	}
	size_type size(){
		return mP.size();
	}
	*/
		

	
	void close(){
		mP.close();
	}
#if 0
	///指定したサイズ分を必ず読み込む
	void responsible_read(void *buf,size_t size){
		//sizeのテンポラリ
		size_t tsize = size;
		//読み込んだサイズのテンポラリ
		size_t t;

		while(1){
			t = read(buf,tsize);
			if(t == size){
				break;
			}
			if(eof()){
				throw filesystem_error("responsible_read eof");
			}
			if(error()){
				throw filesystem_error("responsible_read error");
			}
			tsize = tsize - t;
			buf = (char *)buf + t;
		}

		return;
	}
	///指定したサイズ分必ず書き込む
	void responsible_write(const void *buf,size_t size){

		size_t tsize = size;

		size_t t = 0;

		while(1){
			t = write(buf,tsize);
			if(t == size){
				break;
			}
			if(eof()){
				throw filesystem_error("responsible_write eof");
			}
			if(error()){
				throw filesystem_error("responsible_write error");
			}
			tsize = tsize - t;
			buf = (char *)buf + t;
		}
		return size;
	}
	class all_read_functor{
	private:
		BYTE *mp;
		//size_type msize;
		size_type mrest;
	public:
		all_read_functor(BYTE *ptr,size_type size){
			mp = ptr;
			mrest = size;
		}
		void operator()(const BYTE *buff,size_type size){
			if(mrest < size){
				DKUTIL_THROW_OR_NOTICE(std::out_of_range("all_read_functor buffer over flow"));
			}
			memcpy(mp,buff,size);
			mp += size;
			mrest -= size;
		}

	};

	///今の位置から最後まで読み込む
	void all_read(void *buf,size_type size,size_t innerbuffsize = 1024 * 512){
		all_read_functor func((BYTE *)buf,size);
		div_read_each(func,innerbuffsize);
	}


	/*!
	@param func_[in] ロードしたデータを処理する為のファンクター
	//@param ope[in] このファイルのメンバ関数(readかwrite)
	@param innerbuffsize[in] 内部で使われるバッファのサイズ
	*/
	template<class FUNCTOR_>
	bool div_read_each(FUNCTOR_ func_,size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		for(;false==eof();)
		{
			if(true==error() ){
				throw filesystem_error("ferror error");
			}
			size_t count = read(t.get(),t.size());
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());

			func_(t.get(),count);
		}
		return true;
	}
	template<class FUNCTOR_>
	bool div_write_each(FUNCTOR_ func_,size_t innerbuffsize = 1024 * 512)
	{
		scoped_buffer_byte t(innerbuffsize);
		for(;false==eof();)
		{
			if(true==error() ){
				throw filesystem_error("ferror error");
			}
			size_t count = write(t.get(),t.size());
			//ありえないエラーチェック
			dkcmNOT_ASSERT(count > t.size());

			func_(t.get(),count);
		}
		return true;
	}
#endif



};

}//end of dkutil namespace



#endif
