/*-------------------------------------------------------------------------
 * drawElements Quality Program Test Executor
 * ------------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief XML Writer.
 *//*--------------------------------------------------------------------*/

#include "xeXMLWriter.hpp"

#include <cstring>

namespace xe
{
namespace xml
{

const Writer::EndElementType Writer::EndElement = Writer::EndElementType();

inline const char* getEscapeEntity (char ch)
{
	switch (ch)
	{
		case '<':	return "&lt;";
		case '>':	return "&gt;";
		case '&':	return "&amp;";
		case '\'':	return "&apos;";
		case '"':	return "&quot;";

		// Non-printable characters.
		case 0:		return "&lt;NUL&gt;";
		case 1:		return "&lt;SOH&gt;";
		case 2:		return "&lt;STX&gt;";
		case 3:		return "&lt;ETX&gt;";
		case 4:		return "&lt;EOT&gt;";
		case 5:		return "&lt;ENQ&gt;";
		case 6:		return "&lt;ACK&gt;";
		case 7:		return "&lt;BEL&gt;";
		case 8:		return "&lt;BS&gt;";
		case 11:	return "&lt;VT&gt;";
		case 12:	return "&lt;FF&gt;";
		case 14:	return "&lt;SO&gt;";
		case 15:	return "&lt;SI&gt;";
		case 16:	return "&lt;DLE&gt;";
		case 17:	return "&lt;DC1&gt;";
		case 18:	return "&lt;DC2&gt;";
		case 19:	return "&lt;DC3&gt;";
		case 20:	return "&lt;DC4&gt;";
		case 21:	return "&lt;NAK&gt;";
		case 22:	return "&lt;SYN&gt;";
		case 23:	return "&lt;ETB&gt;";
		case 24:	return "&lt;CAN&gt;";
		case 25:	return "&lt;EM&gt;";
		case 26:	return "&lt;SUB&gt;";
		case 27:	return "&lt;ESC&gt;";
		case 28:	return "&lt;FS&gt;";
		case 29:	return "&lt;GS&gt;";
		case 30:	return "&lt;RS&gt;";
		case 31:	return "&lt;US&gt;";

		default:	return DE_NULL;
	}
}

std::streamsize EscapeStreambuf::xsputn (const char* s, std::streamsize count)
{
	std::streamsize	numWritten = 0;

	for (std::streamsize inPos = 0; inPos < count; inPos++)
	{
		const char* entity = getEscapeEntity(s[inPos]);

		if (entity)
		{
			// Flush data prior to entity.
			if (inPos > numWritten)
			{
				m_dst.write(s + numWritten, inPos-numWritten);
				if (m_dst.fail())
					return numWritten;
			}

			// Flush entity value
			m_dst.write(entity, (std::streamsize)strlen(entity));

			numWritten = inPos+1;
		}
	}

	if (numWritten < count)
	{
		m_dst.write(s + numWritten, count-numWritten);
		if (m_dst.fail())
			return numWritten;
	}

	return count;
}

int EscapeStreambuf::overflow (int ch)
{
	if (ch == -1)
		return -1;
	else
	{
		DE_ASSERT((ch & 0xff) == ch);
		const char chVal = (char)(deUint8)(ch & 0xff);
		return xsputn(&chVal, 1) == 1 ? ch : -1;
	}
}

Writer::Writer (std::ostream& dst)
	: m_rawDst	(dst)
	, m_dataBuf	(dst)
	, m_dataStr	(&m_dataBuf)
	, m_state	(STATE_DATA)
{
}

Writer::~Writer (void)
{
}

Writer& Writer::operator<< (const BeginElement& begin)
{
	if (m_state == STATE_ELEMENT)
		m_rawDst << ">";

	if (m_state == STATE_ELEMENT || m_state == STATE_ELEMENT_END)
	{
		m_rawDst << "\n";
		for (int i = 0; i < (int)m_elementStack.size(); i++)
			m_rawDst << "  ";
	}

	m_rawDst << "<" << begin.element;

	m_elementStack.push_back(begin.element);
	m_state = STATE_ELEMENT;

	return *this;
}

Writer& Writer::operator<< (const Attribute& attribute)
{
	DE_ASSERT(m_state == STATE_ELEMENT);

	// \todo [2012-09-05 pyry] Escape?
	m_rawDst << " " << attribute.name << "=\"" << attribute.value << "\"";

	return *this;
}

Writer& Writer::operator<< (const EndElementType&)
{
	if (m_state == STATE_ELEMENT)
		m_rawDst << "/>";
	else
	{
		if (m_state == STATE_ELEMENT_END)
		{
			m_rawDst << "\n";
			for (int i = 0; i < (int)m_elementStack.size()-1; i++)
				m_rawDst << "  ";
		}

		m_rawDst << "</" << m_elementStack.back() << ">";
	}

	m_elementStack.pop_back();
	m_state = STATE_ELEMENT_END;

	return *this;
}

} // xml
} // xe