
#ifndef DKUTIL_SCOPED_FILE_BUFFER_HPP
#define DKUTIL_SCOPED_FILE_BUFFER_HPP

#include <dkutil/boost/parm_string.hpp>


#include <dkutil/filesystem/std_filestream.hpp>
#include <dkutil/filesystem/utility.hpp>

namespace dkutil{

class scoped_fseek{
public:
	long offset;
	FILE *mfp;
	scoped_fseek(FILE *fp){
		mfp = NULL;
		offset = 0;
		if(fp){
			offset = ftell(fp);
			mfp = fp;
		}
	}
	~scoped_fseek(){
		if(mfp)
		{
			fseek(mfp,offset,SEEK_SET);
		}
	}
};

///このクラスは宣言後、isValidでもして確かめておくこと。４Gクラスのファイル読み込めないので^^;
template<class TYPE_T>
class scoped_file_buffer_base : public 
	scoped_buffer_base<policy::allocate_policy_malloc,TYPE_T>
{
protected:
	///ファイル自体のサイズ
	size_t mFileSize;
	///実際に読み込んだサイズ
	size_t mReadSize;
public:
	typedef scoped_file_buffer_base self_type;
	typedef scoped_buffer_base<policy::allocate_policy_malloc,TYPE_T> base_type;
	scoped_file_buffer_base(const char *filename = NULL){
		mFileSize = 0;
		mReadSize = 0;
		reset(filename);
	}
	~scoped_file_buffer_base(){
		clear();
	}
	size_t filesize()const{
		return mFileSize;
	}
	size_t readsize()const{
		return mReadSize;
	}
	void claer(){
		mFileSize = 0;
		mReadSize = 0;
		base_type::clear();
	}
	/**
	@param filename[in] ファイル名
	@param admit_size[in] 容認できるファイルサイズ(5MBくらいかな？)
	*/
	///@return 容認できるファイルサイズだったらtrue
	static bool isAdmitFileSize(parm_string filename,size_t admit_size)
	{
		DWORD high=0;
		DWORD low = 0;
		GetFileSizeHighLow(filename.c_str(),&high,&low);
		if(low > admit_size){
			return false;
		}
		return true;
	}

	/**
	@param filename[in] ファイル名
	@oaram binary[in] バイナリモードならtrue テキストモードなら false
	@param admit_size[in] 許容量を指定 0だと0xFFFFFFFFbyteまで許容する
	@return trueで成功
	*/
	///ファイルを読みこむ
	bool reset(parm_string filename,bool binary = true,size_t admit_size = 0){
		{
				if(filename.empty()){
				return false;
			}
			DWORD high=0;
			DWORD low = 0;
			GetFileSizeHighLow(filename.c_str(),&high,&low);
			if(0 != admit_size){
				if(false==isAdmitFileSize(filename,admit_size))
				{//許容量を超えた
					return false;
				}
			}
			//こんなものは読めない！
			if(high != 0){
				return false;
			}
			
			bool r;
			UINT flag = read_mode;
			
			

			if(binary){
				r = base_type::reset(low );
				flag |= binary_mode;
			}else{//text
				r = base_type::reset(low + 1);
				flag |= text_mode;
			}
			if(false==r) return false;

			std_filestream o(filename.c_str(),flag);
			
			if(false==o.open()){
				goto Error;
			}
			if(binary){
				uint64 t = o.all_read(get(),size());
				///絶対にありえない条件
				dkcmFORCE_NOT_ASSERT(t > UINT_MAX);
					
				mReadSize = (size_t)t;
			}else{
				//o.all_read(get(),low);
				size_t tts = o.text_all_read(get(),size());
				get()[tts] = '\0';
				mReadSize = tts;
			}

			/*
			if(size() != o.read(get(),size())){
				throw std::logic_error("scoped_file_buffer_base::reset() file read error!!");
			}*/

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

};

typedef scoped_file_buffer_base<BYTE> scoped_file_buffer;
typedef scoped_file_buffer_base<char> scoped_file_buffer_char;

struct scoped_textfile_buffer : public scoped_file_buffer_char{
	typedef scoped_file_buffer_char base_type;
	scoped_textfile_buffer(const char *filename = NULL){
		if(filename){reset(filename);}
	}
	bool reset(parm_string filename,size_t admit_size = 0){
		return base_type::reset(filename,false,admit_size);
	}
};
/*
///non thread safe
template<class T1>
inline std::string to_string(const scoped_file_buffer_base<T1> &x){
	typedef  scoped_file_buffer_base<T1> obj;
	
	return to_string((obj::base_type)x);

}
*/
}//end of dkutil namespace



#endif