
/*!
@auther d金魚
@since 2003/11/15
*/

#ifndef _dkutilINIManager__h_
#define _dkutilINIManager__h_

#include <dkutil/include/dkutilBoostPolicy.h>
#include "dKingyoMacro.h"
#include "dkutilDefined.h"
//#include "dKingyoUtilClass.h"
#include "dkutilMap.h"
#include "dkutilString.h"
#include "dkutilParser.h"
#include "dkutilOutput.h"
#include "dkutilFileSystem.h"
#include "dkutilOS.h"


namespace dkutil{
namespace policy{
/*!
only for boost::spirit
*/
class ini_semantic_action {
public:	
struct DATA_TYPE{
	//boost::any Any;
	//std::string Data;
	dkutil::GeneralPurposeVariable Data;
};
typedef dkutil::map_ex<std::string,DATA_TYPE> KEY_MAP;
typedef KEY_MAP::DATA_TYPE KEY_DATA;
typedef KEY_MAP::const_iterator const_key_iterator;
//struct SECTION_DATA{
//	KEY_MAP mMap;
//};
typedef dkutil::map_ex<std::string,KEY_MAP> SECTION_MAP;
//typedef SECTION_MAP::const_iterator const_iterator;
typedef SECTION_MAP::DATA_TYPE SECTION_DATA;
typedef SECTION_MAP::const_iterator const_section_iterator;

struct ini_semantic_action_base{
	std::string& mStr;
	SECTION_MAP& mMap;
	ini_semantic_action_base(std::string &get,SECTION_MAP &section_map) : 
		mStr(get) ,mMap(section_map) {}
	
};
struct ini_section_push : public ini_semantic_action_base{
	ini_section_push(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}


  void operator()(char c) const { // const が重要
		mStr += c;

  }
};
struct ini_key_push: public ini_semantic_action_base{
	ini_key_push(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}

	//template<class IteratorT>
  void operator()(char c) const { // const が重要
		mStr += c;

  }
};
struct ini_param_push: public ini_semantic_action_base{
	ini_param_push(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}


	//template<class IteratorT>
  /*void operator()(IteratorT first, IteratorT last) const { // const が重要
		mStr += (*first);

  }*/
	void operator()(char c) const { // const が重要
		mStr += c;
  }

};

struct ini_begin_section: public ini_semantic_action_base{
	ini_begin_section(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}
	
	//template<class IteratorT>
  /*void operator()(IteratorT first, IteratorT last) const { // const が重要
		mStr.clear();
		mStr += (*first);

  }*/
	void operator()(char c) const { // const が重要
		mStr.clear();
		mStr += c;

  }

};
struct ini_begin_key: public ini_semantic_action_base{
	ini_begin_key(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}
	template<class IteratorT>
  void operator()(IteratorT first, IteratorT last) const { // const が重要
		mStr.clear();
		mStr += (*first);

  }

};
struct ini_begin_param: public ini_semantic_action_base{
	ini_begin_param(std::string &get,SECTION_MAP &section_map) : 
		ini_semantic_action_base(get,section_map){}
	template<class IteratorT>
  void operator()(IteratorT first, IteratorT last) const { // const が重要
		mStr.clear();
		mStr += (*first);

  }

};

struct ini_section_set{
	SECTION_DATA &mData;
	std::string &mStr;
	SECTION_MAP &mMap;
	ini_section_set(std::string &Str,SECTION_DATA &getData,SECTION_MAP &section_map) : 
	mStr(Str) , mData(getData),mMap(section_map)	{}


	template<class IteratorT>
  void operator()(IteratorT first, IteratorT last) const { // const が重要
		mData.first = mStr;
  }
};
struct ini_key_set{
	KEY_DATA &mData;
	std::string &mStr;
	SECTION_MAP &mMap;
	ini_key_set(std::string &Str,KEY_DATA &getData,SECTION_MAP &section_map) : 
	mStr(Str) , mData(getData),mMap(section_map)	{}
	template<class IteratorT>
  void operator()(IteratorT first, IteratorT last) const { // const が重要
		mData.first = mStr;
  }
};

struct ini_param_set{
	KEY_DATA &mData;
	SECTION_DATA &mSectionData;
	std::string &mStr;
	SECTION_MAP &mMap;
	ini_param_set(std::string &Str,SECTION_DATA &getSection,KEY_DATA &getData,SECTION_MAP &section_map) : 
	mStr(Str) , mData(getData),mMap(section_map)	,mSectionData(getSection){}

