/** 
 * @file llxuiparser.cpp
 * @brief Utility functions for handling XUI structures in XML
 *
 * $LicenseInfo:firstyear=2003&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#include "linden_common.h"

#include "llxuiparser.h"

#include "llxmlnode.h"

#ifdef LL_STANDALONE
#include <expat.h>
#else
#include "expat/expat.h"
#endif

#include <fstream>
#include <boost/tokenizer.hpp>
//#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/classic_core.hpp>

#include "lluicolor.h"

using namespace BOOST_SPIRIT_CLASSIC_NS;

const S32 MAX_STRING_ATTRIBUTE_SIZE = 40;

static 	LLInitParam::Parser::parser_read_func_map_t sXSDReadFuncs;
static 	LLInitParam::Parser::parser_write_func_map_t sXSDWriteFuncs;
static 	LLInitParam::Parser::parser_inspect_func_map_t sXSDInspectFuncs;


//
// LLXSDWriter
//
LLXSDWriter::LLXSDWriter()
: Parser(sXSDReadFuncs, sXSDWriteFuncs, sXSDInspectFuncs)
{
	registerInspectFunc<bool>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:boolean", _1, _2, _3, _4));
	registerInspectFunc<std::string>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
	registerInspectFunc<U8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedByte", _1, _2, _3, _4));
	registerInspectFunc<S8>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedByte", _1, _2, _3, _4));
	registerInspectFunc<U16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedShort", _1, _2, _3, _4));
	registerInspectFunc<S16>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:signedShort", _1, _2, _3, _4));
	registerInspectFunc<U32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:unsignedInt", _1, _2, _3, _4));
	registerInspectFunc<S32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:integer", _1, _2, _3, _4));
	registerInspectFunc<F32>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:float", _1, _2, _3, _4));
	registerInspectFunc<F64>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:double", _1, _2, _3, _4));
	registerInspectFunc<LLColor4>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
	registerInspectFunc<LLUIColor>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
	registerInspectFunc<LLUUID>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
	registerInspectFunc<LLSD>(boost::bind(&LLXSDWriter::writeAttribute, this, "xs:string", _1, _2, _3, _4));
}

void LLXSDWriter::writeXSD(const std::string& type_name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace)
{
	mSchemaNode = node;
	node->setName("xs:schema");
	node->createChild("attributeFormDefault", true)->setStringValue("unqualified");
	node->createChild("elementFormDefault", true)->setStringValue("qualified");
	node->createChild("targetNamespace", true)->setStringValue(xml_namespace);
	node->createChild("xmlns:xs", true)->setStringValue("http://www.w3.org/2001/XMLSchema");
	node->createChild("xmlns", true)->setStringValue(xml_namespace);

	node = node->createChild("xs:complexType", false);
	node->createChild("name", true)->setStringValue(type_name);
	node->createChild("mixed", true)->setStringValue("true");

	mAttributeNode = node;
	mElementNode = node->createChild("xs:choice", false);
	mElementNode->createChild("minOccurs", true)->setStringValue("0");
	mElementNode->createChild("maxOccurs", true)->setStringValue("unbounded");
	block.inspectBlock(*this);

	// duplicate element choices
	LLXMLNodeList children;
	mElementNode->getChildren("xs:element", children, FALSE);
	for (LLXMLNodeList::iterator child_it = children.begin(); child_it != children.end(); ++child_it)
	{
		LLXMLNodePtr child_copy = child_it->second->deepCopy();
		std::string child_name;
		child_copy->getAttributeString("name", child_name);
		child_copy->setAttributeString("name", type_name + "." + child_name);
		mElementNode->addChild(child_copy);
	}

	LLXMLNodePtr element_declaration_node = mSchemaNode->createChild("xs:element", false);
	element_declaration_node->createChild("name", true)->setStringValue(type_name);
	element_declaration_node->createChild("type", true)->setStringValue(type_name);
}

void LLXSDWriter::writeAttribute(const std::string& type, const Parser::name_stack_t& stack, S32 min_count, S32 max_count, const std::vector<std::string>* possible_values)
{
	name_stack_t non_empty_names;
	std::string attribute_name;
	for (name_stack_t::const_iterator it = stack.begin();
		it != stack.end();
		++it)
	{
		const std::string& name = it->first;
		if (!name.empty())
		{
			non_empty_names.push_back(*it);
		}
	}

	for (name_stack_t::const_iterator it = non_empty_names.begin();
		it != non_empty_names.end();
		++it)
	{
		if (!attribute_name.empty())
		{
			attribute_name += ".";
		}
		attribute_name += it->first;
	}

	// only flag non-nested attributes as mandatory, nested attributes have variant syntax
	// that can't be properly constrained in XSD
	// e.g. <foo mandatory.value="bar"/> vs <foo><mandatory value="bar"/></foo>
	bool attribute_mandatory = min_count == 1 && max_count == 1 && non_empty_names.size() == 1;

	// don't bother supporting "Multiple" params as xml attributes
	if (max_count <= 1)
	{
		// add compound attribute to root node
		addAttributeToSchema(mAttributeNode, attribute_name, type, attribute_mandatory, possible_values);
	}

	// now generated nested elements for compound attributes
	if (non_empty_names.size() > 1 && !attribute_mandatory)
	{
		std::string element_name;

		// traverse all but last element, leaving that as an attribute name
		name_stack_t::const_iterator end_it = non_empty_names.end();
		end_it--;

		for (name_stack_t::const_iterator it = non_empty_names.begin();
			it != end_it;
			++it)
		{
			if (it != non_empty_names.begin())
			{
				element_name += ".";
			}
			element_name += it->first;
		}

		std::string short_attribute_name = non_empty_names.back().first;

		LLXMLNodePtr complex_type_node;

		// find existing element node here, starting at tail of child list
		if (mElementNode->mChildren.notNull())
		{
			for(LLXMLNodePtr element = mElementNode->mChildren->tail;
				element.notNull(); 
				element = element->mPrev)
			{
				std::string name;
				if(element->getAttributeString("name", name) && name == element_name)
				{
					complex_type_node = element->mChildren->head;
					break;
				}
			}
		}
		//create complex_type node
		//
		//<xs:element
        //    maxOccurs="1"
        //    minOccurs="0"
        //    name="name">
        //       <xs:complexType>
        //       </xs:complexType>
        //</xs:element>
		if(complex_type_node.isNull())
		{
			complex_type_node = mElementNode->createChild("xs:element", false);

			complex_type_node->createChild("minOccurs", true)->setIntValue(min_count);
			complex_type_node->createChild("maxOccurs", true)->setIntValue(max_count);
			complex_type_node->createChild("name",		true)->setStringValue(element_name);
			complex_type_node = complex_type_node->createChild("xs:complexType", false);
		}

		addAttributeToSchema(complex_type_node, short_attribute_name, type, false, possible_values);
	}
}

void LLXSDWriter::addAttributeToSchema(LLXMLNodePtr type_declaration_node, const std::string& attribute_name, const std::string& type, bool mandatory, const std::vector<std::string>* possible_values)
{
	if (!attribute_name.empty())
	{
		LLXMLNodePtr new_enum_type_node;
		if (possible_values != NULL)
		{
			// custom attribute type, for example
			//<xs:simpleType>
			 // <xs:restriction
			 //    base="xs:string">
			 //     <xs:enumeration
			 //      value="a" />
			 //     <xs:enumeration
			 //      value="b" />
			 //   </xs:restriction>
			 // </xs:simpleType>
			new_enum_type_node = new LLXMLNode("xs:simpleType", false);

			LLXMLNodePtr restriction_node = new_enum_type_node->createChild("xs:restriction", false);
			restriction_node->createChild("base", true)->setStringValue("xs:string");

			for (std::vector<std::string>::const_iterator it = possible_values->begin();
				it != possible_values->end();
				++it)
			{
				LLXMLNodePtr enum_node = restriction_node->createChild("xs:enumeration", false);
				enum_node->createChild("value", true)->setStringValue(*it);
			}
		}

		string_set_t& attributes_written = mAttributesWritten[type_declaration_node];

		string_set_t::iterator found_it = attributes_written.lower_bound(attribute_name);

		// attribute not yet declared
		if (found_it == attributes_written.end() || attributes_written.key_comp()(attribute_name, *found_it))
		{
			attributes_written.insert(found_it, attribute_name);

			LLXMLNodePtr attribute_node = type_declaration_node->createChild("xs:attribute", false);

			// attribute name
			attribute_node->createChild("name", true)->setStringValue(attribute_name);

			if (new_enum_type_node.notNull())
			{
				attribute_node->addChild(new_enum_type_node);
			}
			else
			{
				// simple attribute type
				attribute_node->createChild("type", true)->setStringValue(type);
			}

			// required or optional
			attribute_node->createChild("use", true)->setStringValue(mandatory ? "required" : "optional");
		}
		 // attribute exists...handle collision of same name attributes with potentially different types
		else
		{
			LLXMLNodePtr attribute_declaration;
			if (type_declaration_node.notNull())
			{
				for(LLXMLNodePtr node = type_declaration_node->mChildren->tail; 
					node.notNull(); 
					node = node->mPrev)
				{
					std::string name;
					if (node->getAttributeString("name", name) && name == attribute_name)
					{
						attribute_declaration = node;
						break;
					}
				}
			}

			bool new_type_is_enum = new_enum_type_node.notNull();
			bool existing_type_is_enum = !attribute_declaration->hasAttribute("type");

			// either type is enum, revert to string in collision
			// don't bother to check for enum equivalence
			if (new_type_is_enum || existing_type_is_enum)
			{
				if (attribute_declaration->hasAttribute("type"))
				{
					attribute_declaration->setAttributeString("type", "xs:string");
				}
				else
				{
					attribute_declaration->createChild("type", true)->setStringValue("xs:string");
				}
				attribute_declaration->deleteChildren("xs:simpleType");
			}
			else 
			{
				// check for collision of different standard types
				std::string existing_type;
				attribute_declaration->getAttributeString("type", existing_type);
				// if current type is not the same as the new type, revert to strnig
				if (existing_type != type)
				{
					// ...than use most general type, string
					attribute_declaration->setAttributeString("type", "string");
				}
			}
		}
	}
}

//
// LLXUIXSDWriter
//
void LLXUIXSDWriter::writeXSD(const std::string& type_name, const std::string& path, const LLInitParam::BaseBlock& block)
{
	std::string file_name(path);
	file_name += type_name + ".xsd";
	LLXMLNodePtr root_nodep = new LLXMLNode();

	LLXSDWriter::writeXSD(type_name, root_nodep, block, "http://www.lindenlab.com/xui");

	// add includes for all possible children
	const std::type_info* type = *LLWidgetTypeRegistry::instance().getValue(type_name);
	const widget_registry_t* widget_registryp = LLChildRegistryRegistry::instance().getValue(type);

	// add choices for valid children
	if (widget_registryp)
	{
		// add include declarations for all valid children
		for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
		     it != widget_registryp->currentRegistrar().endItems();
		     ++it)
		{
			std::string widget_name = it->first;
			if (widget_name == type_name)
			{
				continue;
			}
			LLXMLNodePtr nodep = new LLXMLNode("xs:include", false);
			nodep->createChild("schemaLocation", true)->setStringValue(widget_name + ".xsd");
			
			// add to front of schema
			mSchemaNode->addChild(nodep, mSchemaNode);
		}

		for (widget_registry_t::Registrar::registry_map_t::const_iterator it = widget_registryp->currentRegistrar().beginItems();
			it != widget_registryp->currentRegistrar().endItems();
			++it)
		{
			std::string widget_name = it->first;
			//<xs:element name="widget_name" type="widget_name">
			LLXMLNodePtr widget_node = mElementNode->createChild("xs:element", false);
			widget_node->createChild("name", true)->setStringValue(widget_name);
			widget_node->createChild("type", true)->setStringValue(widget_name);
		}
	}

	LLFILE* xsd_file = LLFile::fopen(file_name.c_str(), "w");
	LLXMLNode::writeHeaderToFile(xsd_file);
	root_nodep->writeToFile(xsd_file);
	fclose(xsd_file);
}

static 	LLInitParam::Parser::parser_read_func_map_t sXUIReadFuncs;
static 	LLInitParam::Parser::parser_write_func_map_t sXUIWriteFuncs;
static 	LLInitParam::Parser::parser_inspect_func_map_t sXUIInspectFuncs;

//
// LLXUIParser
//
LLXUIParser::LLXUIParser()
:	Parser(sXUIReadFuncs, sXUIWriteFuncs, sXUIInspectFuncs),
	mLastWriteGeneration(-1),
	mCurReadDepth(0)
{
	if (sXUIReadFuncs.empty())
	{
		registerParserFuncs<bool>(readBoolValue, writeBoolValue);
		registerParserFuncs<std::string>(readStringValue, writeStringValue);
		registerParserFuncs<U8>(readU8Value, writeU8Value);
		registerParserFuncs<S8>(readS8Value, writeS8Value);
		registerParserFuncs<U16>(readU16Value, writeU16Value);
		registerParserFuncs<S16>(readS16Value, writeS16Value);
		registerParserFuncs<U32>(readU32Value, writeU32Value);
		registerParserFuncs<S32>(readS32Value, writeS32Value);
		registerParserFuncs<F32>(readF32Value, writeF32Value);
		registerParserFuncs<F64>(readF64Value, writeF64Value);
		registerParserFuncs<LLColor4>(readColor4Value, writeColor4Value);
		registerParserFuncs<LLUIColor>(readUIColorValue, writeUIColorValue);
		registerParserFuncs<LLUUID>(readUUIDValue, writeUUIDValue);
		registerParserFuncs<LLSD>(readSDValue, writeSDValue);
	}
}

static LLFastTimer::DeclareTimer FTM_PARSE_XUI("XUI Parsing");

void LLXUIParser::readXUI(LLXMLNodePtr node, LLInitParam::BaseBlock& block, const std::string& filename, bool silent)
{
	LLFastTimer timer(FTM_PARSE_XUI);
	mNameStack.clear();
	mRootNodeName = node->getName()->mString;
	mCurFileName = filename;
	mCurReadDepth = 0;
	setParseSilently(silent);

	if (node.isNull())
	{
		parserWarning("Invalid node");
	}
	else
	{
		readXUIImpl(node, block);
	}
}

bool LLXUIParser::readXUIImpl(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
{
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep(".");

	bool values_parsed = false;

	// submit attributes for current node
	values_parsed |= readAttributes(nodep, block);

	// treat text contents of xml node as "value" parameter
	std::string text_contents = nodep->getSanitizedValue();
	if (!text_contents.empty())
	{
		mCurReadNode = nodep;
		mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
		// child nodes are not necessarily valid parameters (could be a child widget)
		// so don't complain once we've recursed
		bool silent = mCurReadDepth > 0;
		if (!block.submitValue(mNameStack, *this, true))
		{
			mNameStack.pop_back();
			block.submitValue(mNameStack, *this, silent);
		}
		else
		{
			mNameStack.pop_back();
		}
	}

	// then traverse children
	// child node must start with last name of parent node (our "scope")
	// for example: "<button><button.param nested_param1="foo"><param.nested_param2 nested_param3="bar"/></button.param></button>"
	// which equates to the following nesting:
	// button
	//     param
	//         nested_param1
	//         nested_param2
	//             nested_param3	
	mCurReadDepth++;
	for(LLXMLNodePtr childp = nodep->getFirstChild(); childp.notNull();)
	{
		std::string child_name(childp->getName()->mString);
		S32 num_tokens_pushed = 0;

		// for non "dotted" child nodes	check to see if child node maps to another widget type
		// and if not, treat as a child element of the current node
		// e.g. <button><rect left="10"/></button> will interpret <rect> as "button.rect"
		// since there is no widget named "rect"
		if (child_name.find(".") == std::string::npos) 
		{
			mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
			num_tokens_pushed++;
		}
		else
		{
			// parse out "dotted" name into individual tokens
			tokenizer name_tokens(child_name, sep);

			tokenizer::iterator name_token_it = name_tokens.begin();
			if(name_token_it == name_tokens.end()) 
			{
				childp = childp->getNextSibling();
				continue;
			}

			// check for proper nesting
			if (mNameStack.empty())
			{
				if (*name_token_it != mRootNodeName)
				{
					childp = childp->getNextSibling();
					continue;
				}
			}
			else if(mNameStack.back().first != *name_token_it)
			{
				childp = childp->getNextSibling();
				continue;
			}

			// now ignore first token
			++name_token_it; 

			// copy remaining tokens on to our running token list
			for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
			{
				mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
				num_tokens_pushed++;
			}
		}

		// recurse and visit children XML nodes
		if(readXUIImpl(childp, block))
		{
			// child node successfully parsed, remove from DOM

			values_parsed = true;
			LLXMLNodePtr node_to_remove = childp;
			childp = childp->getNextSibling();

			nodep->deleteChild(node_to_remove);
		}
		else
		{
			childp = childp->getNextSibling();
		}

		while(num_tokens_pushed-- > 0)
		{
			mNameStack.pop_back();
		}
	}
	mCurReadDepth--;
	return values_parsed;
}

bool LLXUIParser::readAttributes(LLXMLNodePtr nodep, LLInitParam::BaseBlock& block)
{
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep(".");

	bool any_parsed = false;

	for(LLXMLAttribList::const_iterator attribute_it = nodep->mAttributes.begin(); 
		attribute_it != nodep->mAttributes.end(); 
		++attribute_it)
	{
		S32 num_tokens_pushed = 0;
		std::string attribute_name(attribute_it->first->mString);
		mCurReadNode = attribute_it->second;

		tokenizer name_tokens(attribute_name, sep);
		// copy remaining tokens on to our running token list
		for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
		{
			mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
			num_tokens_pushed++;
		}

		// child nodes are not necessarily valid attributes, so don't complain once we've recursed
		bool silent = mCurReadDepth > 0;
		any_parsed |= block.submitValue(mNameStack, *this, silent);
		
		while(num_tokens_pushed-- > 0)
		{
			mNameStack.pop_back();
		}
	}

	return any_parsed;
}

void LLXUIParser::writeXUI(LLXMLNodePtr node, const LLInitParam::BaseBlock &block, const LLInitParam::BaseBlock* diff_block)
{
	mWriteRootNode = node;
	block.serializeBlock(*this, Parser::name_stack_t(), diff_block);
	mOutNodes.clear();
}

// go from a stack of names to a specific XML node
LLXMLNodePtr LLXUIParser::getNode(const name_stack_t& stack)
{
	name_stack_t name_stack;
	for (name_stack_t::const_iterator it = stack.begin();
		it != stack.end();
		++it)
	{
		if (!it->first.empty())
		{
			name_stack.push_back(*it);
		}
	}

	LLXMLNodePtr out_node = mWriteRootNode;

	name_stack_t::const_iterator next_it = name_stack.begin();
	for (name_stack_t::const_iterator it = name_stack.begin();
		it != name_stack.end();
		it = next_it)
	{
		++next_it;
		if (it->first.empty())
		{
			continue;
		}

		out_nodes_t::iterator found_it = mOutNodes.lower_bound(it->second);

		// node with this name not yet written
		if (found_it == mOutNodes.end() || mOutNodes.key_comp()(found_it->first, it->second))
		{
			// make an attribute if we are the last element on the name stack
			bool is_attribute = next_it == name_stack.end();
			LLXMLNodePtr new_node = new LLXMLNode(it->first.c_str(), is_attribute);
			out_node->addChild(new_node);
			mOutNodes.insert(found_it, std::make_pair(it->second, new_node));
			out_node = new_node;
		}
		else
		{
			out_node = found_it->second;
		}
	}

	return (out_node == mWriteRootNode ? LLXMLNodePtr(NULL) : out_node);
}


bool LLXUIParser::readBoolValue(Parser& parser, void* val_ptr)
{
	S32 value;
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	bool success = self.mCurReadNode->getBoolValue(1, &value);
	*((bool*)val_ptr) = (value != FALSE);
	return success;
}

bool LLXUIParser::writeBoolValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setBoolValue(*((bool*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readStringValue(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	*((std::string*)val_ptr) = self.mCurReadNode->getSanitizedValue();
	return true;
}

bool LLXUIParser::writeStringValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		const std::string* string_val = reinterpret_cast<const std::string*>(val_ptr);
		if (string_val->find('\n') != std::string::npos 
			|| string_val->size() > MAX_STRING_ATTRIBUTE_SIZE)
		{
			// don't write strings with newlines into attributes
			std::string attribute_name = node->getName()->mString;
			LLXMLNodePtr parent_node = node->mParent;
			parent_node->deleteChild(node);
			// write results in text contents of node
			if (attribute_name == "value")
			{
				// "value" is implicit, just write to parent
				node = parent_node;
			}
			else
			{
				// create a child that is not an attribute, but with same name
				node = parent_node->createChild(attribute_name.c_str(), false);
			}
		}
		node->setStringValue(*string_val);
		return true;
	}
	return false;
}

bool LLXUIParser::readU8Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	return self.mCurReadNode->getByteValue(1, (U8*)val_ptr);
}

bool LLXUIParser::writeU8Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setUnsignedValue(*((U8*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readS8Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	S32 value;
	if(self.mCurReadNode->getIntValue(1, &value))
	{
		*((S8*)val_ptr) = value;
		return true;
	}
	return false;
}

bool LLXUIParser::writeS8Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setIntValue(*((S8*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readU16Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	U32 value;
	if(self.mCurReadNode->getUnsignedValue(1, &value))
	{
		*((U16*)val_ptr) = value;
		return true;
	}
	return false;
}

bool LLXUIParser::writeU16Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setUnsignedValue(*((U16*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readS16Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	S32 value;
	if(self.mCurReadNode->getIntValue(1, &value))
	{
		*((S16*)val_ptr) = value;
		return true;
	}
	return false;
}

bool LLXUIParser::writeS16Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setIntValue(*((S16*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readU32Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	return self.mCurReadNode->getUnsignedValue(1, (U32*)val_ptr);
}

bool LLXUIParser::writeU32Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setUnsignedValue(*((U32*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readS32Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	return self.mCurReadNode->getIntValue(1, (S32*)val_ptr);
}

bool LLXUIParser::writeS32Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setIntValue(*((S32*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readF32Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	return self.mCurReadNode->getFloatValue(1, (F32*)val_ptr);
}

bool LLXUIParser::writeF32Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setFloatValue(*((F32*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readF64Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	return self.mCurReadNode->getDoubleValue(1, (F64*)val_ptr);
}

bool LLXUIParser::writeF64Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setDoubleValue(*((F64*)val_ptr));
		return true;
	}
	return false;
}

bool LLXUIParser::readColor4Value(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLColor4* colorp = (LLColor4*)val_ptr;
	if(self.mCurReadNode->getFloatValue(4, colorp->mV) >= 3)
	{
		return true;
	}

	return false;
}

bool LLXUIParser::writeColor4Value(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		LLColor4 color = *((LLColor4*)val_ptr);
		node->setFloatValue(4, color.mV);
		return true;
	}
	return false;
}

bool LLXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLUIColor* param = (LLUIColor*)val_ptr;
	LLColor4 color;
	bool success =  self.mCurReadNode->getFloatValue(4, color.mV) >= 3;
	if (success)
	{
		param->set(color);
		return true;
	}
	return false;
}

bool LLXUIParser::writeUIColorValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		LLUIColor color = *((LLUIColor*)val_ptr);
		//RN: don't write out the color that is represented by a function
		// rely on param block exporting to get the reference to the color settings
		if (color.isReference()) return false;
		node->setFloatValue(4, color.get().mV);
		return true;
	}
	return false;
}

bool LLXUIParser::readUUIDValue(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLUUID temp_id;
	// LLUUID::set is destructive, so use temporary value
	if (temp_id.set(self.mCurReadNode->getSanitizedValue()))
	{
		*(LLUUID*)(val_ptr) = temp_id;
		return true;
	}
	return false;
}

bool LLXUIParser::writeUUIDValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		node->setStringValue(((LLUUID*)val_ptr)->asString());
		return true;
	}
	return false;
}

bool LLXUIParser::readSDValue(Parser& parser, void* val_ptr)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);
	*((LLSD*)val_ptr) = LLSD(self.mCurReadNode->getSanitizedValue());
	return true;
}

bool LLXUIParser::writeSDValue(Parser& parser, const void* val_ptr, const name_stack_t& stack)
{
	LLXUIParser& self = static_cast<LLXUIParser&>(parser);

	LLXMLNodePtr node = self.getNode(stack);
	if (node.notNull())
	{
		std::string string_val = ((LLSD*)val_ptr)->asString();
		if (string_val.find('\n') != std::string::npos || string_val.size() > MAX_STRING_ATTRIBUTE_SIZE)
		{
			// don't write strings with newlines into attributes
			std::string attribute_name = node->getName()->mString;
			LLXMLNodePtr parent_node = node->mParent;
			parent_node->deleteChild(node);
			// write results in text contents of node
			if (attribute_name == "value")
			{
				// "value" is implicit, just write to parent
				node = parent_node;
			}
			else
			{
				node = parent_node->createChild(attribute_name.c_str(), false);
			}
		}

		node->setStringValue(string_val);
		return true;
	}
	return false;
}

/*virtual*/ std::string LLXUIParser::getCurrentElementName()
{
	std::string full_name;
	for (name_stack_t::iterator it = mNameStack.begin();	
		it != mNameStack.end();
		++it)
	{
		full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
	}

	return full_name;
}

