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

#include "SaxParseInterface.h"

#include <alib/string>
#include <cstring>
#include <cstdlib>
#include <alib/iostream>
#include <alib/algorithm>
#include <exception/CommonException.h>
#include <alib/measure>

#include <registration/AlgoRegistration.hpp>

namespace sax {

void SaxParseInterface::parseMemory(const std::string& xmlIn, ext::deque<Token>& out) {
	xmlParserInputBufferPtr buf = xmlParserInputBufferCreateMem (xmlIn.c_str(), xmlIn.length(), XML_CHAR_ENCODING_NONE);
	xmlTextReaderPtr reader = xmlNewTextReader(buf, "");

	int result = SaxParseInterface::xmlSAXUserParse(reader, out);

	xmlFreeTextReader(reader);
	xmlFreeParserInputBuffer(buf);
	xmlCleanupCharEncodingHandlers();
	xmlCleanupParser();

	if (result != 0) {
		throw exception::CommonException("Cannot parse the XML " + xmlIn);
	}
}

ext::deque < Token > SaxParseInterface::parseMemory ( const std::string& xmlIn ) {
	ext::deque < Token > tokens;
	SaxParseInterface::parseMemory ( xmlIn, tokens );
	return tokens;
}

void SaxParseInterface::parseFile(const std::string& filename, ext::deque<Token>& out) {
	xmlTextReaderPtr reader = xmlNewTextReaderFilename(filename.c_str());

	int result = SaxParseInterface::xmlSAXUserParse(reader, out);

	xmlFreeTextReader(reader);
	xmlCleanupCharEncodingHandlers();
	xmlCleanupParser();

	if (result != 0) {
		throw exception::CommonException("Cannot parse the XML file " + filename);
	}
}

ext::deque < Token > SaxParseInterface::parseFile ( const std::string & filename ) {
	ext::deque < Token > tokens;
	SaxParseInterface::parseFile ( filename, tokens );
	return tokens;
}

void SaxParseInterface::parseStdin(ext::deque<Token>& out) {
	SaxParseInterface::parseFile("-", out);
}

ext::deque < Token > SaxParseInterface::parseStdin ( ) {
	return SaxParseInterface::parseFile ( "-" );
}

void SaxParseInterface::parseStream(std::istream& in, ext::deque<Token>& out) {
	std::string input(std::istreambuf_iterator<char>(in), (std::istreambuf_iterator<char>()));
	SaxParseInterface::parseMemory(input, out);
}

ext::deque < Token > SaxParseInterface::parseStream ( std::istream & in ) {
	ext::deque < Token > tokens;
	SaxParseInterface::parseStream ( in, tokens );
	return tokens;
}

int SaxParseInterface::xmlSAXUserParse(xmlTextReaderPtr reader, ext::deque<Token>& out) {
	int ret = xmlTextReaderRead(reader);
	measurements::start("Sax Parser", measurements::Type::INIT);
	while (ret == 1) {
		xmlChar* name = xmlTextReaderName(reader);
		xmlChar* value;
		bool empty;

		switch(xmlTextReaderNodeType(reader)) {
			case 1: // START_ELEMENT
				out.emplace_back((const char*) name, Token::TokenType::START_ELEMENT);
				empty = xmlTextReaderIsEmptyElement(reader);
				while(xmlTextReaderMoveToNextAttribute(reader)) {
					xmlChar* attrName = xmlTextReaderName(reader);
					xmlChar* attrValue = xmlTextReaderValue(reader);

					out.emplace_back((const char*) attrName, Token::TokenType::START_ATTRIBUTE);
					out.emplace_back((const char*) attrValue, Token::TokenType::CHARACTER);
					out.emplace_back((const char*) attrName, Token::TokenType::END_ATTRIBUTE);

					xmlFree(attrName);
					xmlFree(attrValue);
				}
				if(empty) out.emplace_back((const char*) name, Token::TokenType::END_ELEMENT);
				break;
			case 3: //CHARACTER
				value = xmlTextReaderValue(reader);
				if(! std::all_of(value, value + strlen((const char*) value), ext::isspace)) out.emplace_back((const char*) value, Token::TokenType::CHARACTER);
				xmlFree(value);
				break;
			case 15: //END_EMENENT
				out.emplace_back((const char*) name, Token::TokenType::END_ELEMENT);
				break;
		}

		xmlFree(name);

		ret = xmlTextReaderRead(reader);
	}
	measurements::end();
	return ret;
}

} /* namespace sax */

namespace {

auto SaxParseInterfaceToken = registration::AbstractRegister < sax::SaxParseInterface, ext::deque < sax::Token >, const std::string & > ( sax::SaxParseInterface::parseMemory, "xmlIn" ).setDocumentation (
"Parses the string containing XML.\n\
\n\
@param xmlIn input XML\n\
@return parsed list of xml tokens\n\
@throws CommonException when an error occurs (e.g. XML is not valid)" );

} /* namespace */