	template<class IteratorT>
  void operator()(IteratorT first, IteratorT last) const { // const が重要
		dkutil::GeneralPurposeVariable gp;
		if(!mStr.empty()){
			if(false==private_::ParsedStringToGeneralPurposeVariable(mStr.c_str(),&gp)){
				DKUTIL_TRUE_ASSERT("UNKNOWN ERROR");
			}
			if(gp.isString()){
				//ダブルクォーテーション削除
				if(*(gp.mstr.begin()) == '"'){
					gp.mstr.erase(gp.mstr.begin());
					if(*(gp.mstr.end() - 1) == '"'){
						gp.mstr.erase((gp.mstr.end() - 1));
					}
				}
			}
		}else{
			gp = mStr;
		}
		
		mData.second.Data = gp;
		if(false==mSectionData.second.rb_insert(mData)){
			DKUTIL_TRUE_ASSERT("UNKNOWN ERROR");
		}
		
		
		if(false==mMap.rb_insert(mSectionData))
		{
			SECTION_MAP::iterator it = mMap.find(mSectionData.first);
			if(it == mMap.end()){
				DKUTIL_TRUE_ASSERT("UNKNOWN ERROR");
			}
			if(false==(*it).second.isInserted(
				(*it).second.insert(mData)
				))
			{
				DKUTIL_TRUE_ASSERT("キーが重複しているのか！？");
			}
		}
		//削除処理
		mSectionData.second.clear();
		mStr.clear();
  }
};

};//end of ini_semantic_action 


}//end of policy namespace


/*!
*/

//template<class POLICY=policy::ini_semantic_action >
class INIManager{
public:
	typedef policy::ini_semantic_action POLICY ;


	typedef	boost::spirit::file_iterator<char> file_iterator;
	typedef POLICY semantic_action;
	

	///boost::spirit ini parser
	struct ini_parser : public boost::spirit::grammar<ini_parser> {
		typedef semantic_action sa;

		std::string &rsection;
		std::string &rkey;
		std::string &rparam;
		//std::string SectionStr;
		//std::string KeyStr;
		//std::string ParamStr;
		sa::SECTION_DATA &mSectionData;
		sa::KEY_DATA &mKeyData;

		sa::SECTION_MAP &mMap;
		const sa::SECTION_MAP &database()const{return mMap;}
		sa::SECTION_MAP &database(){return mMap;}
		ini_parser(sa::SECTION_MAP &getMap,
			std::string &section,std::string &key,std::string &param,
			sa::SECTION_DATA &section_data,sa::KEY_DATA &key_data) : 
		mMap(getMap) , rsection(section) ,rkey(key) , rparam(param) ,
		mSectionData(section_data),mKeyData(key_data){}