void LLXUIParser::parserWarning(const std::string& message)
{
#ifdef LL_WINDOWS
	// use Visual Studo friendly formatting of output message for easy access to originating xml
	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", mCurFileName.c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
	utf16str += '\n';
	OutputDebugString(utf16str.c_str());
#else
	Parser::parserWarning(message);
#endif
}

void LLXUIParser::parserError(const std::string& message)
{
#ifdef LL_WINDOWS
	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", mCurFileName.c_str(), mCurReadNode->getLineNumber(), message.c_str()).c_str());
	utf16str += '\n';
	OutputDebugString(utf16str.c_str());
#else
	Parser::parserError(message);
#endif
}


//
// LLSimpleXUIParser
//

struct ScopedFile
{
	ScopedFile( const std::string& filename, const char* accessmode )
	{
		mFile = LLFile::fopen(filename, accessmode);
	}

	~ScopedFile()
	{
		fclose(mFile);
		mFile = NULL;
	}

	S32 getRemainingBytes()
	{
		if (!isOpen()) return 0;

		S32 cur_pos = ftell(mFile);
		fseek(mFile, 0L, SEEK_END);
		S32 file_size = ftell(mFile);
		fseek(mFile, cur_pos, SEEK_SET);
		return file_size - cur_pos;
	}

