#include"CXMLDocument.h"
#include"../CString.h"
#include"../FileIO/CFileRead.h"
#include"../FileIO/CFileWrite.h"
#include"../Debug/CWarning.h"
#include"../Debug/CException.h"

#include<string>
#include<stack>
namespace Maid
{

	mstring ReadToken( const mstring& Text, int& pos )
	{
		mstring ret;

		if( Text[pos]=='<' )
		{	//	タグ読み込み
			while( true )
			{
				if( (int)Text.length()<=pos ) { break; }
				
				const mstring::value_type c = Text[pos];
				if( c=='\t' ) { ++pos; continue; }
				ret += c;
				++pos;
				if( c=='>' ) { break; }
			}

		}else
		{	//	テキスト読み込み
			while( true )
			{
				if( (int)Text.length()<=pos ) { break; }
				
				const mstring::value_type c = Text[pos];
			//	if( c==' '  ) { ++pos; continue; }
			//	if( c=='\r' ) { ++pos; continue; }
			//	if( c=='\n' ) { ++pos; continue; }
			//	if( c=='\t' ) { ++pos; continue; }
				if( c=='<' ) { break; }
				ret += c;
				++pos;
			}
		}
		return ret;
	}


	mstring ReadAttributeName( const mstring& Text, int& pos )
	{
		mstring ret;

		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }

			const mstring::value_type c = Text[pos];

			if( c=='/' )
			{
				if( Text.length() - pos==2 ) { break; }
			}

			if( c=='>' || c==' ' || c=='=' ) { break; }
			ret += c;
			++pos;
		}

		return ret;
	}

	mstring ReadAttributeValue( const mstring& Text, int& pos )
	{
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }

			const mstring::value_type c = Text[pos];
			++pos;
			if( c=='\"' ) {  break; }
		}

		mstring ret;
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }

			const mstring::value_type c = Text[pos];
			++pos;
			if( c=='\"' ) { break; }
			ret += c;
		}

		return ret;
	}

	void SkipSpace( const mstring& Text, int& pos )
	{
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }

			const mstring::value_type c = Text[pos];
			if( c!=' ' ) { break; }

			++pos;
		}
	}
	void SkipEqual( const mstring& Text, int& pos )
	{
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }

			const mstring::value_type c = Text[pos];
			if( c!=' ' && c!='=' ) { break; }

			++pos;
		}
	}

	mstring ReadComment( const mstring& Text )
	{
		mstring ret;

		int pos = 2;

		//	<!-- をすっ飛ばす
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }
			if( Text[pos-2]=='-' && Text[pos-1]=='-' ) { break; }
			++pos;
		}

		//	コメント文読み取り
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }
			if( Text[pos]=='-' && Text[pos+1]=='-' ) { break; }
			ret += Text[pos];
			++pos;
		}

		//	--> をすっ飛ばす
		while( true )
		{
			if( (int)Text.length()<=pos ) { break; }
			if( Text[pos-2]=='-' && Text[pos-1]=='>' ) { break; }
			++pos;
		}
		return ret;
	}


	/*!
	 	@class	CXMLDocument CXMLDocument.h
	 	@brief	XMLでごにょごにょするときにベースとなるクラス
	 */


/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/
//! XMLファイルの読み込み
/*!
 	@param	FileName [i ] 読み込むファイル名
 */
void	CXMLDocument::Load( const mstring& FileName )
{
	mstring Text;
	{
		MySTL::string t;
		CFileRead::Read( FileName, t );

		//	ここで文字コードを判定しないとまずいけど、sjisでいいか(´ー｀)
		Text = CString::ConvertSJIStoMAID(t);
	}

	return LoadText(Text);
}


void CXMLDocument::LoadText( const mstring& Text )
{
	int pos=0;

	{
		//	最初のトークンは<?xml で決まっている
		mstring token = ReadToken( Text, pos );
	}
	{

		//	次のトークンはルートノードですが、
		//	そこまでの空白等は全部飛ばす

		while( true )
		{
			if( Text[pos]=='<' ) { break; }
			++pos;
		}
	}

	MySTL::stack<SPXMLNODE>	NodeStack;

	m_pRootNode = ReadChildNode( ReadToken( Text, pos ) );

	NodeStack.push( m_pRootNode );

	while( true )
	{
		if( (int)Text.length()<=pos ) { break; }
		if( NodeStack.empty() ) { break; }
		const mstring token = ReadToken( Text, pos );

		if( token.empty() ) { continue; }

		if( token[0]=='<' )//	ノード発見
		{
			if( token[1]=='/' )//	みつけたのは終了ノード
			{
				NodeStack.pop();

			}ef( token[1]=='!' )//	みつけたのはコメントノード
			{
				SPXMLNODE pNode( new CXMLNode );
				const mstring comment = ReadComment( token );

				pNode->SetComment( comment );
				NodeStack.top()->AddChildNode( pNode );
			}
			else
			{
				SPXMLNODE pNode = ReadChildNode( token );
				NodeStack.top()->AddChildNode( pNode );

				if( token.substr(token.length()-2)==MAIDTEXT("/>") )
				{

				}else
				{
					NodeStack.push( pNode );
				}
			}
		}else
		{
			SPXMLNODE pNode( new CXMLNode );
			pNode->SetText( token );
			NodeStack.top()->AddChildNode( pNode );
		}
	}

	//	スタックが０でない＝＝閉じタグの個数が合ってない
	if( !NodeStack.empty() ) { MAID_THROWEXCEPTION( MAIDTEXT("閉じタグの個数が合ってない") ); }
}

CXMLNode& CXMLDocument::GetRoot()
{
	return *m_pRootNode;
}


void CXMLDocument::DivAttribute( const mstring& Element, mstring& Name, MySTL::vector<ATTRIBUTE>& Attribute )
{
	MAID_ASSERT( Element[0]!='<', "タグ名ではありません" << Element );

	int pos = 1;

	Name = ReadAttributeName( Element, pos );
	SkipSpace( Element, pos );

	while( true )
	{
		if( (int)Element.length()-2<=pos ) { break; }
		//	↑ -2 なのは ">" か "/>" のケースを考えてのこと
		//	">" か "/>" で条件わけをしなくても問題ない

		ATTRIBUTE a;

		a.Name = ReadAttributeName( Element, pos );
		SkipEqual( Element, pos );
		a.Value= ReadAttributeValue( Element, pos );
		SkipSpace( Element, pos );

		Attribute.push_back( a );
	}
}

SPXMLNODE CXMLDocument::ReadChildNode( const mstring& token )
{
	mstring name;
	MySTL::vector<ATTRIBUTE> att;

	DivAttribute( token, name, att );

	SPXMLNODE pNode( new CXMLNode );
	pNode->SetElementName( name );

	for( int i=0; i<(int)att.size(); ++i )
	{
		pNode->SetAttribute( att[i].Name, att[i].Value );
	}

	return pNode;
}



}

