/**
@note
この実装はakaxiso2 beta2のexpat_parserを元に作成しています。
かなり柔軟でない設計ですが勘弁。

*/
#ifndef DKUTIL_EXPAT_XML_PARSER_HPP
#define DKUTIL_EXPAT_XML_PARSER_HPP


//#include <dkutil/scoped_array_ex.hpp>


#include <dkutil/config.hpp>
#include <dkutil/macro.hpp>
#include <deque>
#include <dkutil/boost/format.hpp>
#include <dkutil/boost/parm_string.hpp>


#include <dkutil/scoped_buffer.hpp>
#include <dkutil/parser/general_purpose_data.hpp>

#include <dkutil/parser/char_util.hpp>
#include <dkutil/parser/string_printf.hpp>
#include <dkutil/parser/sjis_util.hpp>

#include <helper/expat_static_lib.h>

namespace dkutil{

	//template<class POLICY_T>	class expat_parser_interface;



	/**
	XML_が接頭辞に付くメンバ関数はEXPATの関数をラップしただけのもの
	付かないメンバ関数は独自のラッパー
	*/

	template<class POLICY_T>
	class expat_parser_interface{
		///これはポインターである。
		XML_Parser mP;
		POLICY_T mPolicy;
	public:
		typedef typename POLICY_T policy_type;
		//typedef typename POLICY_T base_type;
		typedef typename expat_parser_interface<POLICY_T> self_type;


		expat_parser_interface(const XML_Char *encoding=NULL){
			mP = NULL;
			reset(encoding);
		}
		XML_Parser getObj(){
			return mP;
		}
		policy_type &getPolicy(){
			return mPolicy;
		}
		~expat_parser_interface()
		{
			clear();
		}
		void clear(){
			if(mP){
				XML_ParserFree(mP);
				mP = NULL;
			}
		}
		
		bool reset(const XML_Char *encoding=NULL){
			if(!mP){
				mP = XML_ParserCreate(encoding);
				if(!mP) return false;
			}
			XML_Bool b = XML_ParserReset(mP,encoding);
			if(FALSE==b){
				return false;
			}
			XML_SetUserData(mP, this);
			XML_SetElementHandler(mP, policy_type::startElement, policy_type::endElement);
			XML_SetCharacterDataHandler(mP,policy_type::characters);
			XML_SetCommentHandler(mP,policy_type::comment);
			//XML_SetDefaultHandler(mP,policy_type::characters);
			return true;
		}
		bool isValid()const{
			return NULL != mP;
		}

		std::string parse_error_message()const{
			std::string s=XML_ErrorString(XML_GetErrorCode());
			s+=" at line ";
			s+=to_string_int32(XML_GetCurrentLineNumber());
			return s;
		}
	      


		//以下expatの関数をそのままラップしたもの

		enum XML_Status XML_Parse(const char *s,
          int len,
          int isFinal)
		{
			dkcmNOT_ASSERT(NULL==mP);
			return ::XML_Parse(mP,s,len,isFinal);
		}

		int XML_GetCurrentLineNumber()const{
			dkcmNOT_ASSERT(NULL==mP);
			return ::XML_GetCurrentLineNumber(mP);
		}

		const XML_LChar *XML_ErrorString(enum XML_Error code)const{
			return ::XML_ErrorString(code);
		}

		enum XML_Error XML_GetErrorCode()const{
			dkcmNOT_ASSERT(NULL==mP);
			return ::XML_GetErrorCode(mP);
		}
		enum XML_Status	XML_StopParser(XML_Bool resumable)
		{
			dkcmNOT_ASSERT(NULL==mP);
			return ::XML_StopParser(mP,resumable);
		}
		enum XML_Status XML_ResumeParser()
		{
			dkcmNOT_ASSERT(NULL==mP);
			return ::XML_ResumeParser(mP);
		}

		
	};

	namespace policy{

