#include <xercesc/parsers/XercesDOMParser.hpp>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/sax/HandlerBase.hpp>
#include <xercesc/util/XMLString.hpp>
#include <xercesc/util/PlatformUtils.hpp>
#include "xmlui.h"

// ----------------------------------------------------------------------------
CXmlFile::CXmlFile(CXmlUi* pXmlUi)
{
	m_pXmlUi = pXmlUi;

	m_pathname.clear();
	m_filename.clear();
	m_nodes.clear();
}

// ----------------------------------------------------------------------------
CXmlFile::~CXmlFile()
{
	for (vector<CXmlNode*>::iterator iter = m_nodes.begin(); iter != m_nodes.end(); iter++)
		delete *iter;
	m_nodes.clear();
}

// ----------------------------------------------------------------------------
bool CXmlFile::LoadFile(const string& pathname, const string& filename)
{
	bool resultBool = true;
	XercesDOMParser* parser = new XercesDOMParser();
	parser->setDoSchema(true);						// optional
	//parser->setDoNamespaces(true);					// optional	
	parser->setValidationScheme(AbstractDOMParser::Val_Always);

	ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
	parser->setErrorHandler(errHandler);

	try {		
		parser->parse(filename.c_str());
	}
	catch (const XMLException& toCatch) {
		char* message = XMLString::transcode(toCatch.getMessage());
		printf("Exception message is: \n%s\n", message);
		XMLString::release(&message);
		resultBool = false;
	}
	catch (const DOMException& toCatch) {
		char* message = XMLString::transcode(toCatch.msg);
		printf("Exception message is: \n%s\n", message);
		XMLString::release(&message);
		resultBool = false;
	}
	catch (...) {
		printf("[%s]Unexpected Exception \n", filename.c_str());
		resultBool = false;
	}

	if ( resultBool )
	{		
		DOMDocument* document = parser->getDocument();
		DOMElement* element = document->getDocumentElement();
		DOMNode* node = element->getFirstChild();
		while ( node )
		{
			CXmlNode* pXmlNode = NULL;
			char* nodeName = NULL;
			if ( node->getNodeType() == DOMNode::ELEMENT_NODE )
			{
				nodeName = XMLString::transcode(node->getNodeName());

				if ( XMLString::compareString(nodeName, "Script") == 0 )
				{
					pXmlNode = new CXmlScript(pathname);
				}
				else if ( XMLString::compareString(nodeName, "FontString") == 0 )
				{
					pXmlNode = new CXmlFontString(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "Texture") == 0 )
				{
					pXmlNode = new CXmlTexture(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "Frame") == 0 )
				{
					pXmlNode = new CXmlFrame(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "Model") == 0 )
				{
					pXmlNode = new CXmlModel(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "Button") == 0 )
				{
					pXmlNode = new CXmlButton(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "CheckButton") == 0 )
				{
					pXmlNode = new CXmlCheckButton(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "RadioButton") == 0 )
				{
					pXmlNode = new CXmlRadioButton(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "Slider") == 0 )
				{
					pXmlNode = new CXmlSlider(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "StatusBar") == 0 )
				{
					pXmlNode = new CXmlStatusBar(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "EditBox") == 0 )
				{
					pXmlNode = new CXmlEditBox(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "ListBox") == 0 )
				{
					pXmlNode = new CXmlListBox(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "MessageFrame") == 0 )
				{
					pXmlNode = new CXmlMessageFrame(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "ScrollFrame") == 0 )
				{
					pXmlNode = new CXmlScrollFrame(m_pXmlUi);
				}
				else if ( XMLString::compareString(nodeName, "GameTooltip") == 0 )
				{
					pXmlNode = new CXmlTooltip(m_pXmlUi);
				}
				XMLString::release(&nodeName);

				if ( pXmlNode )
				{
					if ( pXmlNode->LoadFromDOMNode(node) )
						m_nodes.push_back(pXmlNode);
					else
						XML_SAFE_DLETE(pXmlNode);
				}
			}
			node = node->getNextSibling();
		}
	}

	m_pathname = pathname;
	m_filename = filename;

	delete parser;
	delete errHandler;

	return resultBool;
}

// ----------------------------------------------------------------------------
CXmlUi::CXmlUi()
{
	m_nameIndex.clear();
	m_files.clear();	
}

