
/*!
@since 2004/03/17 2004/12/02
@author d金魚

*/


#ifndef DKUTIL_FILESYSTEM_CURRENT_DIR_HPP
#define DKUTIL_FILESYSTEM_CURRENT_DIR_HPP

#include <string>
//#include <dkutil/filesystem/utility.hpp>
#include <dkutil/macro.hpp>
#include <dkutil/synchronized.hpp>

namespace dkutil{


///@return std::stringを返すGetCurrentDirecory()
inline std::string SafeGetCurrentDirectory(){
	char buff[dkcdMAXPATH_BUFFER];
	if(FALSE==dkcGetCurrentDirectory(buff,sizeof(buff))){
		throw std::logic_error("SafeGetCurrentDirectory Error");
	}
	return std::string(buff);
}
/*!
@param s[in] ディレクトリの文字列が入ったstd::string
@param bForce[in] trueだと、ディレクトリが存在しなくても強制的にディレクトリを作る
@return trueは成功
*/
///デフォルトではディレクトリが無かったとき、強制的にディレクトリを作るように出来ている。
inline bool SafeSetCurrentDirectory(parm_string ps,bool bForce=true){
	std::string s = ps;
	std::string::iterator it = s.end() - 1;
	if(dkcmIS_PATH_SEP(*it) ){
		s.erase(it);
	}
	if(bForce==true){//強制的にDirectoryを作る
		if(false==FileAndFolderExist(s.c_str())){
			DKUTIL_RETURN_FALSE(CreateDirectory(s.c_str()));
		}
	}
	if(FALSE==dkcSetCurrentDirectory(s.c_str()))
	{
		return false;
	}
	return true;
}

/*!
カレントディレクトリを一時チェンジし、スコープアウトすると元に戻してくれます。
*/
///ディレクトリを一時チェンジするときに使います。
class scoped_current_directory{
	std::string mPreviousDir;
public:
	scoped_current_directory(const char *dir=NULL){
		try{
			if(dir){
				reset(dir);
			}
		}catch(...){
			undo();//戻してやる
			throw;//受け取ったものをまた投げてやる
		}
	}
	virtual ~scoped_current_directory(){
		undo();
	}
	///ディレクトリを変更する。
	bool reset(const char *dir,bool bForce=false)
	{
		synchronized lock;
		if(empty()==false){//すでに使用済みだったら一時戻す。
			undo();
		}

		if(bForce==true){//強制的にDirectoryを作る
			if(false==FileAndFolderExist(dir)){
				DKUTIL_RETURN_FALSE(CreateDirectory(dir));
			}
		}else{
			DKUTIL_RETURN_FALSE(FileAndFolderExist(dir));
		}
		mPreviousDir = SafeGetCurrentDirectory();
		if(FALSE==SetCurrentDirectory(dir))
		{//まぁ、エラーは0％に近いような気がするが･･･
			mPreviousDir.clear();
			return false;
		}
		return true;
	}
	///ディレクトリを元に戻す
	bool undo(){
		synchronized lock;
		if(mPreviousDir.empty()) return false;
		if(FALSE==SetCurrentDirectory(mPreviousDir.c_str()))
		{
			return false;
		}
		mPreviousDir.clear();
		return true;
	}
	bool empty()const{return mPreviousDir.empty();}
	const std::string &previous_directory()const{return mPreviousDir;}
	//const char* previous_directory()const{return mPreviousDir.c_str();}
};

/**
カレントディレクトリの状態を保存するクラス
保存しているカレントディレクトリの文字列を元に相対パスから絶対パスに変換する能力を持つ
*/
class current_directory_state{
	std::string mDir;
	current_directory_state(parm_string dir=NULL){
		if(dir.empty()==false){
			reset(dir);
		}
	}
	bool reset(parm_string dir)
	{
		if(dir.empty()){
			return false;
		}
		if(false==FolderExist(dir))
		{//フォルダではなかった。
			return false;
		}
		mDir = dir.c_str();
		return true;
	}
	std::string getCurrentDirectory(){
		return SafeGetCurrentDirectory();
	}
	void setNowCurrentDirectory(){
		if(false==reset(getCurrentDirectory())){
			DKUTIL_THROW_OR_NOTICE(
				std::runtime_error("current_directory_state::setNowCurrentDirectory() SetCurrentDirectory()とかで無効なパスを入力した可能性がある。")
			);
		}
	}

	std::string getDirectory()const{
		return mDir;
	}
	///内部に保存されているカレントディレクトリと引数の相対パスを元に絶対パスを返す。
	std::string createPath(parm_string relative_path){
		std::string s;
		if(false==createPath(s,relative_path)){
			DKUTIL_THROW_OR_NOTICE(
				std::runtime_error("current_directory_state::createPath() path generate error")
			);
		}
		return s;
	}
	/*
	@return falseで失敗、destの状態は元に戻らない
	@note destは関数内部でclear()される。
	*/
	bool createPath(std::string &dest,parm_string relative_path){
		dest.clear();
		dest += getDirectory();
		if(false==DirectoryPlusFileName(dest,relative_path)){
			return false;
		}
		return true;
	}
	
};

}//end of dkutil namespace








#endif //end of include once 
