/**
@file spirit_ini_definition.hpp
@author d金魚
@since 2003/11/15 2004/12/04
@note
spirit使ってます。一部のコンパイラではエラルかもです。
*/

#ifndef DKUTIL_PARSER_INI_SPIRIT_DEFINITION_HPP
#define DKUTIL_PARSER_INI_SPIRIT_DEFINITION_HPP

//boost::spirit 1.6.1
#include <dkutil/boost/spirit/core.hpp>// 基本的な機能,
#include <dkutil/boost/spirit/iterator/file_iterator.hpp>

#include <dkutil/parser/general_purpose_data.hpp>
#include <dkutil/parser/string_util.hpp>
#include <dkutil/dktl/map_ex.hpp>
#include <string>

namespace dkutil{
namespace semantic_action{



/*!
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;
		StringToVariable conv;
		if(!mStr.empty()){
			//if(false==private_::ParsedStringToGeneralPurposeVariable(mStr.c_str(),&gp)){
			if(false==conv.StringToGeneralPurposeVariable(&gp,mStr.c_str(),mStr.size(),true)){
				DKUTIL_NOT_ASSERT("UNKNOWN ERROR");
			}
			if(gp.isString()){
				//ダブルクォーテーション削除
				if(*(gp.GetString().begin()) == '"'){
					gp.GetString().erase(gp.GetString().begin());
					if(*(gp.GetString().end() - 1) == '"'){
						gp.GetString().erase((gp.GetString().end() - 1));
					}
				}
			}
		}else{
			gp = mStr;
		}
		
		mData.second.Data = gp;
		if(false==mSectionData.second.rb_insert(mData)){
			DKUTIL_NOT_ASSERT("UNKNOWN ERROR");
		}
		
		
		if(false==mMap.rb_insert(mSectionData))
		{
			SECTION_MAP::iterator it = mMap.find(mSectionData.first);
			if(it == mMap.end()){
				DKUTIL_NOT_ASSERT("UNKNOWN ERROR");
			}
			if(false==(*it).second.isInserted(
				(*it).second.insert(mData)
				))
			{
				DKUTIL_NOT_ASSERT("キーが重複しているのか！？");
			}
		}
		//削除処理
		mSectionData.second.clear();
		mStr.clear();
  }
};


///日本語SJIS用パーさ。(一時的避難）
struct kanzi_parser : public boost::spirit::char_parser<kanzi_parser >
{
    typedef kanzi_parser self_t;
		/*template<typename CharT>
		struct traits{
			enum{
				value = sizeof(CharT),
			};

		};
		*/
    kanzi_parser() {}

    template <typename CharT>
    bool test(CharT ch) const
    { return (dkcIsSJIS1(ch) || dkcIsSJIS2(ch)); }
};

//space + yenn
struct space_and_enter : public boost::spirit::char_parser<space_and_enter >
{
    typedef kanzi_parser self_t;
		/*template<typename CharT>
		struct traits{
			enum{
				value = sizeof(CharT),
			};

		};
		*/
    space_and_enter() {}

    template <typename CharT>
    bool test(CharT ch) const
    { return dkcIsSpace(ch) || '\n'==ch }
};

};//end of ini_semantic_action 




}//end of semantic_action namespace


///INIファイルをパースする。
//template<class POLICY=semantic_action::ini_semantic_action >
class INIManager{
public:
	typedef semantic_action::ini_semantic_action policy_type ;


	typedef	boost::spirit::file_iterator<char> file_iterator;
	//typedef POLICY ;
	
	
	///boost::spirit ini parser
	struct ini_parser : public boost::spirit::grammar<ini_parser> {
		typedef policy_type 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,japanese_param_r;
			boost::spirit::rule<S> ini, section, key , comment,comment_or_space;

			definition(const ini_parser& self)
			{
				using namespace boost::spirit;
				semantic_action::ini_semantic_action::kanzi_parser kanzi_p;
				
				comment = ";" >> *(anychar_p - '\n') >> '\n' | +space_p;

				comment_or_space = comment;


				japanese_param_r = *(print_p[sa::ini_param_push(self.rparam,self.mMap)] | 
					kanzi_p[sa::ini_param_push(self.rparam,self.mMap)]) ;
				
				
				/*param_r = *(print_p[sa::ini_param_push(self.rparam,self.mMap)]) | 
					*(kanzi_p[sa::ini_param_push(self.rparam,self.mMap)]);
				*/
				param_r = japanese_param_r ;

				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 );
					comment_or_space >> *(key >> comment_or_space );

				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; }

		};

	};
	struct comment_parser : public boost::spirit::grammar<comment_parser> {

		template<typename S> struct definition {

			boost::spirit::rule<S> comment;

			definition(const comment_parser& self)
			{	using namespace boost::spirit;
				comment		=	";" >> *(anychar_p - '\n') >> '\n';
			}

			const boost::spirit::rule<S>& start() const { return comment; }
		};
	};
	typedef policy_type::const_section_iterator const_section_iterator;
	typedef policy_type::const_key_iterator const_key_iterator;
	typedef boost::spirit::parse_info<file_iterator> parse_result;
	typedef std::pair<const_key_iterator,bool> find_key_result;

private:
	
	std::string mFileName;

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

	policy_type::SECTION_DATA mSectionData;
	policy_type::KEY_DATA mKeyData;

	policy_type::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;
	}
	parse_result Parsing_with_comment(file_iterator &it){
		namespace bs = boost::spirit;
		ini_parser mINI(mMap,rsection,rkey,rparam,mSectionData,mKeyData);
		comment_parser com;
		file_iterator end_it = it.make_end();
		bs::parse_info<file_iterator> r = bs::parse(it,end_it,mINI,com);

		return r;
	}
	bool parse(const char *filename,parse_result *result_ptr=NULL){
		if(dkcFileExist(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;
		}
	}



public:
	INIManager(const char *filename=NULL){
		if(filename){
			mFileName = filename;
		}
	}
	virtual ~INIManager(){}
	bool reset(const char *filename,parse_result *result_ptr=NULL)
	{
		if(dkcFileExist(filename)==FALSE)
			return false;
		parse_result r;
		bool res;
		res = false;
		{
			
			mFileName = filename;
			mMap.clear();
			
			{
				mMap.clear();
				file_iterator f(filename);
				r = Parsing(f);
			}
			if(r.full){	// 入力された文字列が全て解析されていたらOK
				res = true;
				goto End;
			}

			{
				mMap.clear();
				file_iterator f(filename);
				r = Parsing_with_comment(f);
			}
			
			if(r.full){	// 入力された文字列が全て解析されていたらOK
				res = true;
				goto End;
			}else{	// 入力された文字列が全て解析されてないならエラーとみなす
				//r = false;
			}
		}
	End:
		if(result_ptr){
			*result_ptr = r; 
		}
		return res;
	}

	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);
	}


};//end of INIManager class


}//end of namespace


#endif
