#include "StdAfx.h"
#include "XMLTextParser.h"
#include "TypedBuffer.h"

enum
{
	TAG_TYPE_START		= 0,
	TAG_TYPE_START_END	= 1,
	TAG_TYPE_END		= 2,
};

CXMLTextParser::CXMLTextParser(void)
{
	m_escape.AddEscape(_T("<"), _T("&lt;"));
	m_escape.AddEscape(_T(">"), _T("&gt;"));
	m_escape.AddEscape(_T("&"), _T("&amp;"));
}

CXMLTextParser::~CXMLTextParser(void)
{
}


/////////////////////////////////////////////////////////////////////////////////////
//	開始
/////////////////////////////////////////////////////////////////////////////////////
/*!
	スキャン
*/
void CXMLTextParser::StartScan(CString xml)
{
	xml.Trim();
	m_xmlData = xml;
	m_dataLen = xml.GetLength();

	m_lpXml = m_xmlData.GetBuffer(0);

	DWORD tick = GetTickCount();
	BuildTagIndex();
	TRACE("BuildTagIndex : %d\n", GetTickCount() - tick);

	tick = GetTickCount();
	m_tagCounter = 0;
	while(ParseNextTag())
		;
	TRACE("ParseNextTag : %d\n", GetTickCount() - tick);
}


/*!
	ファイルから取得
*/
CString CXMLTextParser::LoadXmlFile(CString path)
{
	//	開く
	FILE	*in;
	if(_tfopen_s(&in, path, _T("rb")) != 0)
		throw CXMLTextParserException("ファイルのオープンに失敗しました", 0);
	
	//	サイズ取得
	fseek(in, 0, SEEK_END);
	long	len = ftell(in);
	fseek(in, 0, SEEK_SET);

	//	バッファ用意
	CTypedBuffer<char> buf(len + 1);

	//	読み込み
	fread(buf, len, 1, in);
	buf[len] = 0;
	fclose(in);

	//	変換
	CStringW	ret;
	int wbLen = MultiByteToWideChar(CP_UTF8, 0, buf, len, NULL, 0);
	MultiByteToWideChar(CP_UTF8, 0, buf, len, ret.GetBuffer(wbLen), wbLen);
	ret.ReleaseBufferSetLength(wbLen);

	return(CString(ret));
}


/////////////////////////////////////////////////////////////////////////////////////
//	タグ
/////////////////////////////////////////////////////////////////////////////////////
/*!
	インデックスの作成
*/
void CXMLTextParser::BuildTagIndex()
{
	LPSTR str = m_lpXml;

	//	タグチェック
	if(*str != '<')
		throw CXMLTextParserException(_T("異常な文字を検出しました"), 0);

	//	タグ位置検索
	int	curTagIsStart = FALSE;
	for(int i=0;i<m_dataLen;i++)
	{
		//	開始タグ検出
		if(*str == '<')
		{
			*str = 0;
			str++;
			if(curTagIsStart || *str == 0)
				throw CXMLTextParserException(_T("異常なタグを検出しました"), i);

			m_tags.Add(str);
			curTagIsStart = TRUE;
			continue;
		}

		//	終了タグ検出
		if(*str == '>')
		{
			*str = 0;
			str++;
			if(!curTagIsStart)
				throw CXMLTextParserException(_T("異常なタグを検出しました"), i);

			m_datas.Add(str);
			curTagIsStart = FALSE;
			continue;
		}

		str++;
	}

	//	閉じてない
	if(curTagIsStart || m_tags.GetSize() != m_datas.GetSize())
		throw CXMLTextParserException(_T("異常なタグを検出しました"), m_dataLen);
}

/*!
	開始タグ切り出し
*/
int CXMLTextParser::ParseNextTag()
{
	//	次のタグを取得
	LPSTR	lpTag, lpData;
	if(!GetNextTag(&lpTag, &lpData))
		return(0);

	//	タグとオプション切り分け
	LPSTR	lpOption;
	SplitTag(lpTag, &lpOption);

	//	特殊タグ
	if(lpTag[0] == '?' || lpTag[0] == '!')
	{
		OnSpecialTag(lpTag);
		return(1);
	}

	//	異常なタグ
	if(!_istalpha(lpTag[0]) && lpTag[0] != '/')
		throw CXMLTextParserException(_T("異常なタグを検出しました"), lpTag - m_lpXml);

	//	タグタイプ取得
	int tagType = GetTagType(&lpTag);

	//	タイプ検出
	if(tagType == TAG_TYPE_END)
	{
		//	終了タグ
		OnEndTag(lpTag);
	}
	else if(tagType == TAG_TYPE_START_END)
	{
		//	解析
		CTagOptionArray	keys;
		ParseTagOption(lpTag, keys);

		//	イベント
		OnStartTag(lpTag, keys);
		OnEndTag(lpTag);
	}
	else
	{
		//	解析
		CTagOptionArray	keys;
		ParseTagOption(lpTag, keys);

		//	イベント
		OnStartTag(lpTag, keys);

		//	データ解析
//		ParseData(data);
	}
	return(1);
}


