#include <stdlib.h>

#define XMLPARSEAPI(type) type
#include "expat\expat.h"
#include "xml.h"
#include <assert.h>
#include <algorithm>

/**
 ******************************************************************************
 * XmlNode implementation.
 ******************************************************************************
 */
XmlAttribute XmlNode::tempAttr;

XmlNode::~XmlNode()
{
	// Clear parent pointer from childs.
	for (XmlNodes::const_iterator it = m_childs.begin(); it != m_childs.end(); ++it)
	{
		XmlNode *node = *it;
		node->m_parent = 0;
	}
}

//! The only ctor and private, protect us from deriviation.
XmlNode::XmlNode( cstr tag )
{
	m_tag = tag;
	m_parent = 0;
	m_refCount = 0;
}

//////////////////////////////////////////////////////////////////////////
bool XmlNode::isTag( cstr tag ) const
{
#if defined(LINUX)
	return compareTextFileStrings(m_tag, tag) == 0;
#else
	return stricmp( tag, m_tag ) == 0;
#endif
}

XmlString const& XmlNode::getAttr( cstr key ) const
{
	tempAttr.key = key;
	XmlAttributes::const_iterator it = std::find( m_attributes.begin(),m_attributes.end(),tempAttr );
	if (it != m_attributes.end()) {
		return it->value;
	}

	static XmlString empty;
	return empty;
}

bool XmlNode::haveAttr( cstr key ) const
{
	tempAttr.key = key;
	XmlAttributes::const_iterator it = std::find( m_attributes.begin(),m_attributes.end(),tempAttr );
	if (it != m_attributes.end()) {
		return true;
	}
	return false;
}

void XmlNode::delAttr( cstr key )
{
	tempAttr.key = key;
	XmlAttributes::iterator it = std::find( m_attributes.begin(),m_attributes.end(),tempAttr );
	if (it != m_attributes.end()) {
		m_attributes.erase(it);
	}
}

void XmlNode::removeAllAttributes()
{
	m_attributes.clear();
}

void XmlNode::setAttr( cstr key,cstr value )
{
	tempAttr.key = key;
	tempAttr.value = value;
	XmlAttributes::iterator it = std::find( m_attributes.begin(),m_attributes.end(),tempAttr );
	if (it == m_attributes.end())
	{
		m_attributes.push_back( tempAttr );
	}
	else
	{
		// If already exist, ovveride this member.
		XmlAttribute &attr = *it;
		attr = tempAttr;
	}
}

void XmlNode::setAttr( cstr key,int value )
{
	char str[1024];
	itoa( value,str,10 );
	setAttr( key,str );
}

void XmlNode::setAttr( cstr key,unsigned int value )
{
	char str[1024];
	itoa( value,str,10 );
	setAttr( key,str );
}

void XmlNode::setAttr( cstr key,float value )
{
	char str[1024];
	sprintf( str,"%g",value );
	setAttr( key,str );
}


bool XmlNode::getAttr( cstr key, XmlString& value ) const
{
	tempAttr.key = key;
	XmlAttributes::const_iterator it = std::find( m_attributes.begin(),m_attributes.end(),tempAttr );
	if (it != m_attributes.end()) {
		value = it->value;
		return true;
	}
	return false;
}

bool XmlNode::getAttr( cstr key,int &value ) const
{
	XmlString str;
	if (getAttr(key, str)) {
		value = atoi(str);
		return true;
	}
	return false;
}

bool XmlNode::getAttr( cstr key,unsigned int &value ) const
{
	XmlString str;
	if (getAttr(key, str)) {
		value = atoi(str);
		return true;
	}
	return false;
}

bool XmlNode::getAttr( cstr key,bool &value ) const
{
	XmlString str;
	if (getAttr(key, str)) {
		value = atoi(str)!=0;
		return true;
	}
	return false;
}

bool XmlNode::getAttr( cstr key,float &value ) const
{
	XmlString str;
	if (getAttr(key, str)) {
		value = (float)atof(str);
		return true;
	}
	return false;
}

XmlNode* XmlNode::findChild( cstr tag ) const
{
	for (XmlNodes::const_iterator it = m_childs.begin(); it != m_childs.end(); ++it) {
		if ((*it)->isTag(tag))
		{
			return *it;
		}
	}
	return 0;
}

