
#ifndef DKUTIL_FILE_SYSTEM_STD_FILESTREAM_HPP
#define DKUTIL_FILE_SYSTEM_STD_FILESTREAM_HPP

#include <dkutil/filesystem/interface.hpp>
#include <dkutil/synchronized.hpp>
#include <dkutil/macro.hpp>
#include <cstdio>

namespace dkutil{

namespace policy{

class stdfile_operate_policy{
	FILE *mfp;
	void check_fp()const
	{
		if(NULL==mfp)
		{

			DKUTIL_THROW_OR_NOTICE(
				filesystem_error("file_operator:openしてない。")
			);
		}
	}
public:
	typedef std::size_t size_type;
	typedef FILE * handle_type;

	BOOST_STATIC_CONSTANT(int,invalid_handle = NULL);
	
	stdfile_operate_policy(){
		mfp = NULL;
	}
	
	~stdfile_operate_policy(){
		close();
	}

	inline bool open(const char *filename,ULONG flag)
	{
		//const int size = 4;
		char fb[4]="";
		/*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;
		}*/
		if(false==detail::flag_to_fopen_mode(fb,sizeof(fb),flag))
		{
			return false;
		}

		FILE *fp = fopen(filename,fb);
		if(NULL==fp)
		{
			return false;
		}

		mfp = fp;
		
		return true;
	}
	void close(){
		if(mfp){
			fclose(mfp);
			mfp = NULL;
		}
	}
	bool flush(){
		return 0 == fflush(mfp);
	}
	const FILE *handle()const{
		return mfp;
	}
	FILE *handle(){
		return mfp;
	}
	long tell()const{
		check_fp();
		return ftell(mfp);
	}
	/*size_t read(const void *buf,size_t size){
		check_fp();
		return fread(const_cast<void *>(buf),1,size,mfp);
	}
	size_t write(const void *buf,size_t size){
		check_fp();
		return fwrite(const_cast<void *>(buf),1,size,mfp);
	}*/
	size_t read(void *buf,size_t size){
		check_fp();
		return fread((buf),1,size,mfp);
	}
	size_t ref(void *buf,size_t size){
		synchronized swimming;
		check_fp();
		long t = 0;
		size_t rs;
		t = tell();
		rs = read(buf,size);
		if(false==seek(t,seek_begin)){
			DKUTIL_THROW_OR_NOTICE(filesystem_error("stdfile_operate_policy::ref() seek error"));
		}
		return rs;
	}
	size_t write(const void *buf,size_t size){
		check_fp();
		return fwrite((buf),1,size,mfp);
	}
	bool eof()const{
		check_fp();
		return ( 0 != feof(mfp) );
	}
	bool error()const{
		check_fp();
		return ( 0 != ferror(mfp) );
	}
	bool seek(long offset, int origin ){
		check_fp();
		return (0 == fseek(mfp,offset,origin) );
	}
	bool seek(size_type offset){
		if(offset > LONG_MAX){
			DKUTIL_THROW_OR_NOTICE(filesystem_error("stdfile_operate_policy::seek(size_type) offset argument value overflow"));
		}
		return seek(offset,seek_begin);
	}
	size_type size(){
		check_fp();
		//スレッドをロック
		synchronized swimming;
		//今の状態取得
		long t = tell();
		//最後に移動
		seek(0,SEEK_END);
		//最後の長さを取得
		long result = tell();
		//元に戻す
		seek(t,SEEK_SET);
		
		return (size_type)result;
	}


};



}//end of policy namespace

///standard file stream...
typedef filestream_interface<policy::stdfile_operate_policy> std_filestream;


}//end of dkutil namespace



#endif