	class expat_parser_deque_container_policy{
	public:
		typedef expat_parser_deque_container_policy self_type;
		typedef expat_parser_interface<self_type> interface_type;
		
		struct attribute_data{
			std::string name;
			GeneralPurposeVariable data;
		};
		typedef std::list<attribute_data> att_list;
		struct element_data{
			
			void reset(const XML_Char * name_,const XML_Char ** attr,int depth_){
				name = name_;
				for (size_t i = 0; attr[i]; i += 2) 
				{
					attribute_data data;
					data.name=attr[i];
					data.data.insert(attr[i + 1]);
					atts.push_back(data);
					dODSDebug(" %s='%s'", attr[i], attr[i + 1]);
				}
				depth = depth_;
			}
			std::string name;
			att_list atts;
			std::string between_data;
			int depth;
			typedef att_list::iterator attribute_iterator;
			attribute_iterator begin(){
				return atts.begin();
			}
			attribute_iterator end(){
				return atts.end();
			}
		};
		typedef std::deque<element_data> container_type;
		typedef std::deque<container_type::iterator> current_tag_stack;
		typedef container_type::iterator iterator;

		current_tag_stack mCTStack;

		container_type&getContainer(){
			return mC;
		}
		
		int getDepth()const{
			return mDepth;
		}
		
		iterator begin(){
			return mC.begin();
		}
		iterator end(){
			return mC.end();
		}
		expat_parser_deque_container_policy(){
			mDepth = 0;
		}
		void clear(){
			mDepth = 0;
			mC.clear();
		}
		static void startElement(void *userData, const XML_Char *name, const XML_Char **atts)
		{
			interface_type *p =(interface_type*)userData;
			self_type &a = p->getPolicy();
			element_data d;
			
			d.reset(name,atts,a.getDepth());
			
			a.mC.push_back(d);
			a.mCTStack.push_back(boost::prior(a.mC.end()));
			
			a.depth_inc();
		}
    static void endElement(void *userData, const XML_Char *name)
		{
			interface_type *p =(interface_type*)userData;
			self_type &a = p->getPolicy();
			a.depth_dec();
			a.mCTStack.pop_back();
			dkcmNOT_ASSERT(0 > a.getDepth());
		}
    static void characters(void *userData, const XML_Char *data, int len)
		{
			interface_type *p =(interface_type*)userData;
			self_type &a = p->getPolicy();
			
			dkcmNOT_ASSERT(a.mCTStack.empty());
			if(a.mCTStack.empty())
				return;

			iterator it = a.mCTStack.back();
			for(int i=0;i<len;i++)
			{//もっと高速化したいななぜかこれじゃないと上手くいかない。
				(*it).between_data += data[i];
			}
		}
		static void comment(void *userData,const XML_Char *data)
		{
			

		}
	
    static int unknownEncodingHandler(void *encodingHandlerData, 
				      const XML_Char *name, XML_Encoding *info){
		}

    static void elementDeclHandler(void *userData, const XML_Char *name, XML_Content *model){
		}
    static void attlistDeclHandler(void *userData,
				   const XML_Char *elname,
				   const XML_Char *attname,
				   const XML_Char *att_type,
				   const XML_Char *dflt,
				   int isrequired){
		}
    static void startDoctypeDeclHandler(void *userData,
					const XML_Char *doctypeName,
					const XML_Char *sysid,
					const XML_Char *pubid,
					int has_internal_subset){
		}
    static void endDoctypeDeclHandler(void *userData){
		}

    static int externalEntityRefHandler(XML_Parser p,
					const XML_Char *context,
					const XML_Char *base,
					const XML_Char *systemId,
					const XML_Char *publicId){
		}

		void depth_inc(){
			mDepth++;
		}
		void depth_dec(){
			mDepth--;
		}
	private:
		container_type mC;
		int mDepth;
		

	};


	}//eon

}//end of dkutil namespace




#endif//end of include once