XmlNode* XmlNode::findChild(XmlString const& tag, XmlString const& attr, XmlString const& val)
{
	for (int c = 0; ; c++)
	{
		XmlNode* child = getChild(c);
		if (!child)
			return 0;

		if (child->getTag() == tag && child->getAttr(attr) == val)
			return child;
	}

	return 0;
}


//////////////////////////////////////////////////////////////////////////
void XmlNode::deleteChild( cstr tag )
{
	for (XmlNodes::iterator it = m_childs.begin(); it != m_childs.end(); ++it)
	{
		if ((*it)->isTag(tag))
		{
			m_childs.erase(it);
			return;
		}
	}
}

//////////////////////////////////////////////////////////////////////////
void XmlNode::deleteChildAt( int nIndex )
{
	if (nIndex >= 0 && nIndex < (int)m_childs.size())
	{
		m_childs.erase( m_childs.begin() + nIndex );
	}
}

//! Adds new child node.
void XmlNode::addChild( XmlNode* node, int pos )
{
	assert( node != 0 );
	if (pos < 0)
		m_childs.push_back(node);
	else
		m_childs.insert(&m_childs.at(pos), node);
	node->m_parent = this;
};

XmlNode* XmlNode::newChild( cstr tagName )
{
	XmlNode* node = new XmlNode(tagName);
	addChild(node);
	return node;
}

void XmlNode::removeChild( XmlNode* node )
{
	XmlNodes::iterator it = std::find(m_childs.begin(), m_childs.end(), node );
	if (it != m_childs.end())
	{
		m_childs.erase(it);
	}
}

void XmlNode::removeAllChilds()
{
	m_childs.clear();
}

//! Get XML Node child nodes.
XmlNode* XmlNode::getChild( int i ) const
{
	if (i >= 0 && i < (int)m_childs.size())
		return m_childs[i];
	else
		return 0;
}

//////////////////////////////////////////////////////////////////////////
void XmlNode::copyAttributes( XmlNode const& fromNode )
{
	m_attributes = fromNode.m_attributes;
}

//////////////////////////////////////////////////////////////////////////
bool XmlNode::getAttributeByIndex( int index, XmlString* key, XmlString* value )
{
	XmlAttributes::iterator it = m_attributes.begin();
	if (it != m_attributes.end())
	{
		std::advance( it,index );
		if (it != m_attributes.end())
		{
			*key = it->key;
			*value = it->value;
			return true;
		}
	}
	return false;
}

//////////////////////////////////////////////////////////////////////////
XmlNode* XmlNode::clone()
{
	XmlNode* node = new XmlNode(m_tag);
	// Clone attributes.
	node->m_attributes = m_attributes;

	// Clone sub nodes.
	for (XmlNodes::const_iterator it = m_childs.begin(); it != m_childs.end(); ++it) {
		XmlNode* child = (*it)->clone();
		node->addChild( child);
	}
	
	return node;
}

XmlString XmlNode::getXML( int level )
{
	XmlString padding;
	XmlString xml;
	int i;

	//m_content = "";

	// Add tabs.
	for (i = 0; i < level; i++)
		padding += "\t";

	xml = padding;

	cstr sxml = xml;

	// Begin Tag
	xml += "<" + m_tag;

	// Put attributes on separate lines.
	for (XmlAttributes::const_iterator it = m_attributes.begin(); it != m_attributes.end(); ++it) {
		xml += "\n";
		xml += padding + "\t" + XmlString(it->key) + "=\"" + it->value + "\"";
	}

	if (m_content.empty() && m_childs.empty()) {
		// Compact tag form.
		xml += "/>\n";
		return xml;
	}
	xml += ">\n";
	
	// Put sub nodes.
	for (XmlNodes::const_iterator it = m_childs.begin(); it != m_childs.end(); ++it) {
		xml += (*it)->getXML( level+1 );
	}

	// End tag.
	
	// Add tabs.
	xml += padding;
	xml += "</" + m_tag + ">\n";
	return xml;
}

bool XmlNode::saveToFile( cstr fileName )
{
	XmlString xml = getXML();
	FILE *file = fopen( fileName,"wt" );
	if (file)
	{
		cstr sxml = (cstr)xml;
		fprintf( file,"%s",sxml );
		fclose(file);
		return true;
	}
	return false;
}

/**
 ******************************************************************************
 * XmlParserImp class.
 ******************************************************************************
 */