/*!
	データ切り出し
*/
void CXMLTextParser::ParseData(CString &data)
{
	//	終了/無し
	if(data.IsEmpty())
		return;

	//	変換
	data = m_escape.UnEscape(data);

	//	イベント
	OnData(data);
}


/*!
	タグオプションの解析
*/
void CXMLTextParser::ParseTagOption(LPSTR tag, CTagOptionArray &keys)
{
/*	//	スペース文字置き換え
	LPSTR	str = tag;
	while(*str)
	{
		if(*str == '\t')
			*str = ' ';
		str++;
	}

	//	タグ切り出し
	CString	tagName;
	tag.Replace(_T("\t"), _T(" "));
	if(!GetNextToken(tag, _T(" "), tagName))
	{
		while(1)
		{
			//	キー
			CString	key, data;
			if(GetNextToken(tag, _T("="), key))
				break;
			key.Trim();

			//	データ
			if(GetNextToken(tag, _T("\""), data))
				throw CXMLTextParserException(_T("異常なタグを検出しました"), m_dataLen - m_xmlData.GetLength());
			if(GetNextToken(tag, _T("\""), data))
				throw CXMLTextParserException(_T("異常なタグを検出しました"), m_dataLen - m_xmlData.GetLength());

			//	追加
			keys.Add(CTagOption(key, data));
		}
	}

	tag = tagName;*/
}


/////////////////////////////////////////////////////////////////////////////////////
//	ツール
/////////////////////////////////////////////////////////////////////////////////////
/*!
	指定した文字までを切り出す
	指定した文字は切り出し文字に含まれない

	指定した文字が見つからない場合は、-1が戻る
*/
int CXMLTextParser::GetNextToken(CString &src, CString find, CString &token, int tokenStart, int removeToken)
{
	//	検索する
	int tokenEnd = src.Find(find, tokenStart);
	if(tokenEnd == -1)
		return(-1);

	//	切り出し
	token = src.Mid(tokenStart, tokenEnd - tokenStart);

	//	削除
	if(removeToken)
		src.Delete(0, tokenEnd + find.GetLength());
	else
		src.Delete(0, tokenEnd);

	return(0);
}


/*!
	次のタグを取得
*/
_inline int CXMLTextParser::GetNextTag(LPSTR *tag, LPSTR *data)
{
	if(m_tags.GetSize() <= m_tagCounter)
		return(0);

	*tag = m_tags[m_tagCounter];
	*data = m_datas[m_tagCounter];
	m_tagCounter++;
	return(1);
}


/*!
	タグを分離
*/
_inline void CXMLTextParser::SplitTag(LPSTR tag, LPSTR *tagOption)
{
	for(int i=0;;i++)
	{
		if(*tag == 0)
		{
			*tagOption = tag;
			return;
		}
		else if(*tag == ' ' || *tag == '\t')
		{
			*tag = 0;
			*tagOption = tag;
			return;
		}
		tag++;
	}
}


/*!
	タグタイプ取得
*/
_inline int CXMLTextParser::GetTagType(LPSTR *tag)
{
	int	tagLen = strlen(*tag);

	//	タイプ検出
	if(*tag[0] == _T('/'))
	{
		*tag++;
		return TAG_TYPE_END;
	}
	else if((*tag)[tagLen-1] == _T('/'))
	{
		*tag[tagLen-1] = 0;
		return TAG_TYPE_START_END;
	}
	else
	{
		return TAG_TYPE_START;
	}
}

/*!
	指定した文字までを切り出す
*/
/*
_inline int CXMLTextParser::GetNextToken(LPSTR *src, LPSTR splitter, LPSTR *token)
{
	LPSTR	str = *src;
	while(1)
	{
		//	全区切り検出
		for(int i=0;splitter[i];i++)
		{
			if(*str == splitter[i])
			{
				*src = str;

			}
		}
	}
}

*/