		template<typename S> struct definition {
			
			boost::spirit::rule<S> key_name_r, param_r , section_name_r;
			boost::spirit::rule<S> ini, section, key;

			definition(const ini_parser& self)
			{
				using namespace boost::spirit;
				
				param_r = *(print_p[sa::ini_param_push(self.rparam,self.mMap)]);
				
				key_name_r		= (alpha_p[sa::ini_begin_section(self.rkey,self.mMap)] | 
					ch_p('_')[sa::ini_begin_section(self.rkey,self.mMap)]) >> 
					*( (alnum_p[sa::ini_section_push(self.rkey,self.mMap)] | 
						ch_p('_')[sa::ini_section_push(self.rkey,self.mMap)])
					);
				section_name_r = (alpha_p[sa::ini_begin_section(self.rsection,self.mMap)] | 
					ch_p('_')[sa::ini_begin_section(self.rsection,self.mMap)]) >> 
					*( (alnum_p[sa::ini_section_push(self.rsection,self.mMap)] | 
						ch_p('_')[sa::ini_section_push(self.rsection,self.mMap)])
					);
				
				section		= '[' >> section_name_r >> ']' >> 
					eps_p[sa::ini_section_set(self.rsection,self.mSectionData,self.mMap)] >>
					+space_p >> *(key >> +space_p);
				
				key			= key_name_r[sa::ini_key_set(self.rkey/**/,self.mKeyData,self.mMap)] >>
					*space_p >> '=' >> *space_p >>
					param_r[sa::ini_param_set(self.rparam/**/,self.mSectionData,self.mKeyData,self.mMap)];
				ini			= *space_p >> *section >> *space_p;
				/*
				param_r = *(print_p[&AckString]);
				name_r		= (alpha_p[&BeginName] | ch_p('_')[&BeginName]) >> 
					*( (alnum_p[&AckName] | ch_p('_')[&AckName]) ) >>
					eps_p[&EndName];
				section		= '[' >> name_r >> ']' >> 
					eps_p[&MOV_name_section] >>
					+space_p >> *(key >> +space_p);
				key			= name_r[&MOV_name_key] >> *space_p >> '=' >> *space_p >>
					param_r[&AddKeyString];
				ini			= *space_p >> *section >> *space_p;
				*/
			}

			// 開始記号を定義
			const boost::spirit::rule<S>& start() const { return ini; }

		};

	};
	typedef semantic_action::const_section_iterator const_section_iterator;
	typedef semantic_action::const_key_iterator const_key_iterator;
	typedef boost::spirit::parse_info<file_iterator> parse_result;
	typedef boost::compressed_pair<const_key_iterator,bool> find_key_result;
	//typedef semantic_action::
private:
	
	std::string mFileName;

	std::string rsection;
	std::string rkey;
	std::string rparam;

	semantic_action::SECTION_DATA mSectionData;
	semantic_action::KEY_DATA mKeyData;

	semantic_action::SECTION_MAP mMap;

	///今のstateの時の次の文字(c)をチェック。
	parse_result Parsing(file_iterator &it){
		namespace bs = boost::spirit;
		ini_parser mINI(mMap,rsection,rkey,rparam,mSectionData,mKeyData);
		file_iterator end_it = it.make_end();
		bs::parse_info<file_iterator> r = bs::parse(it,end_it,mINI);

		return r;
	}


public:
	INIManager(const char *filename=NULL){
		if(filename){
			mFileName = filename;
		}
	}
	virtual ~INIManager(){}
	bool reset(const char *filename,parse_result *result_ptr=NULL){
		if(FileExist(filename)==false) return false;
		mFileName = filename;
		mMap.clear();
		file_iterator f(filename);
		parse_result r = Parsing(f);
		if(result_ptr){
			*result_ptr = r; 
		}
		if(r.full){	// 入力された文字列が全て解析されていたらOK
			return true;
		}else{	// 入力された文字列が全て解析されてないならエラーとみなす
			//assert(false);
			return false;
		}
	}

	bool empty()const{	return mMap.empty();}
	const char *name()const{ return mFileName.c_str();}
	void clear(){mFileName.clear();mMap.clear();}
	const_section_iterator begin()const{return mMap.begin();}
	const_section_iterator end()const{return mMap.end();}
	const_section_iterator find_section(const char *section_name){
		return mMap.find(section_name);
	}
	/*
	find_key_result find_key(const char *section_name,const char *key_name){
		find_key_result result(;
		//result.first;
		const_section_iterator it = find_section(section_name);
		if(it == end()){ result.second = false;return result;}
		result.first = (*it).second.find(key_name);
		if(result.first == (*it).second.end()){
			result.second = false;
			return result;
		}
		result.second = true;
		return result;
	}
	*/
	const_key_iterator find_key_from_found_section(const_section_iterator it,
		const char *key_name)
	{
		if(it == mMap.end()) throw std::runtime_error("find_key_from_found_section invalid argument");
		return it->second.find(key_name);
	}


};






/*!
@note
<b>GetPrivateProfileSection</b><br>
Windows 95:<br>
セクションのサイズは32KBを超えてはいけません。<br>
Windows NT:<br>
セクションのサイズに制限はありません。<br>
*/
class inifile_operator{
//#	define ini_exception(s) std::runtime_error("ini_operator::" + s);