	bool isOpen() { return mFile != NULL; }

	LLFILE* mFile;
};
static 	LLInitParam::Parser::parser_read_func_map_t sSimpleXUIReadFuncs;
static 	LLInitParam::Parser::parser_write_func_map_t sSimpleXUIWriteFuncs;
static 	LLInitParam::Parser::parser_inspect_func_map_t sSimpleXUIInspectFuncs;

LLSimpleXUIParser::LLSimpleXUIParser(LLSimpleXUIParser::element_start_callback_t element_cb)
:	Parser(sSimpleXUIReadFuncs, sSimpleXUIWriteFuncs, sSimpleXUIInspectFuncs),
	mLastWriteGeneration(-1),
	mCurReadDepth(0),
	mElementCB(element_cb)
{
	if (sSimpleXUIReadFuncs.empty())
	{
		registerParserFuncs<bool>(readBoolValue);
		registerParserFuncs<std::string>(readStringValue);
		registerParserFuncs<U8>(readU8Value);
		registerParserFuncs<S8>(readS8Value);
		registerParserFuncs<U16>(readU16Value);
		registerParserFuncs<S16>(readS16Value);
		registerParserFuncs<U32>(readU32Value);
		registerParserFuncs<S32>(readS32Value);
		registerParserFuncs<F32>(readF32Value);
		registerParserFuncs<F64>(readF64Value);
		registerParserFuncs<LLColor4>(readColor4Value);
		registerParserFuncs<LLUIColor>(readUIColorValue);
		registerParserFuncs<LLUUID>(readUUIDValue);
		registerParserFuncs<LLSD>(readSDValue);
	}
}

