#include "global.h"
#include "XmlFileUtil.h"
#include "XmlFile.h"
#include "RageFile.h"
#include "RageFileDriverMemory.h"
#include "RageUtil.h"
#include "RageLog.h"
#include "arch/Dialog/Dialog.h"
#include "Foreach.h"

bool XmlFileUtil::LoadFromFileShowErrors( XNode &xml, RageFileBasic &f )
{
	RString sError;
	RString s;
	if( f.Read( s ) == -1 )
		sError = f.GetError();
	else
		Load( &xml, s, sError );
	if( sError.empty() )
		return true;

	RString sWarning = ssprintf( "XML: LoadFromFile failed: %s", sError.c_str() );
	LOG->Warn( "%s", sWarning.c_str() );
	Dialog::OK( sWarning, "XML_PARSE_ERROR" );
	return false;
}

bool XmlFileUtil::LoadFromFileShowErrors( XNode &xml, const RString &sFile )
{
	RageFile f;
	if( !f.Open(sFile, RageFile::READ) )
	{
		LOG->Warn("Couldn't open %s for reading: %s", sFile.c_str(), f.GetError().c_str() );
		return false;
	}

	bool bSuccess = LoadFromFileShowErrors( xml, f );
	if( !bSuccess )
	{
		RString sWarning = ssprintf( "XML: LoadFromFile failed for file: %s", sFile.c_str() );
		LOG->Warn( "%s", sWarning.c_str() );
		Dialog::OK( sWarning, "XML_PARSE_ERROR" );
	}
	return bSuccess;
}

static const char chXMLTagOpen		= '<';
static const char chXMLTagClose		= '>';
static const char chXMLQuestion		= '?';	// used in checking for meta tags: "<?TAG ... ?/>"
static const char chXMLTagPre			= '/';
static const char chXMLExclamation	= '!';
static const char chXMLDash			= '-';

static map<RString,RString> g_mapEntitiesToChars;
static map<char,RString> g_mapCharsToEntities;

static void InitEntities()
{
	if( !g_mapEntitiesToChars.empty() )
		return;

	static struct Entity
	{
		char c;
		const char *pEntity;
	}
	const EntityTable[] =
	{
		{ '&',  "amp", },
		{ '\"', "quot", },
		{ '\'', "apos", },
		{ '<',  "lt", },
		{ '>',  "gt", } 
	};

	for( unsigned i = 0; i < ARRAYLEN(EntityTable); ++i )
	{
		const Entity &ent = EntityTable[i];
		g_mapEntitiesToChars[ent.pEntity] = RString(1, ent.c);
		g_mapCharsToEntities[ent.c] = ent.pEntity;
	}
}

// skip spaces
static void tcsskip( const RString &s, RString::size_type &i )
{
	i = s.find_first_not_of( " \t\r\n", i );
}

// put string of (psz~end) on ps string
static void SetString( const RString &s, int iStart, int iEnd, RString* ps, bool trim = false )
{
	if( trim )
	{
		while( iStart < iEnd && s[iStart] > 0 && isspace(s[iStart]) )
			iStart++;
		while( iEnd-1 >= iStart && s[iEnd-1] > 0 && isspace(s[iEnd-1]) )
			iEnd--;
	}

	int len = iEnd - iStart;
	if( len <= 0 )
		return;

	ps->assign( s, iStart, len );
}

