/*
 * SaxComposeInterface.cpp
 *
 *	Created on: 8.8.2012
 *			Author: Martin Zak
 */

#include "SaxComposeInterface.h"

#include <alib/string>
#include <alib/iostream>
#include <fstream>
#include <sstream>
#include <alib/deque>
#include "ComposerException.h"
#include <alib/measure>

#include <registration/AlgoRegistration.hpp>

namespace sax {

void SaxComposeInterface::composeMemory(std::string& xmlOut, const ext::deque<Token>& in) {
	xmlBufferPtr buf = xmlBufferCreate();
	xmlTextWriterPtr writer = xmlNewTextWriterMemory(buf, 0);

	SaxComposeInterface::xmlSAXUserCompose(writer, in);

	xmlFreeTextWriter(writer);
	xmlOut = (const char*) buf->content;
	xmlBufferFree(buf);
}

std::string SaxComposeInterface::composeMemory ( const ext::deque < Token > & in ) {
	std::string res;
	SaxComposeInterface::composeMemory ( res, in );
	return res;
}

void SaxComposeInterface::composeFile(const std::string& filename, const ext::deque<Token>& in) {
	xmlTextWriterPtr writer = xmlNewTextWriterFilename(filename.c_str(), 0);

	SaxComposeInterface::xmlSAXUserCompose(writer, in);

	xmlFreeTextWriter(writer);
}

void SaxComposeInterface::composeStdout(const ext::deque<Token>& in) {
	SaxComposeInterface::composeFile("-", in);
}

void SaxComposeInterface::composeStream(std::ostream& out, const ext::deque<Token>& in) {
	xmlBufferPtr buf = xmlBufferCreate();
	xmlTextWriterPtr writer = xmlNewTextWriterMemory(buf, 0);

	SaxComposeInterface::xmlSAXUserCompose(writer, in);

	xmlFreeTextWriter(writer);
	out << (const char*) buf->content;
	xmlBufferFree(buf);
}

void SaxComposeInterface::xmlSAXUserCompose(xmlTextWriterPtr writer, const ext::deque<Token>& in) {
	measurements::start("Sax Composer", measurements::Type::FINALIZE);
	xmlTextWriterStartDocument(writer, nullptr, nullptr, nullptr);
	#ifdef DEBUG
		ext::deque<Token> stack;
	#endif
	for(const Token& token : in) {
		switch(token.getType()) {
		case Token::TokenType::START_ELEMENT:
			xmlTextWriterStartElement(writer, (const xmlChar*) token.getData().c_str());
			#ifdef DEBUG
				stack.push_back(token);
			#endif
			break;
		case Token::TokenType::END_ELEMENT:
			#ifdef DEBUG
				if(stack.empty() || stack.back().getData() != token.getData() || stack.back().getType() != Token::TokenType::START_ELEMENT) {
					throw ComposerException(Token(stack.back().getData(), Token::TokenType::END_ELEMENT), token);
				}
				stack.pop_back();
			#endif
			xmlTextWriterEndElement(writer);
			break;
		case Token::TokenType::CHARACTER:
			xmlTextWriterWriteString(writer, (const xmlChar*) token.getData().c_str());
			break;
		case Token::TokenType::START_ATTRIBUTE:
			xmlTextWriterStartAttribute(writer, (const xmlChar*) token.getData().c_str());
			#ifdef DEBUG
				stack.push_back(token);
			#endif
			break;
		case Token::TokenType::END_ATTRIBUTE:
			#ifdef DEBUG
				if(stack.empty() || stack.back().getData() != token.getData() || stack.back().getType() != Token::TokenType::START_ATTRIBUTE) {
					throw ComposerException(Token(stack.back().getData(), Token::TokenType::END_ATTRIBUTE), token);
				}
				stack.pop_back();
			#endif
			xmlTextWriterEndAttribute(writer);
			break;
		}
	}
	xmlTextWriterEndDocument(writer);
	measurements::end();
}

} /* namespace sax */

namespace {

auto SaxComposeInterfaceString = registration::AbstractRegister < sax::SaxComposeInterface, std::string, const ext::deque < sax::Token > & > ( sax::SaxComposeInterface::composeMemory, "in" ).setDocumentation (
"Composes the XML to a string.\n\
\n\
@param in list of xml tokens\n\
@return resulting XML as string\n\
@throws CommonException when an error occurs (e.g. xml tokens incorrectly nested)" );

} /* namespace */