LLSimpleXUIParser::~LLSimpleXUIParser()
{
}


bool LLSimpleXUIParser::readXUI(const std::string& filename, LLInitParam::BaseBlock& block, bool silent)
{
	LLFastTimer timer(FTM_PARSE_XUI);

	mParser = XML_ParserCreate(NULL);
	XML_SetUserData(mParser, this);
	XML_SetElementHandler(			mParser,	startElementHandler, endElementHandler);
	XML_SetCharacterDataHandler(	mParser,	characterDataHandler);

	mOutputStack.push_back(std::make_pair(&block, 0));
	mNameStack.clear();
	mCurFileName = filename;
	mCurReadDepth = 0;
	setParseSilently(silent);

	ScopedFile file(filename, "rb");
	if( !file.isOpen() )
	{
		LL_WARNS("ReadXUI") << "Unable to open file " << filename << LL_ENDL;
		return false;
	}

	S32 bytes_read = 0;
	
	S32 buffer_size = file.getRemainingBytes();
	void* buffer = XML_GetBuffer(mParser, buffer_size);
	if( !buffer ) 
	{
		LL_WARNS("ReadXUI") << "Unable to allocate XML buffer while reading file " << filename << LL_ENDL;
		XML_ParserFree( mParser );
		return false;
	}

	bytes_read = (S32)fread(buffer, 1, buffer_size, file.mFile);
	if( bytes_read <= 0 )
	{
		LL_WARNS("ReadXUI") << "Error while reading file  " << filename << LL_ENDL;
		XML_ParserFree( mParser );
		return false;
	}
	
	if( !XML_ParseBuffer(mParser, bytes_read, TRUE ) )
	{
		LL_WARNS("ReadXUI") << "Error while parsing file  " << filename << LL_ENDL;
		XML_ParserFree( mParser );
		return false;
	}

	XML_ParserFree( mParser );
	return true;
}