// attr1="value1" attr2='value2' attr3=value3 />
//                                            ^- return pointer
// Desc   : loading attribute plain xml text
// Param  : pszAttrs - xml of attributes
//          pi = parser information
// Return : advanced string pointer. (error return npos)
namespace
{
RString::size_type LoadAttributes( XNode *pNode, const RString &xml, RString &sErrorOut, RString::size_type iOffset )
{
	while( iOffset < xml.size() )
	{
		tcsskip( xml, iOffset );
		if( iOffset >= xml.size()  )
			continue;

		// close tag
		if( iOffset < xml.size() &&
			(xml[iOffset] == chXMLTagClose || xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
			return iOffset; // well-formed tag

		// XML Attr Name
		RString::size_type iEnd = xml.find_first_of( " =", iOffset );
		if( iEnd == xml.npos ) 
		{
			// error
			if( sErrorOut.empty() ) 
				sErrorOut = ssprintf( "<%s> attribute has error ", pNode->GetName().c_str() );
			return string::npos;
		}

		// XML Attr Name
		RString sName;
		SetString( xml, iOffset, iEnd, &sName );

		// add new attribute
		DEBUG_ASSERT( sName.size() );
		XNodeValue *pAttr = pNode->AppendAttr( sName );
		iOffset = iEnd;

		// XML Attr Value
		tcsskip( xml, iOffset );

		if( iOffset < xml.size() && xml[iOffset] == '=' )
		{
			++iOffset;

			tcsskip( xml, iOffset );
			if( iOffset >= xml.size()  )
				continue;

			// if " or '
			// or none quote
			char quote = xml[iOffset];
			if( quote == '"' || quote == '\'' )
			{
				++iOffset;
				iEnd = xml.find( quote, iOffset );
			}
			else
			{
				// XXX: This is invalid XML, should we be accepting it? -- Steve
				//attr= value>
				// none quote mode
				iEnd = xml.find_first_of( " >", iOffset );
			}

			if( iEnd == xml.npos ) 
			{
				// error
				if( sErrorOut.empty() ) 
					sErrorOut = ssprintf( "<%s> attribute text: couldn't find matching quote", sName.c_str() );
				return string::npos;
			}

			RString sValue;
			SetString( xml, iOffset, iEnd, &sValue, true );
			ReplaceEntityText( sValue, g_mapEntitiesToChars );
			pAttr->SetValue( sValue );
			iOffset = iEnd;

			if( quote == '"' || quote == '\'' )
				++iOffset;
		}
	}

	// not well-formed tag
	return string::npos;
}

// <TAG attr1="value1" attr2='value2' attr3=value3 >
// </TAG>
// or
// <TAG />
//        ^- return pointer
// Desc   : load xml plain text
// Param  : pszXml - plain xml text
//          pi = parser information
// Return : advanced string pointer  (error return npos)
RString::size_type LoadInternal( XNode *pNode, const RString &xml, RString &sErrorOut, RString::size_type iOffset )
{
	pNode->Clear();

	// <
	iOffset = xml.find( chXMLTagOpen, iOffset );
	if( iOffset == string::npos )
		return string::npos;

	// </
	if( xml[iOffset+1] == chXMLTagPre )
		return iOffset;

	// <!--
	if( !xml.compare(iOffset+1, 3, "!--") )
	{
		iOffset += 4;

		// Find the close tag.
		RString::size_type iEnd = xml.find( "-->", iOffset );
		if( iEnd == string::npos )
		{
			if( sErrorOut.empty() ) 
				sErrorOut = "Unterminated comment";

			return string::npos;
		}

		// Skip -->.
		iOffset = iEnd + 3;

		return LoadInternal( pNode, xml, sErrorOut, iOffset );
	}

	// XML Node Tag Name Open
	iOffset++;
	RString::size_type iTagEnd = xml.find_first_of( " \t\r\n/>", iOffset );
	RString sName;
	SetString( xml, iOffset, iTagEnd, &sName );
	pNode->SetName( sName );
	iOffset = iTagEnd;

	// Generate XML Attribute List
	iOffset = LoadAttributes( pNode, xml, sErrorOut, iOffset );
	if( iOffset == string::npos )
		return string::npos;

	// alone tag <TAG ... /> or <?TAG ... ?> or <!-- ... --> 
	// current pointer:   ^               ^              ^

	if( iOffset < xml.size() && (xml[iOffset] == chXMLTagPre || xml[iOffset] == chXMLQuestion || xml[iOffset] == chXMLDash) )
	{
		iOffset++;

		// skip over 2nd dash
		if( iOffset < xml.size() && xml[iOffset] == chXMLDash )
			iOffset++;

		if( iOffset == xml.size() || xml[iOffset] != chXMLTagClose )
		{
			// error: <TAG ... / >
			if( sErrorOut.empty() ) 
				sErrorOut = "Element must be closed.";

			// ill-formed tag
			return string::npos;
		}

		// well-formed tag
		++iOffset;

		// UGLY: We want to ignore all XML meta tags.  So, since the Node we 
		// just loaded is a meta tag, then Load ourself again using the rest 
		// of the file until we reach a non-meta tag.
		if( !pNode->GetName().empty() && (pNode->GetName()[0] == chXMLQuestion || pNode->GetName()[0] == chXMLExclamation) )
			iOffset = LoadInternal( pNode, xml, sErrorOut, iOffset );

		return iOffset;
	}

	// open/close tag <TAG ..> ... </TAG>
	//                        ^- current pointer
	if( pNode->GetAttr(XNode::TEXT_ATTRIBUTE) == NULL )
	{
		// Text Value 
		++iOffset;
		RString::size_type iEnd = xml.find( chXMLTagOpen, iOffset );
		if( iEnd == string::npos )
		{
			if( sErrorOut.empty() ) 
				sErrorOut = ssprintf( "%s must be closed with </%s>", pNode->GetName().c_str(), pNode->GetName().c_str() );
			// error cos not exist CloseTag </TAG>
			return string::npos;
		}

		RString sValue;
		SetString( xml, iOffset, iEnd, &sValue, true );

		iOffset = iEnd;
		ReplaceEntityText( sValue, g_mapEntitiesToChars );

		pNode->AppendAttr(XNode::TEXT_ATTRIBUTE, sValue );
	}

	// generate child nodes
	while( iOffset < xml.size() )
	{
		XNode *node = new XNode;

		iOffset = LoadInternal( node, xml, sErrorOut, iOffset );
		if( iOffset == string::npos )
		{
			delete node;
			return iOffset;
		}

		if( !node->GetName().empty() )
		{
			DEBUG_ASSERT( node->GetName().size() );
			pNode->m_childs.push_back( node );
		}
		else
		{
			delete node;
		}

		// open/close tag <TAG ..> ... </TAG>
		//                             ^- current pointer
		// CloseTag case
		if( iOffset+1 < xml.size() && xml[iOffset] == chXMLTagOpen && xml[iOffset+1] == chXMLTagPre )
		{
			// </Close>
			iOffset += 2; // C

			tcsskip( xml, iOffset );
			if( iOffset >= xml.size()  )
				continue;

			RString::size_type iEnd = xml.find_first_of( " >", iOffset );
			if( iEnd == string::npos )
			{
				if( sErrorOut.empty() ) 
					sErrorOut = ssprintf( "it must be closed with </%s>", pNode->GetName().c_str() );
				// error
				return string::npos;
			}

			RString closename;
			SetString( xml, iOffset, iEnd, &closename );
			iOffset = iEnd+1;
			if( closename == pNode->GetName() )
			{
				// wel-formed open/close
				// return '>' or ' ' after pointer
				return iOffset;
			}
			else
			{
				// not welformed open/close
				if( sErrorOut.empty() ) 
					sErrorOut = ssprintf( "'<%s> ... </%s>' is not well-formed.", pNode->GetName().c_str(), closename.c_str() );
				return string::npos;
			}
		}
		else	// Alone child Tag Loaded
		{
			if( pNode->GetAttr(XNode::TEXT_ATTRIBUTE) == NULL && iOffset < xml.size() && xml[iOffset] != chXMLTagOpen )
			{
				// Text Value 
				RString::size_type iEnd = xml.find( chXMLTagOpen, iOffset );
				if( iEnd == string::npos ) 
				{
					// error cos not exist CloseTag </TAG>
					if( sErrorOut.empty() )  
						sErrorOut = ssprintf( "it must be closed with </%s>", pNode->GetName().c_str() );
					return string::npos;
				}

				RString sValue;
				SetString( xml, iOffset, iEnd, &sValue, true );

				iOffset = iEnd;
				ReplaceEntityText( sValue, g_mapEntitiesToChars );
				pNode->AppendAttr(XNode::TEXT_ATTRIBUTE, sValue );
			}
		}
	}

	return iOffset;
}

bool GetXMLInternal( const XNode *pNode, RageFileBasic &f, bool bWriteTabs, int &iTabBase )
{
#define WRITE(x) if( f.Write(x) == -1 ) return false
	// tab
	WRITE( "\r\n" );
	if( bWriteTabs )
		for( int i = 0 ; i < iTabBase ; i++)
			WRITE( "\t" );

	// <TAG
	WRITE( "<" );
	WRITE( pNode->GetName() );

	// <TAG Attr1="Val1" 
	FOREACH_CONST_Attr( pNode, p )
	{
		if( p->first == XNode::TEXT_ATTRIBUTE )
			continue;
		RString attr( p->second->GetValue<RString>() );
		ReplaceEntityText( attr, g_mapCharsToEntities );
		WRITE( " " );
		WRITE( p->first );
		WRITE( "='" );
		WRITE( attr );
		WRITE( "'" );
	}

	if( pNode->m_childs.empty() && pNode->GetAttr(XNode::TEXT_ATTRIBUTE) == NULL )
	{
		// <TAG Attr1="Val1"/> alone tag 
		WRITE( "/>" );
	}
	else
	{
		// <TAG Attr1="Val1"> and get child
		WRITE( ">" );

		if( !pNode->m_childs.empty() )
			iTabBase++;

		FOREACH_CONST_Child( pNode, p )
			if( !GetXMLInternal( p, f, bWriteTabs, iTabBase ) )
				return false;

		// Text Value
		const XNodeValue *pText = pNode->GetAttr( XNode::TEXT_ATTRIBUTE );
		if( pText != NULL )
		{
			if( !pNode->m_childs.empty() )
			{
				WRITE( "\r\n" );
				if( bWriteTabs )
					for( int i = 0 ; i < iTabBase ; i++)
						WRITE( "\t" );
			}
			RString s;
			pText->GetValue( s );
			ReplaceEntityText( s, g_mapCharsToEntities );
			WRITE( s );
		}

		// </TAG> CloseTag
		if( !pNode->m_childs.empty() )
		{
			WRITE( "\r\n" );
			if( bWriteTabs )
				for( int i = 0 ; i < iTabBase-1 ; i++)
					WRITE( "\t" );
		}
		WRITE( "</" );
		WRITE( pNode->GetName() );
		WRITE( ">" );

		if( !pNode->m_childs.empty() )
			iTabBase--;
	}
	return true;
#undef WRITE
}
}

void XmlFileUtil::Load( XNode *pNode, const RString &sXml, RString &sErrorOut )
{
	InitEntities();
	LoadInternal( pNode, sXml, sErrorOut, 0 );
}

bool XmlFileUtil::GetXML( const XNode *pNode, RageFileBasic &f, bool bWriteTabs )
{
	int iTabBase = 0;
	InitEntities();
	return GetXMLInternal( pNode, f, bWriteTabs, iTabBase );
}

RString XmlFileUtil::GetXML( const XNode *pNode )
{
	RageFileObjMem f;
	int iTabBase = 0;
	InitEntities();
	GetXMLInternal( pNode, f, true, iTabBase );
	return f.GetString();
}

bool XmlFileUtil::SaveToFile( const XNode *pNode, RageFileBasic &f, const RString &sStylesheet, bool bWriteTabs )
{
	f.PutLine( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
	if( !sStylesheet.empty() )
		f.PutLine( "<?xml-stylesheet type=\"text/xsl\" href=\"" + sStylesheet + "\"?>" );
	int iTabBase = 0;
	InitEntities();
	if( !GetXMLInternal(pNode, f, bWriteTabs, iTabBase) )
		return false;
	f.PutLine( "" );
	if( f.Flush() == -1 )
		return false;
	return true;
}

bool XmlFileUtil::SaveToFile( const XNode *pNode, const RString &sFile, const RString &sStylesheet, bool bWriteTabs )
{
	RageFile f;
	if( !f.Open(sFile, RageFile::WRITE) )
	{
		LOG->Warn( "Couldn't open %s for writing: %s", sFile.c_str(), f.GetError().c_str() );
		return false;
	}

	return SaveToFile( pNode, f, sStylesheet, bWriteTabs );
}

#include "LuaManager.h"
#include "LuaReference.h"
class XNodeLuaValue: public XNodeValue
{
public:
	LuaReference m_Value;
	XNodeValue *Copy() const { return new XNodeLuaValue( *this ); }

	template<typename T>
	T GetValue() const { T val; GetValue(val); return val; }

	void GetValue( RString &out ) const;
	void GetValue( int &out ) const;
	void GetValue( float &out ) const;
	void GetValue( bool &out ) const;
	void GetValue( unsigned &out ) const;
	void PushValue( lua_State *L ) const;

	void SetValue( const RString &v );
	void SetValue( int v );
	void SetValue( float v );
	void SetValue( unsigned v );
	void SetValueFromStack( lua_State *L );
};

void XNodeLuaValue::PushValue( lua_State *L ) const
{
	m_Value.PushSelf( L );
}

void XNodeLuaValue::GetValue( RString &out ) const { Lua *L = LUA->Get(); PushValue( L ); LuaHelpers::Pop( L, out ); LUA->Release( L ); }
void XNodeLuaValue::GetValue( int &out ) const { Lua *L = LUA->Get(); PushValue( L ); LuaHelpers::Pop( L, out ); LUA->Release( L ); }
void XNodeLuaValue::GetValue( float &out ) const { Lua *L = LUA->Get(); PushValue( L ); LuaHelpers::Pop( L, out ); LUA->Release( L ); }
void XNodeLuaValue::GetValue( bool &out ) const { Lua *L = LUA->Get(); PushValue( L ); LuaHelpers::Pop( L, out ); LUA->Release( L ); }
void XNodeLuaValue::GetValue( unsigned &out ) const { Lua *L = LUA->Get(); PushValue( L ); float fVal; LuaHelpers::Pop( L, fVal ); out = unsigned(fVal); LUA->Release( L ); }

void XNodeLuaValue::SetValueFromStack( lua_State *L )
{
	m_Value.SetFromStack( L );
}

void XNodeLuaValue::SetValue( const RString &v ) { Lua *L = LUA->Get(); LuaHelpers::Push( L, v ); SetValueFromStack( L ); LUA->Release( L ); }
void XNodeLuaValue::SetValue( int v ) { Lua *L = LUA->Get(); LuaHelpers::Push( L, v ); SetValueFromStack( L ); LUA->Release( L ); }
void XNodeLuaValue::SetValue( float v ) { Lua *L = LUA->Get(); LuaHelpers::Push( L, v ); SetValueFromStack( L ); LUA->Release( L ); }
void XNodeLuaValue::SetValue( unsigned v ) { Lua *L = LUA->Get(); LuaHelpers::Push( L, (float) v ); SetValueFromStack( L ); LUA->Release( L ); }

namespace
{
	XNodeValue *CompileXMLNodeValue( Lua *L, const RString &sName, const XNodeValue *pValue, const RString &sFile )
	{
		RString sExpression;
		pValue->GetValue( sExpression );

		if( EndsWith(sName, "Command") )
		{
			LuaHelpers::ParseCommandList( L, sExpression, sFile );
		}
		else if( sExpression.size() > 0 && sExpression[0] == '@' )
		{
			// This is a raw string.
			sExpression.erase( 0, 1 );
			LuaHelpers::Push( L, sExpression );
		}
		else
		{
			LuaHelpers::RunExpression( L, sExpression, sFile );
		}

		XNodeLuaValue *pRet = new XNodeLuaValue;
		pRet->SetValueFromStack( L );
		return pRet;
	}
}

void XmlFileUtil::CompileXNodeTree( XNode *pNode, const RString &sFile )
{
	vector<XNode *> aToCompile;
	aToCompile.push_back( pNode );

	Lua *L = LUA->Get();
	while( aToCompile.size() )
	{
		pNode = aToCompile.back();
		aToCompile.pop_back();
		FOREACH_Child( pNode, pChild )
			aToCompile.push_back( pChild );

		FOREACH_Attr( pNode, pAttr )
		{
			XNodeValue *pValue = CompileXMLNodeValue( L, pAttr->first, pAttr->second, sFile );
			delete pAttr->second;
			pAttr->second = pValue;
		}
	}

	LUA->Release( L );
}

namespace
{
	XNode *XNodeFromTableRecursive( lua_State *L, const RString &sName, LuaReference &ProcessedTables )
	{
		XNode *pNode = new XNode( sName );

		// Set the value of the node to the table.
		{
			XNodeLuaValue *pValue = new XNodeLuaValue;
			lua_pushvalue( L, -1 );
			pValue->SetValueFromStack( L );
			pNode->AppendAttrFrom( XNode::TEXT_ATTRIBUTE, pValue );
		}

		// Iterate over the table, pulling out attributes and tables to process.
		vector<RString> NodeNamesToAdd;
		vector<LuaReference> NodesToAdd;

		/* Add array elements first, in array order, so iterating over the XNode
		 * keeps the array in order. */
		FOREACH_LUATABLEI( L, -1, i )
		{
			if( !lua_istable(L, -1) )
				continue;
			NodeNamesToAdd.push_back( ssprintf("%i", i) );
			NodesToAdd.push_back( LuaReference() );
			NodesToAdd.back().SetFromStack( L );
		}

		int iLen = NodeNamesToAdd.size();
		FOREACH_LUATABLE( L, -1 )
		{
			// If this entry is a table, add it recursively.
			if( lua_istable(L, -2) )
			{
				if( lua_isnumber(L, -1) )
				{
					/* If this number is an integer, and between [1,iLen], then
					 * we added this one already above. */
					lua_Number f = lua_tonumber( L, -1 );
					int i;
					lua_number2int(i, f);
					if( i >= 1 && i <= iLen && float(i) == f )
						continue;
				}

				RString nName;
				LuaHelpers::Pop( L, nName );
				NodeNamesToAdd.push_back( nName );
				NodesToAdd.push_back( LuaReference() );
				NodesToAdd.back().SetFromStack( L );
				continue;
			}

			RString nName;
			LuaHelpers::Pop( L, nName );

			// Otherwise, add an attribute.
			XNodeLuaValue *pValue = new XNodeLuaValue;
			pValue->SetValueFromStack( L );
			pNode->AppendAttrFrom( nName, pValue );
		}
		lua_pop( L, 1 );

		// Recursively process tables.
		for( size_t i = 0; i < NodesToAdd.size(); ++i )
		{
			const RString &sNodeName = NodeNamesToAdd[i];
			LuaReference &NodeToAdd = NodesToAdd[i];

			// Check if the table is on the stack.
			ProcessedTables.PushSelf( L );
			NodeToAdd.PushSelf( L ); // push table
			lua_gettable( L, -2 );

			bool bSawThisTableAlready = !lua_isnil(L, -1);
			lua_pop( L, 2 ); // pop lua_gettable result, ProcessedTables
			if( bSawThisTableAlready )
				continue;

			// Add the table to the stack.
			ProcessedTables.PushSelf( L );
			NodeToAdd.PushSelf( L );
			lua_pushboolean( L, true );
			lua_settable( L, -3 );
			lua_pop( L, 1 ); // pop ProcessedTables

			NodeToAdd.PushSelf( L );
			XNode *pNewNode = XNodeFromTableRecursive( L, sNodeName, ProcessedTables );
			if( pNewNode )
				pNode->AppendChild( pNewNode );

			// Remove the table from the stack.
			ProcessedTables.PushSelf( L );
			NodeToAdd.PushSelf( L );
			lua_pushnil( L );
			lua_settable( L, -3 );
			lua_pop( L, 1 ); // pop ProcessedTables
		}

		return pNode;
	}

}

/* Pop a table off of the stack, and return an XNode tree referring recursively
 * to entries in the table.
 *
 * The table may not contain table cycles; if a cycle is detected, only the first
 * table seen will have a corresponding XNode.
 *
 * Users of the resulting XNode may access the original table via PushValue. */
XNode *XmlFileUtil::XNodeFromTable( lua_State *L )
{
	/* Maintain a set of references that we've created. Tables may loop; XNode
	 * trees may not. If we encounter a cycle, skip creating an XNode for
	 * that node. */
	LuaReference ProcessedTables;
	lua_newtable( L );
	ProcessedTables.SetFromStack( L );

	return XNodeFromTableRecursive( L, "Layer", ProcessedTables );
}

/* Move nodes from pFrom into pTo which don't already exist in pTo. For
 * efficiency, nodes will be moved, not copied, so pFrom will be modified.
 * On return, the contents of pFrom will be undefined and should be deleted. */
void XmlFileUtil::MergeIniUnder( XNode *pFrom, XNode *pTo )
{
	/* Batch up nodes to move, and do them all at once, to deal sanely
	 * with the possibility of duplicate child names. */
	vector<XNodes::iterator> aToMove;

	// Iterate over each section in pFrom.
	XNodes::iterator it = pFrom->m_childs.begin();
	while( it != pFrom->m_childs.end() )
	{
		XNodes::iterator next = it;
		++next;

		// If this node doesn't exist in pTo, just move the whole node.
		XNode *pSectionNode = *it;
		XNode *pChildNode = pTo->GetChild( pSectionNode->GetName() );
		if( pChildNode == NULL )
		{
			aToMove.push_back( it );
		}
		else
		{
			FOREACH_Attr( pSectionNode, it2 )
			{
				// Don't overwrite existing nodes.
				pChildNode->AppendAttrFrom( it2->first, it2->second->Copy(), false );
			}
		}

		it = next;
	}

	/* Iterate in reverse, since erasing iterators will invalidate the
	 * iterators after it. */
	for( int i = aToMove.size()-1; i >= 0; --i )
	{
		XNode *pNode = *aToMove[i];
		pFrom->m_childs.erase( aToMove[i] );
		pTo->AppendChild( pNode );
	}
}

/*
 * (c) 2001-2006 Chris Danford, Glenn Maynard
 * All rights reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, and/or sell copies of the Software, and to permit persons to
 * whom the Software is furnished to do so, provided that the above
 * copyright notice(s) and this permission notice appear in all copies of
 * the Software and that both the above copyright notice(s) and this
 * permission notice appear in supporting documentation.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
 * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
 * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
 * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
 * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
 * PERFORMANCE OF THIS SOFTWARE.
 */
