
/*!
@since 2003/10/16
*/


#ifndef _dkutilFileSystem__h_
#define _dkutilFileSystem__h_

#if defined(WIN32) 
#include "dkutilFSWin32Common.h"
#endif

#ifdef USE_DKINGYO_INCLUDE_SETTING_LIKE_BOOST
#include <dkutil/include/dkutilBoostPolicy.h>
#else
#	ifdef _MSC_VER
#		include "include/dkutilBoostPolicy.h"
#	else
#		include "dkutilBoostPolicy.h"
#	endif
#endif




namespace dkutil{
namespace policy{
	/// 読み出しモードで開く。ファイルがないか、見つからない場合は、エラー
	extern const char *f_readonly;
	/// 書き込みモードで空のファイルを開く。指定されたファイルが既にあると、そのファイルは消滅する。 
	extern const char *f_writeonly;
	///  EOF マーカーを削除せずに、ファイルに追記ができます。ファイルがない場合は作成します。
	extern const char *f_postscriptonly;
	/// 読みこみと書き込みの両方のモードで開く。 (対象は既存ファイル)
	extern const char *f_read;  
	///読みこみと書き込みの両方のモードで空のファイルを開く。指定されたファイルが既にあると、そのファイルは消滅する。
	extern const char *f_write;
  ///追記モードで読みこみと書き込みの両方のモードで開く。ファイルがない場合は作成します
  extern const char *f_postscript;
	///バイナリモードで開く。
	extern const char *f_binary;
	///テキストモードで開く。
	extern const char *f_text;
}//end of policy namespace

///fopen,fclose,fread,fwrite,fseek,ferror,feof 等の簡単なラッパー（でも強力！
class file_operator{
	std::string mfilename;
	std::string mmode;
	FILE *mfp;
	void check_fp()const{
		DK_TRUE_ASSERT_OR_THROW(
			!mfp,
			std::runtime_error("file_operator:openしてない。")
		);
	}
public:
	file_operator(const char *filename=NULL,
		const char *mode=policy::f_readonly,
		const char *type=policy::f_binary)
	{
		mfp=NULL;
		if(filename){mfilename=filename;}
		mmode = mode;
		mmode += type;
	}
	virtual ~file_operator(){close();}
	bool reset(const char *filename,const char *mode,const char *type){
		close();
		DK_TRUE_ASSERT_OR_THROW((filename==NULL || mode==NULL || type==NULL),
			std::invalid_argument("file_operator::resetinvalid argument"));

		mfilename=filename;
		mmode = mode;
		mmode += type;
		return true;
	}
	const char *filename()const{return mfilename.c_str();}
	const char *mode()const{return mmode.c_str();}
	bool open(){
		close();
		mfp=::fopen(mfilename.c_str(),mmode.c_str());
		if(mfp==NULL) return false;
		return true;
	}
	const FILE *handle()const{return 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);
	}
	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) );
	}
	
	void close(){
		if(mfp){::fclose(mfp);mfp=NULL;}
	}

};

template<
	class PATH=boost::filesystem::path,
	class DIR_IT=boost::filesystem::directory_iterator
>
class file_finder_base{
public:
	struct dir_data{
		dir_data(const char *getdir){
			dir = getdir;
		}
		dir_data(std::string getsdir){
			dir = getsdir;
		}
		std::string dir;
	};
	typedef std::deque<dir_data> QUEUE_TYPE;
	typedef std::list<std::string> CONTAINER_TYPE;
	typedef CONTAINER_TYPE::iterator iterator;
	typedef CONTAINER_TYPE::const_iterator const_iterator;
private:
	PATH mpath;
	DIR_IT mdirit;
	QUEUE_TYPE mq;
	CONTAINER_TYPE mc;


	void AllClear(){
		mq.clear();
		mc.clear();
	}