void LLSimpleXUIParser::startElementHandler(void *userData, const char *name, const char **atts)
{
	LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
	self->startElement(name, atts);
}

void LLSimpleXUIParser::endElementHandler(void *userData, const char *name)
{
	LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
	self->endElement(name);
}

void LLSimpleXUIParser::characterDataHandler(void *userData, const char *s, int len)
{
	LLSimpleXUIParser* self = reinterpret_cast<LLSimpleXUIParser*>(userData);
	self->characterData(s, len);
}

void LLSimpleXUIParser::startElement(const char *name, const char **atts)
{
	processText();

	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep(".");

	if (mElementCB) 
	{
		LLInitParam::BaseBlock* blockp = mElementCB(*this, name);
		if (blockp)
		{
			mOutputStack.push_back(std::make_pair(blockp, 0));
		}
	}

	mOutputStack.back().second++;
	S32 num_tokens_pushed = 0;
	std::string child_name(name);

	if (mOutputStack.back().second == 1)
	{	// root node for this block
		mScope.push_back(child_name);
	}
	else
	{	// compound attribute
		if (child_name.find(".") == std::string::npos) 
		{
			mNameStack.push_back(std::make_pair(child_name, newParseGeneration()));
			num_tokens_pushed++;
			mScope.push_back(child_name);
		}
		else
		{
			// parse out "dotted" name into individual tokens
			tokenizer name_tokens(child_name, sep);

			tokenizer::iterator name_token_it = name_tokens.begin();
			if(name_token_it == name_tokens.end()) 
			{
				return;
			}

			// check for proper nesting
			if(!mScope.empty() && *name_token_it != mScope.back())
			{
				return;
			}

			// now ignore first token
			++name_token_it; 

			// copy remaining tokens on to our running token list
			for(tokenizer::iterator token_to_push = name_token_it; token_to_push != name_tokens.end(); ++token_to_push)
			{
				mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
				num_tokens_pushed++;
			}
			mScope.push_back(mNameStack.back().first);
		}
	}

	mTokenSizeStack.push_back(num_tokens_pushed);
	readAttributes(atts);
}

