/*
 * UnrankedTree.h
 *
 * Created on: Sep 27, 2017
 * Author: Jan Travnicek
 */

#pragma once

#include <tree/unranked/UnrankedTree.h>
#include <sax/SaxParseInterface.h>
#include <sax/FromXMLParserHelper.h>
#include <sax/SaxComposeInterface.h>
#include <core/rawApi.hpp>

namespace core {

template < class SymbolType >
struct rawApi < tree::UnrankedTree < SymbolType > > {
private:
	static ext::tree < SymbolType > parseContent ( ext::deque < sax::Token >::iterator & input );
	static void composeContent ( ext::deque < sax::Token > & out, const ext::tree < SymbolType > & node );

public:
	static tree::UnrankedTree < SymbolType > parse ( std::istream & input );
	static void compose ( std::ostream & output, const tree::UnrankedTree < SymbolType > & tree );
};

template < class SymbolType >
tree::UnrankedTree < SymbolType > rawApi < tree::UnrankedTree < SymbolType > >::parse ( std::istream & input ) {
	ext::deque < sax::Token > tokens;
	sax::SaxParseInterface::parseStream ( input, tokens );
	ext::deque < sax::Token >::iterator iter = tokens.begin ( );
	return tree::UnrankedTree < SymbolType > ( parseContent ( iter ) );
}

template < class SymbolType >
void rawApi < tree::UnrankedTree < SymbolType > >::compose ( std::ostream & output, const tree::UnrankedTree < SymbolType > & tree ) {
	ext::deque < sax::Token > tokens;
	composeContent ( tokens, tree.getContent ( ) );
	sax::SaxComposeInterface::composeStream ( output, tokens );
}

template < class SymbolType >
ext::tree < SymbolType > rawApi < tree::UnrankedTree < SymbolType > >::parseContent ( ext::deque < sax::Token >::iterator & input ) {
	if ( sax::FromXMLParserHelper::isTokenType ( input, sax::Token::TokenType::START_ELEMENT ) ) {
		ext::tree < SymbolType > node ( SymbolType ( sax::FromXMLParserHelper::popTokenData ( input, sax::Token::TokenType::START_ELEMENT ) ), { } );
		while ( sax::FromXMLParserHelper::isTokenType ( input, sax::Token::TokenType::START_ELEMENT ) || sax::FromXMLParserHelper::isTokenType ( input, sax::Token::TokenType::CHARACTER ) ) {
			node.push_back ( parseContent ( input ) );
		}
		sax::FromXMLParserHelper::popTokenData ( input, sax::Token::TokenType::END_ELEMENT );
		return node;
	} else if ( sax::FromXMLParserHelper::isTokenType ( input, sax::Token::TokenType::CHARACTER ) ) {
		return ext::tree < SymbolType > ( SymbolType ( sax::FromXMLParserHelper::popTokenData ( input, sax::Token::TokenType::CHARACTER ) ), { } );
	} else {
		throw exception::CommonException ( "Invalid token stream" );
	}
}

template < class SymbolType >
void rawApi < tree::UnrankedTree < SymbolType > >::composeContent ( ext::deque < sax::Token > & out, const ext::tree < SymbolType > & node ) {
	if ( node.getChildren ( ).empty ( ) ) {
		out.emplace_back ( sax::Token ( ext::to_string ( node.getData ( ) ), sax::Token::TokenType::CHARACTER ) );
	} else {
		out.emplace_back ( sax::Token ( ext::to_string ( node.getData ( ) ), sax::Token::TokenType::START_ELEMENT ) );

		for ( const ext::tree < SymbolType > & child : node.getChildren ( ) ) {
			composeContent ( out, child );
		}

		out.emplace_back ( sax::Token ( ext::to_string ( node.getData ( ) ), sax::Token::TokenType::END_ELEMENT ) );
	}
}

} /* namespace core */