class XmlParserImp 
{
public:
	XmlParserImp();
	XmlNode* parse( cstr buffer,size_t bufLen,XmlString &errorString );

protected:
	void	onStartElement( cstr tagName,cstr *atts );
	void	onEndElement( cstr tagName );
	void	onRawData( cstr data );

	static void startElement(void *userData, cstr name, cstr *atts) {
		((XmlParserImp*)userData)->onStartElement( name,atts );
	}
	static void endElement(void *userData, cstr name ) {
		((XmlParserImp*)userData)->onEndElement( name );
	}
	static void characterData( void *userData, cstr s, int len ) {
		char str[32768];
		assert( len < 32768 );
		strncpy( str,s,len );
		str[len] = 0;
		((XmlParserImp*)userData)->onRawData( str );
	}

	// First node will become root node.
	std::vector<XmlNode*> nodeStack;
	XmlNode* m_root;

	XML_Parser m_parser;
};

/**
 ******************************************************************************
 * XmlParserImp
 ******************************************************************************
 */
XmlParserImp::XmlParserImp()
{
	m_root = 0;
}

void	XmlParserImp::onStartElement( cstr tagName,cstr *atts )
{
	XmlNode* parent = 0;
	XmlNode* node = new XmlNode( tagName );

	if (!nodeStack.empty()) {
		parent = nodeStack.back();
	} else {
		m_root = node;
	}
	nodeStack.push_back(node);

	if (parent) {
		parent->addChild( node );
	}

	node->setLine( XML_GetCurrentLineNumber( (XML_Parser)m_parser ) );
	
	// Call start element callback.
	XmlString key,value;
	int i = 0;
	while (atts[i] != 0) {
		node->setAttr( atts[i],atts[i+1] );
		i += 2;
	}
}

void	XmlParserImp::onEndElement( cstr tagName )
{
	assert( !nodeStack.empty() );
	if (!nodeStack.empty()) {
		nodeStack.pop_back();
	}
}

void	XmlParserImp::onRawData( cstr data )
{
	assert( !nodeStack.empty() );
	if (!nodeStack.empty())
	{
		XmlNode *node = nodeStack.back();
		node->addContent( data );
	}
}

XmlNode* XmlParserImp::parse( cstr buffer,size_t bufLen,XmlString &errorString )
{
	m_parser = XML_ParserCreate(NULL);

  XML_SetUserData( m_parser, this );
  XML_SetElementHandler( m_parser, startElement,endElement );
	XML_SetCharacterDataHandler( m_parser,characterData );
	XML_SetEncoding( m_parser,"utf-8" );

	XmlNode* root = 0;

	/*
	int size = file->size();
	char *buf = (char*)memory::malloc( size );
	file->read( buf,size );
	if (!XML_Parse( parser,buf,size,1 ))
	{
		error( "XML Error (%s): %s at line %d\n",filename.c_str(),XML_ErrorString(XML_GetErrorCode(parser)),XML_GetCurrentLineNumber(parser) );
		return false;
	}
	memory::free( buf );

	XMLParser::parse( fileName );
	*/
	if (XML_Parse( m_parser,buffer,(int)bufLen,1 ))
	{
		root = m_root;
	} else {
		char *str = new char [32768];
		sprintf( str,"XML Error: %s at line %d",XML_ErrorString(XML_GetErrorCode(m_parser)),XML_GetCurrentLineNumber(m_parser) );
		errorString = str;
    delete [] str;
//		CLogFile::FormatLine( "%s",str );
	}
	
	XML_ParserFree( m_parser );

	m_root = 0;

	return root;
}

//! Parse xml file.
XmlNode* XmlParser::parse( cstr fileName )
{
	m_errorString = "";
	XmlParserImp xml;
	std::vector<char> buf;
	FILE *file = fopen(fileName,"rb");
	if (file) {
		fseek( file,0,SEEK_END );
		int fileSize = ftell(file);
		fseek( file,0,SEEK_SET );
		buf.resize( fileSize );
		fread( &buf[0],fileSize,1,file );
		fclose(file);
		return xml.parse( &buf[0],buf.size(),m_errorString );
	} else {
		return 0;
	}
}
	
//! Parse xml from memory buffer.
XmlNode* XmlParser::parseBuffer( cstr buffer )
{
	m_errorString = "";
	XmlParserImp xml;
	return xml.parse( buffer,strlen(buffer),m_errorString );
};