bool LLSimpleXUIParser::readAttributes(const char **atts)
{
	typedef boost::tokenizer<boost::char_separator<char> > tokenizer;
	boost::char_separator<char> sep(".");

	bool any_parsed = false;
	for(S32 i = 0; atts[i] && atts[i+1]; i += 2 )
	{
		std::string attribute_name(atts[i]);
		mCurAttributeValueBegin = atts[i+1];
		
		S32 num_tokens_pushed = 0;
		tokenizer name_tokens(attribute_name, sep);
		// copy remaining tokens on to our running token list
		for(tokenizer::iterator token_to_push = name_tokens.begin(); token_to_push != name_tokens.end(); ++token_to_push)
		{
			mNameStack.push_back(std::make_pair(*token_to_push, newParseGeneration()));
			num_tokens_pushed++;
		}

		// child nodes are not necessarily valid attributes, so don't complain once we've recursed
		any_parsed |= mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
		
		while(num_tokens_pushed-- > 0)
		{
			mNameStack.pop_back();
		}
	}
	return any_parsed;
}

void LLSimpleXUIParser::processText()
{
	if (!mTextContents.empty())
	{
		LLStringUtil::trim(mTextContents);
		if (!mTextContents.empty())
		{
			mNameStack.push_back(std::make_pair(std::string("value"), newParseGeneration()));
			mCurAttributeValueBegin = mTextContents.c_str();
			mOutputStack.back().first->submitValue(mNameStack, *this, mParseSilently);
			mNameStack.pop_back();
		}
		mTextContents.clear();
	}
}