	std::string mFileName;
	///なんらかのエラーをチェックする。
	void CheckException(){
		if(mFileName.empty())
		{
			//あー、ファイル名が指定されてねぇなぁ
			throw std::runtime_error("ini_operator::... error / not defined filename");
		}
	}
	///Windows95はタブを扱えないため。
	bool search_tab(const char* str){
		char c=1;
		bool result=false;
		for(size_t i=0;c!=UINT_MAX || c=='\0';i++){
			
			if(str[i]=='\0' && '\0'==c){
				break;
			}else if(str[i]=='\t'){
				result = true;
				break;
			}
			c = str[i];
		}
		return result;
	}

		

public:
	inifile_operator(const char *filename){
		if(filename){
			reset(filename);
		}
	}
	virtual ~inifile_operator(){}
	bool reset(const char *filename){
		/*if(false==CheckWin9x(filename)){
			return false;
		}*/
		mFileName = filename;
	}
	/*!
	@param data[in] データの入れ方はMSDNでWritePrivateProfileSectionの第二引数の説明を見てください。
	@return dkutil::edkResult 
	成功：edk_SUCCEEDED / 失敗：edk_FAILED / edk_ArgumentException : 引数が合わない。
	*/
	int write_section(LPCTSTR section_name,LPCTSTR data){
		CheckException();
		if(isOS9x()){
			//MSDNには書いていないけど、信用できないのでいちおうチェック。
			if(true==search_tab(data)){
				return edk_ArgumentException;
			}
		}
		if(false==(0 != WritePrivateProfileSection(
			section_name,// セクション名へのポインタ
			data,// 書き込むデータへのポインタ
			mFileName.c_str()// ファイル名へのポインタ
			))){
			return edk_FAILED;
		}
		return edk_SUCCEEDED;
	}
	int write_string(LPCTSTR section_name,LPCTSTR key_name,LPCTSTR str){
		CheckException();
		if(isOS9x()){
			if(true==search_tab(str)){
				return edk_ArgumentException;
			}
		}
		if(0==WriteProfileString(
			section_name,// セクション名へのポインタ
			key_name,// キー名へのポインタ
			str// 書き込むデータへのポインタ
			)){
			return edk_FAILED;
		}
		return edk_SUCCEEDED;
	}




  ///ini ファイルからセクション全体を削除する
	bool delete_section( const char *SectionName )
	{
		CheckException();
		return ( 0 != WritePrivateProfileString( SectionName, NULL, NULL, mFileName.c_str() ));
	}
	///  ini ファイルからキーを削除する
	bool delete_key( const char *SectionName, const char *KeyName)
	{	
		CheckException();
		return (0 != WritePrivateProfileString( SectionName, KeyName,
			NULL, mFileName.c_str() ));
	}
	/// キーが ini ファイルに存在するかどうかを確かめる。
	bool exist( const char *SectionName, const char *KeyName )
	{
		CheckException(); ;
		// 指定したキーが見つからない場合、
		//関数 GetPrivateProfileInt() は１回目の呼び出しで０を、
		//２回目の呼び出しで１を返す。
		return (
			GetPrivateProfileInt(
				SectionName, KeyName, 0, mFileName.c_str() 
				) == 	GetPrivateProfileInt(
			SectionName, KeyName, 1, mFileName.c_str())
			);
	}
	/*!
	@param SectionName[in] セクション名へのポインタ
	@param Buffer[out] バッファへのポインタ
	@param Size[in] バッファのサイズ
	@param FileName[in] ファイルの名前
	@return 成功：edk_SUCCEEDED / バッファが足らない：edk_BufferOverFlow;
	Windows9x系だと、安全のためedk_FAILEDを返す。
	*/
	static int LoadSectionLogic(LPCTSTR SectionName,
						LPTSTR Buffer,DWORD Size,
						LPCTSTR FileName)
	{
		DWORD r;
		if(false==isOS9x()){ return edk_FAILED;}
		r = GetPrivateProfileSection(SectionName,Buffer,Size,FileName);
		if(r == Size - 2){	return edk_BufferOverFlow;}		
		return edk_SUCCEEDED;
	}
//#	undef ini_exception
};


}//end of dkutil namespace


#endif//end of include once