// ----------------------------------------------------------------------------
CXmlUi::~CXmlUi()
{
	for ( vector<CXmlFile*>::iterator iter = m_files.begin(); iter != m_files.end(); iter++ )
		delete *iter;
	m_files.clear();

	m_nameIndex.clear();
}

// ----------------------------------------------------------------------------
bool CXmlUi::OpenToc(const char* filename)
{
	if ( filename == NULL || !CheckTocFile(filename) )
		return false;	

	FILE *fptr;
	if ( (fptr = fopen( filename, "r" )) == NULL )
		return false;

	vector<string> tocList;
	string path = GetPath(string(filename));
	while ( !feof(fptr) )
	{
		string lineStr;

		// 載入XML檔案
		if ( ReadLine(fptr, lineStr) )
		{
			if ( strncmp(lineStr.c_str(), "##", 2) != 0 )
			{
				if ( CheckXmlFile(lineStr) )
				{
					string tmpStr = path;
					tmpStr += lineStr;
					tocList.push_back(tmpStr);
				}
			}
		}
	}
	fclose( fptr );

	// 初始化 Xerces-c++
	try {
		XMLPlatformUtils::Initialize();
	}
	catch (const XMLException& toCatch) {
		char* message = XMLString::transcode(toCatch.getMessage());
		printf("Error during initialization! :\n%s\n", message );
		XMLString::release(&message);
		return false;
	}

	CXmlFile* pXmlFile = NULL;
	for ( vector<string>::iterator iter = tocList.begin(); iter != tocList.end(); iter++ )
	{
		pXmlFile = new CXmlFile(this);
		if ( pXmlFile->LoadFile(path, *iter) )
			m_files.push_back(pXmlFile);
		else
			delete pXmlFile;
	}
	
	XMLPlatformUtils::Terminate();
	return true;
}

// ----------------------------------------------------------------------------
void CXmlUi::AddName(const char* name, CXmlLayout* layout)
{
	if (name && layout)
	{
		const char* parent = "$parent";
		if ( strncmp(name, parent, strlen(parent)) != 0 )
		{
			pair<map<string, CXmlLayout*>::iterator, bool> hr;
			hr = m_nameIndex.insert(make_pair(name, layout));

			// 名稱重複
			assert(hr.second);
		}
	}
}

// ----------------------------------------------------------------------------
CXmlLayout* CXmlUi::FindByName(const char* name)
{
	map<string, CXmlLayout*>::iterator iter = m_nameIndex.find(name);
	if (iter != m_nameIndex.end())
		return iter->second;
	return NULL;
}