void LLSimpleXUIParser::endElement(const char *name)
{
	processText();

	if (--mOutputStack.back().second == 0)
	{
		if (mOutputStack.empty())
		{
			LL_ERRS("ReadXUI") << "Parameter block output stack popped while empty." << LL_ENDL;
		}
		mOutputStack.pop_back();
	}

	S32 num_tokens_to_pop = mTokenSizeStack.back();
	mTokenSizeStack.pop_back();
	while(num_tokens_to_pop-- > 0)
	{
		mNameStack.pop_back();
	}
	mScope.pop_back();
}

void LLSimpleXUIParser::characterData(const char *s, int len)
{
	mTextContents += std::string(s, len);
}


/*virtual*/ std::string LLSimpleXUIParser::getCurrentElementName()
{
	std::string full_name;
	for (name_stack_t::iterator it = mNameStack.begin();	
		it != mNameStack.end();
		++it)
	{
		full_name += it->first + "."; // build up dotted names: "button.param.nestedparam."
	}

	return full_name;
}

const S32 LINE_NUMBER_HERE = 0;

void LLSimpleXUIParser::parserWarning(const std::string& message)
{
#ifdef LL_WINDOWS
	// use Visual Studo friendly formatting of output message for easy access to originating xml
	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", mCurFileName.c_str(), LINE_NUMBER_HERE, message.c_str()).c_str());
	utf16str += '\n';
	OutputDebugString(utf16str.c_str());
#else
	Parser::parserWarning(message);
#endif
}