	void PushDirectory(QUEUE_TYPE &q,const char *dir)
	{
		namespace fs = boost::filesystem;
		DIR_IT end;
		PATH path(dir,boost::filesystem::native);
		for( DIR_IT it(path); it != end; ++it)
		{
			if( fs::is_directory(*it)){
				q.push_back(dir_data(it->native_directory_string()));
				PushDirectory(q,it->native_directory_string().c_str());
			}/*else{
				//dODS("native file string=%s",it->native_file_string());
			}*/
		}
	}
	void (*LOGIC_TYPE)(const char *,const char *,CONTAINER_TYPE);
	void SearchLogic_Boost(const char *name,const char *dir,CONTAINER_TYPE &c)
	{
		namespace fs = boost::filesystem;
		DIR_IT end;
		PATH path(dir,boost::filesystem::native);
		for( DIR_IT it(path); it != end; ++it)
		{
			if( fs::is_directory(*it)){
			}else{
				//dODS(it->native_file_string().c_str());
				//めっけ！
				if(name==it->native_file_string())
				{
					c.push_back(it->native_file_string());
				}
			}
		}
	}
	bool isFolder(WIN32_FIND_DATA &FindFileData){
		return (
			FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY
			&& strcmp(FindFileData.cFileName,"..")!=0 
			&& strcmp(FindFileData.cFileName,".")!=0
			);
	}
	int SearchLogic_WildCard(const char *wc,const char *dir,CONTAINER_TYPE &c)
	{
		int result=edk_FileNotFound;
		char path[MAX_PATH];
		char fullpath[MAX_PATH];
		{
			//ディレクトリのパス生成
			size_t len=strlen(dir);
			dkstrcpy_amap(path,sizeof(path),dir,len);
			if(path[len]!='\\'){
				char s[2]={'\\','\0'};
				dkstrcat_amap2(path,sizeof(path),s);
			}
			
			//フルパスを記憶しておく
			dkstrcpy_amap(fullpath,sizeof(fullpath),path,strlen(path));
			//検索パスを作成
			dkstrcat_amap2(path,sizeof(path),wc);
		}

		WIN32_FIND_DATA FindFileData;
		HANDLE hFind = FindFirstFile(path, &FindFileData);
		

		if(hFind != INVALID_HANDLE_VALUE){
			char buff[MAX_PATH];
			do{
				NULL_CHAR_ARRAY(buff);
				dkstrcpy_amap(buff,sizeof(buff),fullpath,strlen(fullpath));
				dkstrcat_amap2(buff,sizeof(buff),FindFileData.cFileName);
				c.push_back(buff);

				if(0==FindNextFile(hFind, &FindFileData)){
					break;
				}
			} while(true);
			FindClose(hFind);
			result = edk_SUCCEEDED;
		}
		else{
			//VISUAL_LASTERROR();
		}
		return result;
	}
	


public:
	file_finder_base(){}
	virtual ~file_finder_base(){}
	iterator begin(){return mc.begin();}
	iterator end(){return mc.end();}
	/*!
	@param wc[in] WildCardな文字列(例: "*.*" とか "*.txt"とか "?a.b"とか
	@param dir[in] Directoryな文字列　必ず最後は"\" (C言語上では'\\'で終わるようにしてください。
	@param receptacle[in][out] 結果を保存する入れ物の参照
	*/
	void FindWildCardLogic(const char *wc,const char *dir,CONTAINER_TYPE &receptacle){
		AllClear();
		PushDirectory(mq,dir);
		SearchLogic_WildCard(wc,dir,receptacle);
		QUEUE_TYPE::iterator it=mq.begin();
		for(;it != mq.end();it++)
		{
			SearchLogic_WildCard(wc,(*it).dir.c_str(),receptacle);
		}
	}
	/*!
	このクラスの内部の入れ物を使ってワイルドカードを使い検索する。
	これの結果を参照するにはこのクラスのbegin()やend()を使う
	@return falseなら一つも見つからない
	*/
	bool find_wc(const char *wc,const char *dir){
		FindWildCardLogic(wc,dir,mc);
		if(mc.empty())return false;
		return true;
	}
	/*!
	@param filename[in] 探すファイルの文字列(例: "file.txt" とか "file.bmp"とか "aho.cpp"とか
	@param dir[in] Directoryな文字列　必ず最後は"\" (C言語上では'\\'で終わるようにしてください。
	@param receptacle[in][out] 結果を保存する入れ物の参照
	*/
	void FindLogic(const char *filename,const char *dir,CONTAINER_TYPE &receptacle){
		AllClear();
		//ディレクトリをキューに詰める。
		PushDirectory(mq,dir);
		//まずは生ディレクトリから
		SearchLogic_Boost(filename,dir,receptacle);
		//次は検索したディレクトリから
		QUEUE_TYPE::iterator it=mq.begin();
		for(;it != mq.end();it++)
		{
			SearchLogic_Boost(filename,(*it).dir.c_str(),receptacle);
		}
	}
	/*!
	このクラスの内部の入れ物を使ってファイル名を指定して検索する。
	これの結果を参照するにはこのクラスのbegin()やend()を使う
	@return falseなら一つも見つからない
	*/
	bool find(const char *filename,const char *dir){
		FindLogic(filename,dir,mc);
		if(mc.empty()) return false;
		return true;
	}

	


};

typedef file_finder_base<> file_finder;

}//end of dkutil namespace








#endif //end of include once 