/*
// ----------------------------------------------------------------------------
bool CXmlUi::LoadFile(const string& pathname, const string& filename)
{
	bool resultBool = true;
	XercesDOMParser* parser = new XercesDOMParser();
	parser->setDoSchema(true);						// optional
	//parser->setDoNamespaces(true);					// optional	
	parser->setValidationScheme(AbstractDOMParser::Val_Always);

	ErrorHandler* errHandler = (ErrorHandler*) new HandlerBase();
	parser->setErrorHandler(errHandler);
	
	try {		
		parser->parse(filename.c_str());
	}
	catch (const XMLException& toCatch) {
		char* message = XMLString::transcode(toCatch.getMessage());
		printf("Exception message is: \n%s\n", message);
		XMLString::release(&message);
		resultBool = false;
	}
	catch (const DOMException& toCatch) {
		char* message = XMLString::transcode(toCatch.msg);
		printf("Exception message is: \n%s\n", message);
		XMLString::release(&message);
		resultBool = false;
	}
	catch (...) {
		printf("[%s]Unexpected Exception \n", filename.c_str());
		resultBool = false;
	}

	if ( resultBool )
	{		
		DOMDocument* document = parser->getDocument();
		DOMElement* element = document->getDocumentElement();
		DOMNode* node = element->getFirstChild();
		while ( node )
		{
			if ( node->getNodeType() == DOMNode::ELEMENT_NODE )
			{
				CXmlLayout* pXmlLayout;
				char* nodeName;

				pXmlLayout = NULL;
				nodeName = XMLString::transcode(node->getNodeName());
				if ( XMLString::compareString(nodeName, "Script") == 0 )
				{
					CXmlScript* pXmlScript = new CXmlScript();
					if ( pXmlScript->LoadFromDOMNode(node) )
					{
						pXmlScript->m_file = pathname + pXmlScript->m_file;
						m_scripts.push_back(pXmlScript);
					}
					else
						XML_SAFE_DLETE(pXmlScript);
				}
				else if ( XMLString::compareString(nodeName, "FontString") == 0 )
				{
					pXmlLayout = new CXmlFontString(this);
				}
				else if ( XMLString::compareString(nodeName, "Texture") == 0 )
				{
					pXmlLayout = new CXmlTexture(this);
				}
				else if ( XMLString::compareString(nodeName, "Frame") == 0 )
				{
					pXmlLayout = new CXmlFrame(this);
				}
				else if ( XMLString::compareString(nodeName, "Model") == 0 )
				{
					pXmlLayout = new CXmlModel(this);
				}
				else if ( XMLString::compareString(nodeName, "Button") == 0 )
				{
					pXmlLayout = new CXmlButton(this);
				}
				else if ( XMLString::compareString(nodeName, "CheckButton") == 0 )
				{
					pXmlLayout = new CXmlCheckButton(this);
				}
				else if ( XMLString::compareString(nodeName, "RadioButton") == 0 )
				{
					pXmlLayout = new CXmlRadioButton(this);
				}
				else if ( XMLString::compareString(nodeName, "Slider") == 0 )
				{
					pXmlLayout = new CXmlSlider(this);
				}
				else if ( XMLString::compareString(nodeName, "StatusBar") == 0 )
				{
					pXmlLayout = new CXmlStatusBar(this);
				}
				else if ( XMLString::compareString(nodeName, "EditBox") == 0 )
				{
					pXmlLayout = new CXmlEditBox(this);
				}
				else if ( XMLString::compareString(nodeName, "ListBox") == 0 )
				{
					pXmlLayout = new CXmlListBox(this);
				}
				else if ( XMLString::compareString(nodeName, "MessageFrame") == 0 )
				{
					pXmlLayout = new CXmlMessageFrame(this);
				}
				else if ( XMLString::compareString(nodeName, "ScrollFrame") == 0 )
				{
					pXmlLayout = new CXmlScrollFrame(this);
				}
				XMLString::release(&nodeName);

				if ( pXmlLayout )
				{
					if ( pXmlLayout->LoadFromDOMNode(node) )
						m_layouts.push_back(pXmlLayout);
					else
						XML_SAFE_DLETE(pXmlLayout);
				}
			}
			node = node->getNextSibling();
		}
	}

	delete parser;
	delete errHandler;

	return resultBool;
}
*/

// ----------------------------------------------------------------------------
bool CXmlUi::ReadLine(FILE *fptr, string& lineStr)
{
	static char tmpStr[1024];
	if ( fgets( tmpStr, 1024, fptr ) )
	{
		char *begin = tmpStr;
		char *end = tmpStr + strlen(tmpStr) - 1;

		while ( *begin && (*begin == '\t' || *begin == ' ') ) begin++;
		while ( end > begin && (*end == '\t' || *end == ' ' || *end == '\n' ) ) end--;

		tmpStr[end - begin + 1] = 0;
		lineStr = tmpStr;
		return true;
	}
	return false;
}

// ----------------------------------------------------------------------------
bool CXmlUi::CheckTocFile(const string& filename)
{
	string ext(GetFileExt(filename));
	_strlwr( (char*)ext.c_str() );
	if ( ext == "toc" )
		return true;
	return false;
}

// ----------------------------------------------------------------------------
bool CXmlUi::CheckXmlFile(const string& filename)
{
	string ext(GetFileExt(filename));
	_strlwr( (char*)ext.c_str() );
	if ( ext == "xml" )
		return true;
	return false;
}

// ----------------------------------------------------------------------------
string CXmlUi::GetFileExt(const string& filename)
{
	int len = (int)filename.length();
	int i;
	for (i = len-1; i >= 0; i--)
	{
		if (filename[i] == '.')
		{
			return filename.substr(i+1, len-i-1);
			//return ext;
		}
	}
	return string("");
}

// ----------------------------------------------------------------------------
string CXmlUi::GetPath(const string& filename)
{
	int len = (int)filename.length();
	int i;
	for (i = len-1; i >= 0; i--)
	{
		if (filename[i] == '\\')
		{
			return filename.substr(0, i+1);
		}
	}
	return string("");
}