void LLSimpleXUIParser::parserError(const std::string& message)
{
#ifdef LL_WINDOWS
	llutf16string utf16str = utf8str_to_utf16str(llformat("%s(%d):\t%s", mCurFileName.c_str(), LINE_NUMBER_HERE, message.c_str()).c_str());
	utf16str += '\n';
	OutputDebugString(utf16str.c_str());
#else
	Parser::parserError(message);
#endif
}

bool LLSimpleXUIParser::readBoolValue(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	if (!strcmp(self.mCurAttributeValueBegin, "true")) 
	{
		*((bool*)val_ptr) = true;
		return true;
	}
	else if (!strcmp(self.mCurAttributeValueBegin, "false"))
	{
		*((bool*)val_ptr) = false;
		return true;
	}

	return false;
}

bool LLSimpleXUIParser::readStringValue(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	*((std::string*)val_ptr) = self.mCurAttributeValueBegin;
	return true;
}

bool LLSimpleXUIParser::readU8Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U8*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS8Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S8*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readU16Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U16*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS16Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S16*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readU32Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, uint_p[assign_a(*(U32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readS32Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, int_p[assign_a(*(S32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readF32Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F32*)val_ptr)]).full;
}

bool LLSimpleXUIParser::readF64Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	return parse(self.mCurAttributeValueBegin, real_p[assign_a(*(F64*)val_ptr)]).full;
}
	
bool LLSimpleXUIParser::readColor4Value(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	LLColor4 value;

	if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
	{
		*(LLColor4*)(val_ptr) = value;
		return true;
	}
	return false;
}

bool LLSimpleXUIParser::readUIColorValue(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	LLColor4 value;
	LLUIColor* colorp = (LLUIColor*)val_ptr;

	if (parse(self.mCurAttributeValueBegin, real_p[assign_a(value.mV[0])] >> real_p[assign_a(value.mV[1])] >> real_p[assign_a(value.mV[2])] >> real_p[assign_a(value.mV[3])], space_p).full)
	{
		colorp->set(value);
		return true;
	}
	return false;
}

bool LLSimpleXUIParser::readUUIDValue(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	LLUUID temp_id;
	// LLUUID::set is destructive, so use temporary value
	if (temp_id.set(std::string(self.mCurAttributeValueBegin)))
	{
		*(LLUUID*)(val_ptr) = temp_id;
		return true;
	}
	return false;
}

bool LLSimpleXUIParser::readSDValue(Parser& parser, void* val_ptr)
{
	LLSimpleXUIParser& self = static_cast<LLSimpleXUIParser&>(parser);
	*((LLSD*)val_ptr) = LLSD(self.mCurAttributeValueBegin);
	return true;
}
